diff options
author | Andrei Pavel <andrei.pavel@qualitance.com> | 2017-08-17 20:04:29 +0200 |
---|---|---|
committer | Andrei Pavel <andrei.pavel@qualitance.com> | 2017-08-17 20:04:29 +0200 |
commit | 529d15326887b3513413567e497118b3db2c24f3 (patch) | |
tree | 8b66b262349433802bd52e920bb4783baac57cb3 /src/hooks | |
parent | Added mysql_execute_script (diff) | |
parent | [master] Added ChangeLog 1288 for trac 5315. (diff) | |
download | kea-529d15326887b3513413567e497118b3db2c24f3.tar.xz kea-529d15326887b3513413567e497118b3db2c24f3.zip |
Merge branch 'isc-master' into minor-changes
Diffstat (limited to 'src/hooks')
24 files changed, 3816 insertions, 23 deletions
diff --git a/src/hooks/dhcp/Makefile.am b/src/hooks/dhcp/Makefile.am index 6785617c01..ebf9cfe743 100644 --- a/src/hooks/dhcp/Makefile.am +++ b/src/hooks/dhcp/Makefile.am @@ -1 +1 @@ -SUBDIRS = user_chk +SUBDIRS = user_chk lease_cmds diff --git a/src/hooks/dhcp/lease_cmds/.gitignore b/src/hooks/dhcp/lease_cmds/.gitignore new file mode 100644 index 0000000000..310e853700 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/.gitignore @@ -0,0 +1,4 @@ +/lease_cmds_messages.cc +/lease_cmds_messages.h +/s-messages +/html diff --git a/src/hooks/dhcp/lease_cmds/Makefile.am b/src/hooks/dhcp/lease_cmds/Makefile.am new file mode 100644 index 0000000000..60f090b6ac --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/Makefile.am @@ -0,0 +1,63 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Define rule to build logging source files from message file +lease_cmds_messages.h lease_cmds_messages.cc: s-messages +s-messages: lease_cmds_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes + touch $@ + +# Tell automake that the message files are built as part of the build process +# (so that they are built before the main library is built). +BUILT_SOURCES = lease_cmds_messages.h lease_cmds_messages.cc + +# Ensure that the message file is included in the distribution +EXTRA_DIST = lease_cmds_messages.mes + +# Get rid of generated message files on a clean +CLEANFILES = *.gcno *.gcda lease_cmds_messages.h lease_cmds_messages.cc s-messages + +# convenience archive + +noinst_LTLIBRARIES = liblease_cmds.la + +liblease_cmds_la_SOURCES = lease_cmds.cc lease_cmds.h +liblease_cmds_la_SOURCES += lease_parser.h lease_parser.cc +liblease_cmds_la_SOURCES += lease_cmds_log.cc lease_cmds_log.h +liblease_cmds_la_SOURCES += load_unload.cc +liblease_cmds_la_SOURCES += version.cc + +nodist_liblease_cmds_la_SOURCES = lease_cmds_messages.cc lease_cmds_messages.h + +liblease_cmds_la_CXXFLAGS = $(AM_CXXFLAGS) +liblease_cmds_la_CPPFLAGS = $(AM_CPPFLAGS) + +# install the shared object into $(libdir)/hooks +lib_hooksdir = $(libdir)/hooks +lib_hooks_LTLIBRARIES = libdhcp_lease_cmds.la + +libdhcp_lease_cmds_la_SOURCES = +libdhcp_lease_cmds_la_LDFLAGS = $(AM_LDFLAGS) +libdhcp_lease_cmds_la_LDFLAGS += -avoid-version -export-dynamic -module +libdhcp_lease_cmds_la_LIBADD = liblease_cmds.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libdhcp_lease_cmds_la_LIBADD += $(LOG4CPLUS_LIBS) +libdhcp_lease_cmds_la_LIBADD += $(CRYPTO_LIBS) +libdhcp_lease_cmds_la_LIBADD += $(BOOST_LIBS) diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.cc b/src/hooks/dhcp/lease_cmds/lease_cmds.cc new file mode 100644 index 0000000000..85eb442948 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.cc @@ -0,0 +1,895 @@ +// Copyright (C) 2017 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 <lease_cmds.h> +#include <config/command_mgr.h> +#include <lease_parser.h> +#include <cc/command_interpreter.h> +#include <cc/data.h> +#include <asiolink/io_address.h> +#include <lease_cmds_log.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/lease_mgr.h> +#include <dhcpsrv/lease_mgr_factory.h> +#include <dhcpsrv/subnet_id.h> +#include <dhcp/duid.h> +#include <util/encode/hex.h> +#include <util/strutil.h> +#include <exceptions/exceptions.h> +#include <boost/bind.hpp> +#include <string> + +using namespace isc::dhcp; +using namespace isc::data; +using namespace isc::config; +using namespace isc::asiolink; +using namespace std; + +namespace isc { +namespace lease_cmds { + +/// @brief Wrapper class around reservation command handlers. +class LeaseCmdsImpl { +public: + LeaseCmdsImpl(); + + ~LeaseCmdsImpl(); + +/// @brief Parameters specified for reservation-get and reservation-del +/// +/// As both call types (get and delete) need specify which reservation to +/// act on, they have the same set of parameters. In particular, those +/// two call types support the following sets of parameters: +/// - address +/// - subnet-id, identifier-type, identifier-value (v4) +/// - subnet-id, lease-type, iaid, identifier-type, identifier-value (v6) +/// +/// This class stores those parameters and is used to pass them around. +class Parameters { +public: + + /// @brief specifies type of query (by IP addr, by hwaddr, by DUID) + typedef enum { + TYPE_ADDR, ///< query by IP address (either v4 or v6) + TYPE_HWADDR, ///< query by hardware address (v4 only) + TYPE_DUID ///< query by DUID (v6 only) + } Type; + + /// @brief Specifies subnet-id (always used) + SubnetID subnet_id; + + /// @brief Specifies IPv4/v6 address (used when query_type is TYPE_ADDR) + IOAddress addr; + + /// @brief Specifies hardware address (used when query_type is TYPE_HWADDR) + HWAddrPtr hwaddr; + + /// @brief Specifies identifier value (used when query_type is TYPE_DUID) + isc::dhcp::DuidPtr duid; + + /// @brief Attempts to covert text to one of specified types + /// + /// Supported values are: "address", hw-address and duid. + /// + /// @param txt text to be converted + /// @return value converted to Parameters::Type + /// @throw BadValue if unsupported type is specified + static Type txtToType(const std::string& txt) { + if (txt == "address") { + return (Parameters::TYPE_ADDR); + } else if (txt == "hw-address") { + return (Parameters::TYPE_HWADDR); + } else if (txt == "duid") { + return (Parameters::TYPE_DUID); + } else { + isc_throw(BadValue, "Incorrect identifier type: " + << txt << ", the only supported values are: " + "address, hw-address, duid"); + } + } + + /// @brief specifies parameter types (true = query by address, false = + /// query by identifier-type,identifier) + Type query_type; + + /// @brief Lease type (NA,TA or PD) used for v6 leases + Lease::Type lease_type; + + /// @brief IAID identifier used for v6 leases + uint32_t iaid; + + /// @brief Default constructor. + Parameters() + :addr("::"), query_type(TYPE_ADDR), lease_type(Lease::TYPE_NA), + iaid(0) { + } +}; + +private: + + /// @brief Registers commands: + /// + /// Registers: + /// - lease4-add + /// - lease6-add + /// - lease4-get + /// - lease6-get + /// - lease4-del + /// - lease6-del + /// - lease4-update + /// - lease6-update + /// - lease4-wipe + /// - lease6-wipe + + /// @throw Unexpected if CommandMgr is not available (should not happen) + void registerCommands(); + + /// @brief Deregisters commands: + /// + /// Deregisters: + /// - lease4-add + /// - lease6-add + /// - lease4-get + /// - lease6-get + /// - lease4-del + /// - lease6-del + /// - lease4-update + /// - lease6-update + /// - lease4-wipe + /// - lease6-wipe + /// + /// @throw Unexpected if CommandMgr is not available (should not happen) + void deregisterCommands(); + + /// @brief lease4-add, lease6-add command handler + /// + /// This command attempts to add a lease. + /// + /// An example full command looks as follows. Note that the args + /// parameter is expected to contain the "arguments" portion of it. + /// This function covers both v4 and v6 leases. + /// + /// Example command for v4: + /// { + /// "command": "lease4-add", + /// "parameters": { + /// "address": "192.0.2.1", + /// "hwaddr": "00:01:02:03:04:05", + /// "client-id": "this-is-a-client", + /// "valid-lft": 3600, + /// "expire": 12345678, + /// "subnet-id": 1, + /// "fqdn-fwd": true, + /// "fqdn-rev": true, + /// "hostname": "myhost.example.org", + /// "state": 0 + /// } + /// } + /// Example command for v6: + /// { + /// "command": "lease6-add", + /// "arguments": { + /// "subnet-id": 66, + /// "ip-address": "2001:db8:abcd::", + /// "type": "IA_PD", + /// "prefix-len": 48, + /// "duid": "01:02:03:04:05:06:07:08", + /// "iaid": 1234, + /// "preferred-lft": 500, + /// "valid-lft": 1000, + /// "expire": 12345678, + /// "fqdn-fwd": true, + /// "fqdn-rev": true, + /// "hostname": "urania.example.org"" + /// } + /// } + + /// + /// @param command should be 'lease4-add' or 'lease6-add' + /// @param args must contain host reservation definition. + /// @return result of the operation + static ConstElementPtr + leaseAddHandler(const string& command, ConstElementPtr args); + + /// @brief lease4-get, lease6-get command handler + /// + /// This command attempts to retrieve a lease that match selected criteria. + /// The following types of parameters are supported: + /// - (subnet-id, address) for both v4 and v6 + /// - (subnet-id, identifier-type, identifier) for v4 + /// - (subnet-id, type, iana, identifier-type, identifier) for v6 + /// + /// Example command for query by (subnet-id, address): + /// { + /// "command": "lease4-get", + /// "arguments": { + /// "subnet-id": 1, + /// "ip-address": "192.0.2.202" + /// } + /// } + /// + /// Example command for query by (subnet-id, identifier-type, identifier) + /// { + /// "command": "lease4-get", + /// "arguments": { + /// "subnet-id": 1, + /// "identifier-type": "hw-address", + /// "identifier": "00:01:02:03:04:05" + /// } + /// } + /// + /// Example command for query by (subnet-id, type, iana, identifier-type, + /// identifier): + /// { + /// "command": "lease6-get", + /// "arguments": { + /// "subnet-id": 66, + /// "iaid": 42, + /// "type": "IA_NA", + /// "identifier-type": "duid", + /// "identifier": "77:77:77:77:77:77:77:77" + /// } + /// } + /// @param command "lease4-get" or "lease6-get" + /// @param args must contain host reservation definition. + /// @return result of the operation (includes lease details, if found) + static ConstElementPtr + leaseGetHandler(const string& command, ConstElementPtr args); + + /// @brief lease4-del command handler + /// + /// This command attempts to delete an IPv4 lease that match selected + /// criteria. Two types of parameters are supported: (subnet-id, address) or + /// (subnet-id, identifier-type, identifier). + /// + /// Example command for deletion by (subnet-id, address): + /// { + /// "command": "lease4-del", + /// "arguments": { + /// "subnet-id": 1, + /// "ip-address": "192.0.2.202" + /// } + /// } + /// + /// Example command for deletion by (subnet-id, identifier-type, identifier) + /// { + /// "command": "lease4-del", + /// "arguments": { + /// "subnet-id": 1, + /// "identifier-type": "hw-address", + /// "identifier": "00:01:02:03:04:05" + /// } + /// }"; + /// @param command should be 'lease4-del' (but it's ignored) + /// @param args must contain host reservation definition. + /// @return result of the operation (host will be included as parameters, if found) + static ConstElementPtr + lease4DelHandler(const string& command, ConstElementPtr args); + + /// @brief lease6-del command handler + /// + /// This command attempts to delete a lease that match selected criteria. + /// Two types of parameters are supported: (subnet-id, address) or + /// (subnet-id, type, iaid, identifier-type, identifier). + /// + /// Example command for deletion by (subnet-id, address): + /// { + /// "command": "lease6-del", + /// "arguments": { + /// "subnet-id": 1, + /// "ip-address": "192.0.2.202" + /// } + /// } + /// + /// Example command for deletion by (subnet-id, type, iaid, identifier-type, + /// identifier): + /// { + /// "command": "lease6-del", + /// "arguments": { + /// "subnet-id": 1, + /// "type": "IA_NA", + /// "iaid": 123456, + /// "identifier-type": "hw-address", + /// "identifier": "00:01:02:03:04:05" + /// } + /// }"; + /// @param command should be 'lease6-del' (but it's ignored) + /// @param args must contain host reservation definition. + /// @return result of the operation (host will be included as parameters, if found) + static ConstElementPtr + lease6DelHandler(const string& command, ConstElementPtr args); + + /// @brief lease4-update handler + /// + /// This command attempts to update existing IPv4 lease. The parameters + /// specified will replace existing lease. The only condition is that + /// the IP address must not change. If you want to change the IP address, + /// please use lease4-del and lease4-add instead. + /// + /// Example command: + /// { + /// "command": "lease4-update", + /// "arguments": { + /// "subnet-id": 44, + /// "ip-address": "192.0.2.1", + /// "hw-address": "1a:1b:1c:1d:1e:1f", + /// "hostname": "newhostname.example.org" + /// } + /// }; + /// + /// @param command - should be "lease4-update", but it is ignored + /// @param args arguments that describe the lease being updated. + static ConstElementPtr + lease4UpdateHandler(const string& command, ConstElementPtr args); + + /// @brief lease6-update handler + /// + /// This command attempts to update existing IPv6 lease. The parameters + /// specified will replace existing lease. The only condition is that + /// the IP address must not change. If you want to change the IP address, + /// please use lease6-del and lease6-add instead. + /// + /// Example command: + /// { + /// "command": "lease6-update", + /// "arguments": { + /// "subnet-id": 66, + /// "ip-address": "2001:db8::1", + /// "iaid": 7654321, + /// "duid": "88:88:88:88:88:88:88:88", + /// "hostname": "newhostname.example.org" + /// } + /// }"; + /// + /// @param command - should be "lease6-update" (but it is ignored) + /// @param args arguments that describe the lease being updated. + static ConstElementPtr + lease6UpdateHandler(const string& command, ConstElementPtr args); + + /// @brief lease4-wipe handler + /// + /// This commands attempts to remove all IPv4 leases from a specific + /// subnet. Currently the leases are removed from the database, + /// without any processing (like calling hooks or doing DDNS + /// cleanups). + /// + /// Example command: + /// {\n" + /// "command": "lease4-wipe",\n" + /// "arguments": {" + /// "subnet-id": 44 + /// }\n" + /// }"; + /// @param command - should be "lease4-wipe" (but is ignored) + /// @param args arguments that describe the lease being updated. + static ConstElementPtr + lease4WipeHandler(const string& command, ConstElementPtr args); + + /// @brief lease6-wipe handler + /// + /// This commands attempts to remove all IPv4 leases from a specific + /// subnet. Currently the leases are removed from the database, + /// without any processing (like calling hooks or doing DDNS + /// cleanups). + /// + /// Example command: + /// {\n" + /// "command": "lease4-wipe",\n" + /// "arguments": {" + /// "subnet-id": 44 + /// }\n" + /// }"; + /// @param command - should be "lease4-wipe" (but is ignored) + /// @param args arguments that describe the lease being updated. + static ConstElementPtr + lease6WipeHandler(const string& command, ConstElementPtr args); + + /// @brief Extracts parameters required for reservation-get and reservation-del + /// + /// See @ref Parameters class for detailed description of what is expected + /// in the args structure. + /// + /// @param v6 whether addresses allowed are v4 (false) or v6 (true) + /// @param args arguments passed to command + /// @return parsed parameters + /// @throw BadValue if input arguments don't make sense. + static Parameters getParameters(bool v6, const ConstElementPtr& args); +}; + +LeaseCmdsImpl::LeaseCmdsImpl() { + registerCommands(); +} + +LeaseCmdsImpl::~LeaseCmdsImpl() { + deregisterCommands(); +} + +void LeaseCmdsImpl::registerCommands() { + /// @todo: Use registration mechanism once #5314 is merged. + /// See #5321 discussion. + CommandMgr::instance().registerCommand("lease4-add", + boost::bind(&LeaseCmdsImpl::leaseAddHandler, _1, _2)); + CommandMgr::instance().registerCommand("lease6-add", + boost::bind(&LeaseCmdsImpl::leaseAddHandler, _1, _2)); + + CommandMgr::instance().registerCommand("lease4-get", + boost::bind(&LeaseCmdsImpl::leaseGetHandler, _1, _2)); + CommandMgr::instance().registerCommand("lease6-get", + boost::bind(&LeaseCmdsImpl::leaseGetHandler, _1, _2)); + + CommandMgr::instance().registerCommand("lease4-del", + boost::bind(&LeaseCmdsImpl::lease4DelHandler, _1, _2)); + CommandMgr::instance().registerCommand("lease6-del", + boost::bind(&LeaseCmdsImpl::lease6DelHandler, _1, _2)); + + CommandMgr::instance().registerCommand("lease4-update", + boost::bind(&LeaseCmdsImpl::lease4UpdateHandler, _1, _2)); + CommandMgr::instance().registerCommand("lease6-update", + boost::bind(&LeaseCmdsImpl::lease6UpdateHandler, _1, _2)); + + CommandMgr::instance().registerCommand("lease4-wipe", + boost::bind(&LeaseCmdsImpl::lease4WipeHandler, _1, _2)); + CommandMgr::instance().registerCommand("lease6-wipe", + boost::bind(&LeaseCmdsImpl::lease6WipeHandler, _1, _2)); +} + +void LeaseCmdsImpl::deregisterCommands() { + /// @todo: Use deregistration mechanism once #5321 discussion is done + CommandMgr::instance().deregisterCommand("lease4-add"); + CommandMgr::instance().deregisterCommand("lease6-add"); + + CommandMgr::instance().deregisterCommand("lease4-get"); + CommandMgr::instance().deregisterCommand("lease6-get"); + + CommandMgr::instance().deregisterCommand("lease4-del"); + CommandMgr::instance().deregisterCommand("lease6-del"); + + CommandMgr::instance().deregisterCommand("lease4-update"); + CommandMgr::instance().deregisterCommand("lease6-update"); + + CommandMgr::instance().deregisterCommand("lease4-wipe"); + CommandMgr::instance().deregisterCommand("lease6-wipe"); +} + +ConstElementPtr +LeaseCmdsImpl::leaseAddHandler(const std::string& name, + ConstElementPtr params) { + bool v4 = (name == "lease4-add"); + + string txt = "(missing parameters)"; + if (params) { + txt = params->str(); + } + + try { + if (!params) { + isc_throw(isc::BadValue, "no parameters specified for the command"); + } + + ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg(); + + Lease4Ptr lease4; + Lease6Ptr lease6; + if (v4) { + Lease4Parser parser; + lease4 = parser.parse(config, params); + + // checkLeaseIntegrity(config, lease4); + + if (lease4) { + LeaseMgrFactory::instance().addLease(lease4); + } + + } else { + Lease6Parser parser; + lease6 = parser.parse(config, params); + + // checkLeaseIntegrity(config, lease6); + + if (lease6) { + LeaseMgrFactory::instance().addLease(lease6); + } + } + + + } catch (const std::exception& ex) { + LOG_ERROR(lease_cmds_logger, v4 ? LEASE_CMDS_ADD4_FAILED : LEASE_CMDS_ADD6_FAILED) + .arg(txt) + .arg(ex.what()); + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } + LOG_INFO(lease_cmds_logger, + v4 ? LEASE_CMDS_ADD4 : LEASE_CMDS_ADD6).arg(txt); + return (createAnswer(CONTROL_RESULT_SUCCESS, "Lease added.")); +} + +LeaseCmdsImpl::Parameters +LeaseCmdsImpl::getParameters(bool v6, const ConstElementPtr& params) { + Parameters x; + + if (!params || params->getType() != Element::map) { + isc_throw(BadValue, "Parameters missing or are not a map."); + } + + // We support several sets of parameters for leaseX-get/lease-del: + // lease-get(type, address) + // lease-get(type, subnet-id, identifier-type, identifier) + + if (params->contains("type")) { + string t = params->get("type")->stringValue(); + if (t == "IA_NA" || t == "0") { + x.lease_type = Lease::TYPE_NA; + } else if (t == "IA_TA" || t == "1") { + x.lease_type = Lease::TYPE_TA; + } else if (t == "IA_PD" || t == "2") { + x.lease_type = Lease::TYPE_PD; + } else if (t == "V4" || t == "3") { + x.lease_type = Lease::TYPE_V4; + } else { + isc_throw(BadValue, "Invalid lease type specified: " + << t << ", only supported values are: IA_NA, IA_TA," + << " IA_PD and V4"); + } + } + + ConstElementPtr tmp = params->get("ip-address"); + if (tmp) { + if (tmp->getType() != Element::string) { + isc_throw(BadValue, "'ip-address' is not a string."); + } + + x.addr = IOAddress(tmp->stringValue()); + + if ((v6 && !x.addr.isV6()) || (!v6 && !x.addr.isV4())) { + stringstream txt; + txt << "Invalid " << (v6 ? "IPv6" : "IPv4") + << " address specified: " << tmp->stringValue(); + isc_throw(BadValue, txt.str()); + } + + x.query_type = Parameters::TYPE_ADDR; + return (x); + } + + tmp = params->get("subnet-id"); + if (!tmp) { + isc_throw(BadValue, "Mandatory 'subnet-id' parameter missing."); + } + if (tmp->getType() != Element::integer) { + isc_throw(BadValue, "'subnet-id' parameter is not integer."); + } + x.subnet_id = tmp->intValue(); + + if (params->contains("iaid")) { + x.iaid = params->get("iaid")->intValue(); + } + + // No address specified. Ok, so it must be identifier based query. + // "identifier-type": "duid", + // "identifier": "aa:bb:cc:dd:ee:..." + + ConstElementPtr type = params->get("identifier-type"); + ConstElementPtr ident = params->get("identifier"); + if (!type || type->getType() != Element::string) { + isc_throw(BadValue, "No 'ip-address' provided" + " and 'identifier-type' is either missing or not a string."); + } + if (!ident || ident->getType() != Element::string) { + isc_throw(BadValue, "No 'ip-address' provided" + " and 'identifier' is either missing or not a string."); + } + + // Got the parameters. Let's see if their values make sense. + // Try to convert identifier-type + x.query_type = Parameters::txtToType(type->stringValue()); + + switch (x.query_type) { + case Parameters::TYPE_HWADDR: { + HWAddr hw = HWAddr::fromText(ident->stringValue()); + x.hwaddr = HWAddrPtr(new HWAddr(hw)); + break; + } + case Parameters::TYPE_DUID: { + DUID duid = DUID::fromText(ident->stringValue()); + x.duid = DuidPtr(new DUID(duid)); + break; + } + case Parameters::TYPE_ADDR: { + // We should never get here. The address clause should have been caught + // earlier. + return (x); + } + default: { + isc_throw(BadValue, "Identifier type " << type->stringValue() << + " is not supported."); + } + } + return (x); +} + +ConstElementPtr +LeaseCmdsImpl::leaseGetHandler(const std::string& name, ConstElementPtr params) { + Parameters p; + Lease4Ptr lease4; + Lease6Ptr lease6; + bool v4 = (name == "lease4-get"); + try { + p = getParameters(!v4, params); + + switch (p.query_type) { + case Parameters::TYPE_ADDR: { + // Query by address + if (v4) { + lease4 = LeaseMgrFactory::instance().getLease4(p.addr); + } else { + lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, p.addr); + } + break; + } + case Parameters::TYPE_HWADDR: + if (v4) { + if (!p.hwaddr) { + return (createAnswer(CONTROL_RESULT_ERROR, + "Program error: Query by hw-address " + "requires hwaddr to be specified")); + } + lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id); + } else { + return (createAnswer(CONTROL_RESULT_ERROR, + "Query by hw-address is not allowed in v6.")); + } + break; + case Parameters::TYPE_DUID: + if (!v4) { + if (!p.duid) { + return (createAnswer(CONTROL_RESULT_ERROR, + "Program error: Query by duid " + "requires duid to be specified")); + } + lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid, + p.iaid, p.subnet_id); + } else { + return (createAnswer(CONTROL_RESULT_ERROR, + "Query by duid is not allowed in v4.")); + } + break; + default: { + stringstream tmp; + tmp << "Unknown query type: " << static_cast<int>(p.query_type); + return (createAnswer(CONTROL_RESULT_ERROR, tmp.str())); + } + } + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } + + ElementPtr lease_json; + if (v4 && lease4) { + lease_json = lease4->toElement(); + return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease found.", lease_json)); + } + if (!v4 && lease6) { + lease_json = lease6->toElement(); + return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease found.", lease_json)); + + } + + // If we got here, the lease has not been found. + return (createAnswer(CONTROL_RESULT_EMPTY, "Lease not found.")); +} + +ConstElementPtr +LeaseCmdsImpl::lease4DelHandler(const std::string& , ConstElementPtr params) { + Parameters p; + Lease4Ptr lease4; + IOAddress addr(IOAddress::IPV4_ZERO_ADDRESS()); + try { + p = getParameters(false, params); + + switch (p.query_type) { + case Parameters::TYPE_ADDR: { + + // If address was specified explicitly, let's use it as is. + addr = p.addr; + break; + } + case Parameters::TYPE_HWADDR: + if (!p.hwaddr) { + return (createAnswer(CONTROL_RESULT_ERROR, + "Program error: Query by hw-address " + "requires hwaddr to be specified")); + } + + // Let's see if there's such a lease at all. + lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id); + if (!lease4) { + return (createAnswer(CONTROL_RESULT_EMPTY, "IPv4 lease not found.")); + } + + // Found it, can use it as is. + addr = lease4->addr_; + break; + + case Parameters::TYPE_DUID: + return (createAnswer(CONTROL_RESULT_ERROR, + "Delete by duid is not allowed in v4.")); + break; + + default: { + stringstream tmp; + tmp << "Unknown query type: " << static_cast<int>(p.query_type); + return (createAnswer(CONTROL_RESULT_ERROR, tmp.str())); + } + } + + if (LeaseMgrFactory::instance().deleteLease(addr)) { + return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease deleted.")); + } else { + return (createAnswer(CONTROL_RESULT_EMPTY, "IPv4 lease not found.")); + } + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } +} + +ConstElementPtr +LeaseCmdsImpl::lease6DelHandler(const std::string& , ConstElementPtr params) { + Parameters p; + Lease6Ptr lease6; + IOAddress addr(IOAddress::IPV6_ZERO_ADDRESS()); + try { + p = getParameters(true, params); + + switch (p.query_type) { + case Parameters::TYPE_ADDR: { + + // If address was specified explicitly, let's use it as is. + addr = p.addr; + break; + } + case Parameters::TYPE_HWADDR: + return (createAnswer(CONTROL_RESULT_ERROR, + "Delete by hw-address is not allowed in v6.")); + + case Parameters::TYPE_DUID: + if (!p.duid) { + return (createAnswer(CONTROL_RESULT_ERROR, + "Program error: Query by duid " + "requires duid to be specified")); + } + + // Let's see if there's such a lease at all. + lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid, + p.iaid, p.subnet_id); + if (!lease6) { + return (createAnswer(CONTROL_RESULT_EMPTY, "IPv6 lease not found.")); + } + + // Found it, can use it as is. + addr = lease6->addr_; + break; + + default: { + stringstream tmp; + tmp << "Unknown query type: " << static_cast<int>(p.query_type); + return (createAnswer(CONTROL_RESULT_ERROR, tmp.str())); + } + } + + if (LeaseMgrFactory::instance().deleteLease(addr)) { + return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease deleted.")); + } else { + return (createAnswer(CONTROL_RESULT_EMPTY, "IPv6 lease not found.")); + } + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } +} + +ConstElementPtr +LeaseCmdsImpl::lease4UpdateHandler(const string& , ConstElementPtr params) { + try { + + // We need the lease to be specified. + if (!params) { + isc_throw(isc::BadValue, "no parameters specified for lease4-update command"); + } + + // Get the parameters specified by the user first. + ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg(); + Lease4Ptr lease4; + Lease4Parser parser; + // The parser does sanity checks (if the address is in scope, if + // subnet-id is valid, etc) + lease4 = parser.parse(config, params); + + LeaseMgrFactory::instance().updateLease4(lease4); + return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv4 lease updated.")); + + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } +} + +ConstElementPtr +LeaseCmdsImpl::lease6UpdateHandler(const string& , ConstElementPtr params) { + try { + // We need the lease to be specified. + if (!params) { + isc_throw(isc::BadValue, "no parameters specified for lease6-update command"); + } + + // Get the parameters specified by the user first. + ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg(); + Lease6Ptr lease6; + Lease6Parser parser; + // The parser does sanity checks (if the address is in scope, if + // subnet-id is valid, etc) + lease6 = parser.parse(config, params); + + LeaseMgrFactory::instance().updateLease6(lease6); + return (createAnswer(CONTROL_RESULT_SUCCESS, "IPv6 lease updated.")); + + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } +} + +ConstElementPtr +LeaseCmdsImpl::lease4WipeHandler(const string& /*cmd*/, ConstElementPtr params) { + try { + + // The subnet-id is a mandatory parameter. + if (!params) { + isc_throw(isc::BadValue, "no parameters specified for lease4-wipe command"); + } + SimpleParser parser; + SubnetID id = parser.getUint32(params, "subnet-id"); + + size_t num = LeaseMgrFactory::instance().wipeLeases4(id); + + stringstream tmp; + tmp << "Deleted " << num << " IPv4 lease(s)."; + return (createAnswer(num ? CONTROL_RESULT_SUCCESS : CONTROL_RESULT_EMPTY, + tmp.str())); + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } +} + +ConstElementPtr +LeaseCmdsImpl::lease6WipeHandler(const string& /*cmd*/, ConstElementPtr params) { + try { + + // The subnet-id is a mandatory parameter. + if (!params) { + isc_throw(isc::BadValue, "no parameters specified for lease6-wipe command"); + } + SimpleParser parser; + SubnetID id = parser.getUint32(params, "subnet-id"); + + size_t num = LeaseMgrFactory::instance().wipeLeases6(id); + + stringstream tmp; + tmp << "Deleted " << num << " IPv6 lease(s)."; + return (createAnswer(num ? CONTROL_RESULT_SUCCESS : CONTROL_RESULT_EMPTY, + tmp.str())); + } catch (const std::exception& ex) { + return (createAnswer(CONTROL_RESULT_ERROR, ex.what())); + } +} + +LeaseCmds::LeaseCmds() + :impl_(new LeaseCmdsImpl()) { +} + +LeaseCmds::~LeaseCmds() { + impl_.reset(); +} + +}; +}; diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.dox b/src/hooks/dhcp/lease_cmds/lease_cmds.dox new file mode 100644 index 0000000000..761c65f491 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.dox @@ -0,0 +1,96 @@ +// Copyright (C) 2017 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/. + +/** + +@mainpage Kea Lease Commands Hooks Library + +Welcome to Kea Lease Commands Hooks Library. This documentation is addressed to +developers who are interested in the internal operation of the Lease Commands +library. This file provides information needed to understand and perhaps extend +this library. + +This documentation is stand-alone: you should have read and understood <a +href="http://kea.isc.org/docs/devel/">Kea Developer's Guide</a> and in +particular its section about hooks. + +@section lease_cmds Lease Commands Overview + +Lease Commands (or lease_cmds) is a Hook library that can be loaded by Kea to +extend it with additional mechanisms. + +The primary purpose of this library is to provide commands that manage leases. +As such, the whole library is structured around command handlers. When the +library is loaded it registers a number of handlers for new commands. When a +command is issued (be it directly via control channel or indirectly via REST +interface from control agent), the code receives a JSON command with +parameters. Those are parsed and then actual operation commences. This +operation always interacts with an instantiation of isc::dhcp::LeaseMgr +instance, which is Kea's way of storing leases. At the time of writing this text +(Aug. 2017), Kea supports four types of lease managers: memfile, MySQL, +PostgreSQL or Cassandra. The lease commands provided by this library +provide a unified interface for those backends. + +As with other hooks, this one also keeps its code in a separate namespace which +corresponds to the file name of the library: isc::lease_cmds. + +@section lease_cmdsCode Lease Commands Code Overview + +The library operation starts with Kea calling the load() function (file +load_unload.cc). It instantiates an isc::lease_cmds::LeaseCmds object. +The constructor of that object registers all of the lease commands. For a list, +see @ref isc::lease_cmds::LeaseCmds class documentation. This class uses Pimpl +design pattern, thus the real implementation is hidden in isc::lease_cmds::LeaseCmdsImpl. + +Almost every command has its own handler, except few that share the same handler +between v4 and v6 due to its similarity. For example +isc::lease_cmds::LeaseCmdsImpl::leaseAddHandler handles lease4-add and lease6-add +commands. Although the details differ between handlers, the general approach +is the same. First, it starts with parameters sanitization and then some +interaction with isc::dhcp::LeaseMgr is conducted. + +For commands that do something with a specific lease (lease4-get, lease6-get, +lease4-del, lease6-del), there is a @ref isc::lease_cmds::Parameters class that +contains parsed elements. + +For details see documentation and code of the following handlers: +- @ref isc::lease_cmds::LeaseCmdsImpl::leaseAddHandler (lease4-add, lease6-add) +- @ref isc::lease_cmds::LeaseCmdsImpl::leaseGetHandler (lease4-get, lease6-get) +- @ref isc::lease_cmds::LeaseCmdsImpl::lease4DelHandler (lease4-del) +- @ref isc::lease_cmds::LeaseCmdsImpl::lease6DelHandler (lease6-del) +- @ref isc::lease_cmds::LeaseCmdsImpl::lease4UpdateHandler (lease4-update) +- @ref isc::lease_cmds::LeaseCmdsImpl::lease6UpdateHandler (lease6-update) +- @ref isc::lease_cmds::LeaseCmdsImpl::lease4WipeHandler (lease4-wipe) +- @ref isc::lease_cmds::LeaseCmdsImpl::lease6WipeHandler (lease6-wipe) + +@section lease_cmdsDesigns Lease Commands Design choices + +The lease manipulation commands were implemented to provide a convenient interface +for sysadmins. The primary goal was to offer a way to interact with the live +lease database in unified way, regardless of the actual backend being used. + +For some backends (MySQL, PostgreSQL and Cassandra) it is possible to interact +directly with the backend while Kea is running and possibly change its content. This +ability is both powerful and dangerous. In particular, only rudimentary +checks are enforced by the DB schemas (e.g. not possible to have two leases +for the same address). However, it does not prevent sysadmins from making +more obscure errors, like inserting leases for subnets that do not exist +or configuring an address that is topologically outside of the subnet to which +it should belong. These kind of checks are only possible by DHCP-aware +code, which this library provides. + +Some of the queries may require a seemingly odd set of parameters. For example, +lease6-get query requires at least DUID, subnet-id and IAID to retrieve a lease +by DUID. The need for a sysadmin to know and specify an IAID is troublesome. +However, the guiding principle here was to use whatever queries were already +exposed by the lease manager and not introduce new indexes, unless absolutely +necessary. This ensures that there is no performance degradation when the +library is loaded. The only exception for that was lease4-wipe and lease6-wipe +commands that remove all leases from specific subnet. As there were no +queries that could retrieve or otherwise enumerate leases for a specific subnet, +a new query type and a new index had to be added. + +*/ diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.h b/src/hooks/dhcp/lease_cmds/lease_cmds.h new file mode 100644 index 0000000000..57d2db5a40 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_cmds.h @@ -0,0 +1,67 @@ +// Copyright (C) 2017 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/. + +#ifndef LEASE_CMDS_H +#define LEASE_CMDS_H + +#include <boost/shared_ptr.hpp> + +namespace isc { +namespace lease_cmds { + +/// @brief Forward declaration of implementation class. +class LeaseCmdsImpl; + +/// @brief A wrapper class that provides convenient initialization to the library. +/// +/// This is a wrapper class that simply registers extra commands when +/// instantiated and deregisters them when the instance is destroyed. +/// +/// For an actual implementation, see @ref LeaseCmdsImpl class in lease_cmds.cc file. +class LeaseCmds { +public: + + /// @brief Initializes additional lease commands. + /// + /// It registers the following commands: + /// - lease4-add + /// - lease6-add + /// - lease4-get + /// - lease6-get + /// - lease4-del + /// - lease6-del + /// - lease4-update + /// - lease6-update + /// - lease4-del-all + /// - lease6-del-all + /// + /// @throw Unexpected If any of the above fails. + LeaseCmds(); + + /// @brief Destructor + /// + /// Unregisters commands: + /// - lease4-add + /// - lease6-add + /// - lease4-get + /// - lease6-get + /// - lease4-del + /// - lease6-del + /// - lease4-update + /// - lease6-update + /// - lease4-del-all + /// - lease6-del-all + ~LeaseCmds(); +private: + + /// Pointer to the actual implementation + boost::shared_ptr<LeaseCmdsImpl> impl_; +}; + +}; +}; + +#endif diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc new file mode 100644 index 0000000000..8215a6effa --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc @@ -0,0 +1,16 @@ +// Copyright (C) 2017 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 <lease_cmds_log.h> + +namespace isc { +namespace lease_cmds { + +isc::log::Logger lease_cmds_logger("lease_cmds_hooks"); + +} +} + diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.h b/src/hooks/dhcp/lease_cmds/lease_cmds_log.h new file mode 100644 index 0000000000..56172b0feb --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.h @@ -0,0 +1,23 @@ +// Copyright (C) 2017 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/. + +#ifndef LEASE_CMD_LOG_H +#define LEASE_CMD_LOG_H + +#include <log/logger_support.h> +#include <log/macros.h> +#include <lease_cmds_messages.h> + +namespace isc { +namespace lease_cmds { + +extern isc::log::Logger lease_cmds_logger; + +} // end of isc::lease_cmds +} // end of isc namespace + + +#endif diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes new file mode 100644 index 0000000000..9f5b5b019e --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes @@ -0,0 +1,51 @@ +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") + +% LEASE_CMDS_INIT_FAILED loading Lease Commands hooks library failed: %1 +This error message indicates an error during loading the Lease Commands +hooks library. The details of the error are provided as argument of +the log message. + +% LEASE_CMDS_INIT_OK loading Lease Commands hooks library successful +This info message indicates that the Lease Commands hooks library has been +loaded successfully. Enjoy! + +% LEASE_CMDS_DEINIT_FAILED unloading Lease Commands hooks library failed: %1 +This error message indicates an error during unloading the Lease Commands +hooks library. The details of the error are provided as argument of +the log message. + +% LEASE_CMDS_DEINIT_OK unloading Lease Commands hooks library successful +This info message indicates that the Lease Commands hooks library has been +removed successfully. + +% LEASE_CMDS_ADD4_FAILED lease4-add command failed (parameters: %1, reason: %2) +The lease4-add command has failed. Both the reason as well as the +parameters passed are logged. + +% LEASE_CMDS_ADD6_FAILED Lease6-add command failed (parameters: %1, reason: %2) +The lease6-add command has failed. Both the reason as well as the +parameters passed are logged. + +% LEASE_CMDS_ADD4 lease4-add command successful (parameters: %1) +The lease4-add command has been successful. Parameters of the host +added are logged. + +% LEASE_CMDS_ADD6 lease6-add command successful (parameters: %1) +The lease6-add command has been successful. Parameters of the host +added are logged. + +% LEASE_CMDS_DEL4_FAILED lease4-del command failed (parameters: %1, reason: %2) +The attempt to delete an IPv4 lease (lease4-del command) has failed. Both the +reason as well as the parameters passed are logged. + +% LEASE_CMDS_DEL6_FAILED lease6-del command failed (parameters: %1, reason: %2) +The attempt to delete an IPv6 lease (lease4-del command) has failed. Both the +reason as well as the parameters passed are logged. + +% LEASE_CMDS_DEL4 lease4-del command successful (parameters: %1) +The attempt to delete an IPv4 lease (lease4-del command) has been successful. +Parameters of the host removed are logged. + +% LEASE_CMDS_DEL6 lease4-del command successful (parameters: %1) +The attempt to delete an IPv4 lease (lease4-del command) has been successful. +Parameters of the host removed are logged. diff --git a/src/hooks/dhcp/lease_cmds/lease_parser.cc b/src/hooks/dhcp/lease_cmds/lease_parser.cc new file mode 100644 index 0000000000..b0288002d2 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_parser.cc @@ -0,0 +1,257 @@ +// Copyright (C) 2017 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 <cc/data.h> +#include <dhcp/hwaddr.h> +#include <asiolink/io_address.h> +#include <dhcpsrv/lease.h> +#include <lease_parser.h> + +#include <config.h> + +using namespace std; +using namespace isc::dhcp; +using namespace isc::data; +using namespace isc::asiolink; + +namespace isc { +namespace lease_cmds { + +Lease4Ptr +Lease4Parser::parse(ConstSrvConfigPtr& cfg, + const ConstElementPtr& lease_info) { + if (!lease_info) { + isc_throw(BadValue, "lease information missing"); + } + + // These are mandatory parameters. + IOAddress addr = getAddress(lease_info, "ip-address"); + SubnetID subnet_id = getUint32(lease_info, "subnet-id"); + + if (!addr.isV4()) { + isc_throw(BadValue, "Non-IPv4 address specified: " << addr); + } + + // Not a most straightforward conversion, but it works. + string hwaddr_txt = getString(lease_info, "hw-address"); + HWAddr hwaddr = HWAddr::fromText(hwaddr_txt); + HWAddrPtr hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr)); + + Subnet4Ptr subnet = cfg->getCfgSubnets4()->getSubnet(subnet_id); + if (!subnet) { + isc_throw(BadValue, "Invalid subnet-id: No IPv4 subnet with subnet-id=" + << subnet_id << " currently configured."); + } + + if (!subnet->inRange(addr)) { + isc_throw(BadValue, "The address " << addr.toText() << " does not belong " + "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id); + } + + // Client-id is optional. + ClientIdPtr client_id; + if (lease_info->contains("client-id")) { + string txt = getString(lease_info, "client-id"); + client_id = ClientId::fromText(txt); + } + + // These parameters are optional. If not specified, we'll derive them from + // the current subnet configuration, if possible. + uint32_t valid_lft = 0; + if (lease_info->contains("valid-lft")) { + valid_lft = getUint32(lease_info, "valid-lft"); + } else { + valid_lft = subnet->getValid(); + } + + /// Let's calculate client last transmission time (cltt). If expiration + /// timestamp is specified explicitly, we will use that. Note there are no + /// checks whether this is in the past. There may be valid cases when user + /// wants to insert expired leases, e.g. when migrating from one DHCP server + /// to another and wants to migrate the database as is, without discarding + /// any leases. + time_t cltt; + if (lease_info->contains("expire")) { + int64_t tmp = getUint32(lease_info, "expire"); + cltt = static_cast<time_t>(tmp - valid_lft); + } else { + cltt = time(NULL); + } + + bool fqdn_fwd = false; + if (lease_info->contains("fqdn-fwd")) { + fqdn_fwd = getBoolean(lease_info, "fqdn-fwd"); + } + bool fqdn_rev = false; + if (lease_info->contains("fqdn-rev")) { + fqdn_rev = getBoolean(lease_info, "fqdn-rev"); + } + string hostname; + if (lease_info->contains("hostname")) { + hostname = getString(lease_info, "hostname"); + } + if (hostname.empty() && (fqdn_fwd || fqdn_rev)) { + isc_throw(BadValue, "No hostname specified and either forward or reverse" + " fqdn was set to true."); + } + + uint32_t state = 0; + if (lease_info->contains("state")) { + state = getUint8(lease_info, "state"); + } + + // Check if the state value is sane. + if (state > Lease::STATE_EXPIRED_RECLAIMED) { + isc_throw(BadValue, "Invalid state value: " << state << ", supported " + "values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)"); + } + + // Let's fabricate some data and we're ready to go. + uint32_t t1 = subnet->getT1(); + uint32_t t2 = subnet->getT2(); + + Lease4Ptr l(new Lease4(addr, hwaddr_ptr, client_id, valid_lft, t1, t2, + cltt, subnet_id, + fqdn_fwd, fqdn_rev, hostname)); + l->state_ = state; + return (l); +} + +Lease6Ptr +Lease6Parser::parse(ConstSrvConfigPtr& cfg, + const ConstElementPtr& lease_info) { + if (!lease_info) { + isc_throw(BadValue, "lease information missing"); + } + + // These are mandatory parameters. + IOAddress addr = getAddress(lease_info, "ip-address"); + SubnetID subnet_id = getUint32(lease_info, "subnet-id"); + + if (addr.isV4()) { + isc_throw(BadValue, "Non-IPv6 address specified: " << addr); + } + + // Not a most straightforward conversion, but it works. + string duid_txt = getString(lease_info, "duid"); + DUID duid = DUID::fromText(duid_txt); + DuidPtr duid_ptr = DuidPtr(new DUID(duid)); + + // Check if the subnet-id specified is sane. + Subnet6Ptr subnet = cfg->getCfgSubnets6()->getSubnet(subnet_id); + if (!subnet) { + isc_throw(BadValue, "Invalid subnet-id: No IPv6 subnet with subnet-id=" + << subnet_id << " currently configured."); + } + + Lease::Type type = Lease::TYPE_NA; + uint8_t prefix_len = 128; + if (lease_info->contains("type")) { + string txt = getString(lease_info, "type"); + if (txt == "IA_NA") { + type = Lease::TYPE_NA; + } else if (txt == "IA_TA") { + type = Lease::TYPE_TA; + } else if (txt == "IA_PD") { + type = Lease::TYPE_PD; + + prefix_len = getUint8(lease_info, "prefix-len"); + } else { + isc_throw(BadValue, "Incorrect lease type: " << txt << ", the only " + "supported values are: na, ta and pd"); + } + } + + // Check if the address specified really belongs to the subnet. + if ((type == Lease::TYPE_NA) && !subnet->inRange(addr)) { + isc_throw(BadValue, "The address " << addr.toText() << " does not belong " + "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id); + } + + uint32_t iaid = getUint32(lease_info, "iaid"); + + // Hw-address is optional in v6 leases. + HWAddrPtr hwaddr_ptr; + if (lease_info->contains("hw-address")) { + string hwaddr_txt = getString(lease_info, "hw-address"); + HWAddr hwaddr = HWAddr::fromText(hwaddr_txt); + hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr)); + } + + // These parameters are optional. If not specified, we'll derive them + // from the current subnet configuration, if possible. + uint32_t valid_lft = 0; + if (lease_info->contains("valid-lft")) { + valid_lft = getUint32(lease_info, "valid-lft"); + } else { + valid_lft = subnet->getValid(); + } + + // These parameters are optional. If not specified, we'll derive them + // from the current subnet configuration, if possible. + uint32_t pref_lft = 0; + if (lease_info->contains("preferred-lft")) { + pref_lft = getUint32(lease_info, "preferred-lft"); + } else { + pref_lft = subnet->getValid(); + } + + /// Let's calculate client last transmission time (cltt). If expiration + /// timestamp is specified explicitly, we will use that. Note there are + /// no checks whether this is in the past. There may be valid cases when + /// user wants to insert expired leases, e.g. when migrating from one + /// DHCP server to another and wants to migrate the database as is, without + /// discarding any leases. + time_t cltt; + if (lease_info->contains("expire")) { + int64_t tmp = getUint32(lease_info, "expire"); + cltt = static_cast<time_t>(tmp - valid_lft); + } else { + cltt = time(NULL); + } + + bool fqdn_fwd = false; + if (lease_info->contains("fqdn-fwd")) { + fqdn_fwd = getBoolean(lease_info, "fqdn-fwd"); + } + bool fqdn_rev = false; + if (lease_info->contains("fqdn-rev")) { + fqdn_rev = getBoolean(lease_info, "fqdn-rev"); + } + string hostname; + if (lease_info->contains("hostname")) { + hostname = getString(lease_info, "hostname"); + } + if (hostname.empty() && (fqdn_fwd || fqdn_rev)) { + isc_throw(BadValue, "No hostname specified and either forward or reverse" + " fqdn was set to true."); + } + + uint32_t state = 0; + if (lease_info->contains("state")) { + state = getUint8(lease_info, "state"); + } + + // Check if the state value is sane. + if (state > Lease::STATE_EXPIRED_RECLAIMED) { + isc_throw(BadValue, "Invalid state value: " << state << ", supported " + "values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)"); + } + + // Let's fabricate some data and we're ready to go. + uint32_t t1 = subnet->getT1(); + uint32_t t2 = subnet->getT2(); + + Lease6Ptr l(new Lease6(type, addr, duid_ptr, iaid, pref_lft, valid_lft, t1, t2, + subnet_id, fqdn_fwd, fqdn_rev, hostname, + hwaddr_ptr, prefix_len)); + l->cltt_ = cltt; + l->state_ = state; + return (l); +} + +}; +}; diff --git a/src/hooks/dhcp/lease_cmds/lease_parser.h b/src/hooks/dhcp/lease_cmds/lease_parser.h new file mode 100644 index 0000000000..40400bd94a --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/lease_parser.h @@ -0,0 +1,92 @@ +// Copyright (C) 2017 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/. + +#ifndef LEASE_PARSER_H +#define LEASE_PARSER_H + +#include <cc/data.h> +#include <cc/simple_parser.h> +#include <dhcpsrv/lease.h> +#include <dhcpsrv/srv_config.h> + +namespace isc { +namespace lease_cmds { + +/// @brief Parser for Lease4 structure +/// +/// It expects the data in the following format: +/// { +/// "ip-address": "192.0.2.1", +/// "hw-address": "00:01:02:03:04:05", +/// "client-id": "this-is-a-client", +/// "valid-lft": 3600, +/// "cltt": 12345678, +/// "expire": 1499282530, +/// "subnet-id": 1, +/// "fqdn-fwd": true, +/// "fqdn-rev": true, +/// "hostname": "myhost.example.org", +/// "state": 0 +/// } +class Lease4Parser : public isc::data::SimpleParser { +public: + + /// @brief Parses Element tree and tries to convert to Lease4 + /// + /// See @ref Lease6Parser class description for expected format. + /// + /// @param cfg Currently running config (used for sanity checks and defaults) + /// @param lease_info structure to be parsed + /// @return A pointer to Lease4 + /// @throw BadValue if any of the parameters is invalid + /// @throw DhcpConfigError if mandatory parameter is missing + virtual isc::dhcp::Lease4Ptr parse(isc::dhcp::ConstSrvConfigPtr& cfg, + const isc::data::ConstElementPtr& lease_info); + + /// @brief virtual dtor (does nothing) + virtual ~Lease4Parser() {} +}; + +/// @brief Parser for Lease6 structure +/// +/// { +/// "address": "2001:db8::1", +/// "duid": "00:01:02:03:04:05", +/// "type": "IA_NA", +/// "cltt": 12345678, +/// "preferred-lft": 3600, +/// "valid-lft": 3600, +/// "expire": 1499282530, +/// "subnet-id": 1, +/// "fqdn-fwd": true, +/// "fqdn-rev": true, +/// "hostname": "myhost.example.org", +/// "state": 0 +/// } + +/// It expects the input data to use the following format: +class Lease6Parser : public isc::data::SimpleParser { +public: + /// @brief Parses Element tree and tries to convert to Lease4 + /// + /// See @ref Lease6Parser class description for expected format. + /// + /// @param cfg Currently running config (used for sanity checks and defaults) + /// @param lease_info structure to be parsed + /// @return A pointer to Lease4 + /// @throw BadValue if any of the parameters is invalid + /// @throw DhcpConfigError if mandatory parameter is missing + virtual isc::dhcp::Lease6Ptr parse(isc::dhcp::ConstSrvConfigPtr& cfg, + const isc::data::ConstElementPtr& lease_info); + + /// @brief virtual dtor (does nothing) + virtual ~Lease6Parser() {} +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif diff --git a/src/hooks/dhcp/lease_cmds/load_unload.cc b/src/hooks/dhcp/lease_cmds/load_unload.cc new file mode 100644 index 0000000000..c1277cfc52 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/load_unload.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2017 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/. + +/// @file load_unload.cc Defines the load and unload hooks library functions. + +#include <config.h> +#include <lease_cmds.h> +#include <lease_cmds_log.h> +#include <string> +#include <hooks/hooks.h> +#include <exceptions/exceptions.h> + +using namespace isc::hooks; +using namespace isc::lease_cmds; + +boost::shared_ptr<LeaseCmds> instance; + +extern "C" { + +/// @brief This function is called when the library is loaded. +/// +/// This function creates LeaseCmds object that registers +/// additional commands. +/// +/// @param handle library handle (ignored) +/// @return 0 when initialization is successful, 1 otherwise +int load(LibraryHandle& /*handle*/) { + + try { + instance.reset(new LeaseCmds()); + } catch (const isc::Unexpected& ex) { + LOG_ERROR(lease_cmds_logger, LEASE_CMDS_INIT_FAILED) + .arg(ex.what()); + return (1); + } + + LOG_INFO(lease_cmds_logger, LEASE_CMDS_INIT_OK); + return (0); +} + +/// @brief This function is called when the library is unloaded. +/// +/// This function creates LeaseCmds object that deregisters +/// additional commands. +/// +/// @return 0 if deregistration was successful, 1 otherwise +int unload() { + try { + instance.reset(); + } catch (const isc::Unexpected& ex) { + LOG_ERROR(lease_cmds_logger, LEASE_CMDS_DEINIT_FAILED) + .arg(ex.what()); + return (1); + } + + LOG_INFO(lease_cmds_logger, LEASE_CMDS_DEINIT_OK); + return (0); +} + +} diff --git a/src/hooks/dhcp/lease_cmds/tests/.gitignore b/src/hooks/dhcp/lease_cmds/tests/.gitignore new file mode 100644 index 0000000000..4ec4015b05 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/tests/.gitignore @@ -0,0 +1,5 @@ +lease_cmds_unittests +lease_cmds_unittests.log +lease_cmds_unittests.trs +test-suite.log +*~
\ No newline at end of file diff --git a/src/hooks/dhcp/lease_cmds/tests/Makefile.am b/src/hooks/dhcp/lease_cmds/tests/Makefile.am new file mode 100644 index 0000000000..0e987d2cd6 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/tests/Makefile.am @@ -0,0 +1,57 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/lease_cmds -I$(top_srcdir)/src/hooks/dhcp/lease_cmds +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DLEASE_CMDS_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/lease_cmds/.libs/libdhcp_lease_cmds.so\" +AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +# Unit test data files need to get installed. +EXTRA_DIST = + +CLEANFILES = *.gcno *.gcda + +# TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) +LOG_COMPILER = $(LIBTOOL) +AM_LOG_FLAGS = --mode=execute + +TESTS = +if HAVE_GTEST +TESTS += lease_cmds_unittests + +lease_cmds_unittests_SOURCES = run_unittests.cc +lease_cmds_unittests_SOURCES += lease_cmds_unittest.cc + +lease_cmds_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) + +lease_cmds_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) + +lease_cmds_unittests_CXXFLAGS = $(AM_CXXFLAGS) +if USE_CLANGPP +# This is to workaround unused variables tcout and tcerr in +# log4cplus's streams.h and unused parameters from some of the +# Boost headers. +lease_cmds_unittests_CXXFLAGS += -Wno-unused-parameter +endif + +lease_cmds_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +lease_cmds_unittests_LDADD += $(LOG4CPLUS_LIBS) +lease_cmds_unittests_LDADD += $(CRYPTO_LIBS) +lease_cmds_unittests_LDADD += $(BOOST_LIBS) +lease_cmds_unittests_LDADD += $(GTEST_LDADD) +endif +noinst_PROGRAMS = $(TESTS) diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc new file mode 100644 index 0000000000..30cdd0da2e --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc @@ -0,0 +1,2070 @@ +// Copyright (C) 2017 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 <exceptions/exceptions.h> +#include <hooks/hooks_manager.h> +#include <config/command_mgr.h> +#include <dhcpsrv/lease_mgr.h> +#include <dhcpsrv/lease_mgr_factory.h> +#include <dhcpsrv/cfgmgr.h> +#include <cc/command_interpreter.h> +#include <cc/data.h> +#include <gtest/gtest.h> +#include <cc/data.h> +#include <errno.h> + +using namespace std; +using namespace isc; +using namespace isc::hooks; +using namespace isc::config; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::asiolink; + +namespace { + +/// @brief Test fixture for testing loading and unloading the flex-id library +class LibLoadTest : public ::testing::Test { +public: + /// @brief Constructor + LibLoadTest(std::string lib_filename) + : lib_name_(lib_filename) { + CommandMgr::instance(); + unloadLibs(); + } + + /// @brief Destructor + /// Removes files that may be left over from previous tests + virtual ~LibLoadTest() { + unloadLibs(); + } + + /// @brief Adds library/parameters to list of libraries to be loaded + void addLib(const std::string& lib, ConstElementPtr params) { + libraries_.push_back(make_pair(lib, params)); + } + + /// @brief Load all specified libraries. + /// + /// The libraries are stored in libraries + void loadLibs() { + ASSERT_TRUE(HooksManager::loadLibraries(libraries_)) + << "library loading failed"; + } + + /// @brief Unloads all libraries. + void unloadLibs() { + ASSERT_NO_THROW(HooksManager::unloadLibraries()); + } + + /// @brief Checks whether specified command is registered + /// + /// @param name name of the command to be checked + /// @param expect_true true - must be registered, false - must not be + void checkCommandRegistered(const std::string& name, bool expect_true) { + + // First get the list of registered commands + ConstElementPtr lst = Element::fromJSON("{ \"command\": \"list-commands\" }"); + ConstElementPtr rsp = CommandMgr::instance().processCommand(lst); + + ASSERT_TRUE(rsp); + + ConstElementPtr args = rsp->get("arguments"); + ASSERT_TRUE(args); + + string args_txt = args->str(); + + if (expect_true) { + EXPECT_TRUE(args_txt.find(name) != string::npos); + } else { + EXPECT_TRUE(args_txt.find(name) == string::npos); + } + } + + /// @brief tests specified command and verifies response + /// + /// This method loads the library, sends specific command, + /// then checks if the result is as expected, checks if text response + /// is ok (optional, check skipped if exp_txt is empty) and then returns + /// the response (for possible additional checks). + /// + /// @param cmd JSON command to be sent (must be valid JSON) + /// @param exp_result 0 - success, 1 - error, 2 - ... + /// @param exp_txt expected text response (optional) + /// @return full response returned by the command execution. + ConstElementPtr testCommand(string cmd_txt, int exp_result, string exp_txt) { + // Let's load the library first. + loadLib(); + + ConstElementPtr cmd; + EXPECT_NO_THROW(cmd = Element::fromJSON(cmd_txt)); + if (!cmd) { + ADD_FAILURE() << cmd_txt << " is not a valid JSON, test broken"; + return (ConstElementPtr()); + } + + // Process the command and verify response. + ConstElementPtr rsp = CommandMgr::instance().processCommand(cmd); + checkAnswer(rsp, exp_result, exp_txt); + + return (rsp); + } + + /// @brief Compares the status in the given parse result to a given value. + /// + /// @param answer Element set containing an integer response and string + /// comment. + /// @param exp_status is an integer against which to compare the status. + /// @param exp_txt is expected text (not checked if "") + void checkAnswer(isc::data::ConstElementPtr answer, + int exp_status, + string exp_txt = "") { + int rcode = 0; + isc::data::ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + + if (rcode != exp_status) { + ADD_FAILURE() << "Expected status code " << exp_status + << " but received " << rcode << ", comment: " + << (comment ? comment->str() : "(none)"); + } + + // Ok, parseAnswer interface is weird. If there are no arguments, + // it returns content of text. But if there is an argument, + // it returns the argument and it's not possible to retrieve + // "text" (i.e. comment). + if (comment->getType() != Element::string) { + comment = answer->get("text"); + } + + if (!exp_txt.empty()) { + EXPECT_EQ(exp_txt, comment->stringValue()); + } + } + + /// @brief Loads the library specified by lib_name_ + void loadLib() { + if (libraries_.empty()) { + data::ElementPtr params = data::Element::createMap(); + addLib(lib_name_, params); + } + EXPECT_NO_THROW(loadLibs()); + } + + /// @brief Test checks if specified commands are provided by the library. + /// + /// @param cms a vector of string with command names + void testCommands(const std::vector<string> cmds) { + + // The commands should not be registered yet. + for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) { + checkCommandRegistered(*cmd, false); + } + + loadLib(); + + // The commands should be available after library was loaded. + for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) { + checkCommandRegistered(*cmd, true); + } + + unloadLibs(); + + // and the commands should be gone now. + for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) { + checkCommandRegistered(*cmd, false); + } + + } + + // Check that the library can be loaded and unloaded multiple times. + void testMultipleLoads() { + EXPECT_NO_THROW(loadLib()); + EXPECT_NO_THROW(unloadLibs()); + + EXPECT_NO_THROW(loadLib()); + EXPECT_NO_THROW(unloadLibs()); + + EXPECT_NO_THROW(loadLib()); + EXPECT_NO_THROW(unloadLibs()); + + EXPECT_NO_THROW(loadLib()); + EXPECT_NO_THROW(unloadLibs()); + } + + /// List of libraries to be/being loaded (usually just one) + HookLibsCollection libraries_; + + /// Path to the library filename + std::string lib_name_; +}; + +/// @brief Class dedicated to testing lease_cmds library. +/// +/// Provides convenience methods for loading, testing all commands and +/// unloading the lease_cmds library. +class LeaseCmdsTest : public LibLoadTest { +public: + + /// @brief Pointer to the lease manager + LeaseMgr* lmptr_; + + /// @brief Constructor + /// + /// Sets the library filename and clears the lease manager pointer. + /// Also ensured there is no lease manager leftovers from previous + /// test. + LeaseCmdsTest() + :LibLoadTest(LEASE_CMDS_LIB_SO) { + LeaseMgrFactory::destroy(); + lmptr_ = 0; + } + + /// @brief Destructor + /// + /// Removes library (if any), destroys lease manager (if any). + virtual ~LeaseCmdsTest() { + // destroys lease manager first because the other order triggers + // a clang/boost bug + LeaseMgrFactory::destroy(); + unloadLibs(); + lmptr_ = 0; + } + + /// @brief Initializes lease manager (and optionally populates it with a lease) + /// + /// Creates a lease manager (memfile, trimmed down to keep everything in memory + /// only) and optionally can create a lease, which is useful for leaseX-get and + /// leasex-del type of tests. For lease details, see @ref createLease4 and + /// @ref createLease6. + /// + /// @param v6 true = v6, false = v4 + /// @param insert_lease governs whether a lease should be pre-inserted + void initLeaseMgr(bool v6, bool insert_lease) { + + LeaseMgrFactory::destroy(); + std::ostringstream s; + s << "type=memfile persist=false " << (v6 ? "universe=6" : "universe=4"); + LeaseMgrFactory::create(s.str()); + + lmptr_ = &(LeaseMgrFactory::instance()); + ASSERT_TRUE(lmptr_); + + CfgMgr& cfg_mgr = CfgMgr::instance(); + if (v6) { + Subnet6Ptr subnet6(new Subnet6(IOAddress("2001:db8::"), 48, 1, 2, 3, 4, 66)); + CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6(); + subnets->add(subnet6); + cfg_mgr.commit(); + } else { + Subnet4Ptr subnet4(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 44)); + CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4(); + subnets->add(subnet4); + cfg_mgr.commit(); + } + + if (insert_lease) { + if (v6) { + lmptr_->addLease(createLease6()); + } else { + lmptr_->addLease(createLease4()); + } + } + } + + /// @brief Creates an IPv4 lease + /// + /// Lease parameters: ip-address = 192.0.2.1, hwaddr = 08:08:08:08:08:08, + /// client-id = 42:42:42:42:42:42:42:42, valid lifetime = 3600, + /// cltt = 12345678, subnet-id = 44, fqdn-fwd = false, fqdn-rev = true + /// hostname = myhost.example.com + /// + /// @return Returns the lease created + Lease4Ptr createLease4() { + Lease4Ptr lease(new Lease4()); + + lease->addr_ = IOAddress("192.0.2.1"); + + // Initialize unused fields. + lease->t1_ = 0; // Not saved + lease->t2_ = 0; // Not saved + + // Set other parameters. For historical reasons, address 0 is not used. + lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x08), HTYPE_ETHER)); + lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42))); + lease->valid_lft_ = 3600; + lease->cltt_ = 12345678; + lease->subnet_id_ = 44; + lease->fqdn_fwd_ = false; + lease->fqdn_rev_ = true; + lease->hostname_ = "myhost.example.com."; + + return (lease); + } + + /// @brief Creates an IPv6 lease + /// + /// Lease parameters: ip-address = 2001:db8::1, duid = 77:77:77:77:77:77:77:77, + /// cltt = 12345678, subnet-id = 66, fqdn-fwd = false, fqdn-rev = true, + /// hostname = myhost.example.com preferred lifetime = 1800, + /// valid lifetime = 3600 + /// + /// @return Returns the lease created + Lease6Ptr createLease6() { + Lease6Ptr lease(new Lease6()); + + lease->addr_ = IOAddress("2001:db8::1"); + lease->type_ = Lease::TYPE_NA; + lease->prefixlen_ = 128; + lease->iaid_ = 42; + lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77))); + lease->preferred_lft_ = 1800; + lease->valid_lft_ = 3600; + lease->cltt_ = 12345678; + lease->subnet_id_ = 66; + lease->fqdn_fwd_ = true; + lease->fqdn_rev_ = true; + lease->hostname_ = "myhost.example.com."; + + return (lease); + } + + /// @brief Checks if specified response contains IPv4 lease + /// + /// @param lease Element tree that represents a lease + /// @param ip expected IP address + /// @param subnet_id expected subnet-id + /// @param hwaddr expected value of hardware address + /// @param client_id_required true if client-id is expected + void checkLease4(ConstElementPtr l, std::string ip, + uint32_t subnet_id, std::string hwaddr, + bool client_id_required) { + ASSERT_TRUE(l); + ASSERT_TRUE(l->get("ip-address")); + EXPECT_EQ(ip, l->get("ip-address")->stringValue()); + + ASSERT_TRUE(l->get("subnet-id")); + EXPECT_EQ(subnet_id, l->get("subnet-id")->intValue()); + + ASSERT_TRUE(l->get("hw-address")); + EXPECT_EQ(hwaddr, l->get("hw-address")->stringValue()); + + // client-id may or may not appear + if (client_id_required) { + EXPECT_TRUE(l->get("client-id")); + } + + // Check that other parameters are there. + EXPECT_TRUE(l->contains("valid-lft")); + EXPECT_TRUE(l->contains("cltt")); + EXPECT_TRUE(l->contains("subnet-id")); + EXPECT_TRUE(l->contains("state")); + EXPECT_TRUE(l->contains("fqdn-fwd")); + EXPECT_TRUE(l->contains("fqdn-rev")); + EXPECT_TRUE(l->contains("hostname")); + EXPECT_TRUE(l->contains("state")); + + // Check that there are no v6 specific fields + EXPECT_FALSE(l->contains("prefix")); + EXPECT_FALSE(l->contains("duid")); + EXPECT_FALSE(l->contains("preferred-lft")); + } + + /// @brief Checks if specified response contains IPv6 lease + /// + /// @param lease Element tree that represents a lease + /// @param ip expected IP address (or prefix) + /// @param prefixlen prefix length (0 = expect address) + /// @param subnet_id expected subnet-id + /// @param duid expected value of DUID + /// @param hwaddr_required true if hwaddr is expected + void checkLease6(ConstElementPtr l, std::string ip, + uint8_t prefixlen, + uint32_t subnet_id, std::string duid, + bool hwaddr_required) { + + ASSERT_TRUE(l); + + ASSERT_TRUE(l->contains("ip-address")); + EXPECT_EQ(ip, l->get("ip-address")->stringValue()); + if (prefixlen != 0) { + ASSERT_TRUE(l->get("prefix-len")); + EXPECT_EQ(prefixlen, l->get("prefix-len")->intValue()); + } + + ASSERT_TRUE(l->contains("subnet-id")); + EXPECT_EQ(subnet_id, l->get("subnet-id")->intValue()); + + ASSERT_TRUE(l->contains("duid")); + EXPECT_EQ(duid, l->get("duid")->stringValue()); + + // hwaddr may or may not appear + if (hwaddr_required) { + EXPECT_TRUE(l->get("hwaddr")); + } + + // Check that there are expected fields + EXPECT_TRUE(l->contains("preferred-lft")); + EXPECT_TRUE(l->contains("valid-lft")); + EXPECT_TRUE(l->contains("cltt")); + EXPECT_TRUE(l->contains("subnet-id")); + EXPECT_TRUE(l->contains("fqdn-fwd")); + EXPECT_TRUE(l->contains("fqdn-rev")); + EXPECT_TRUE(l->contains("hostname")); + EXPECT_TRUE(l->contains("state")); + + // Check that there are no v4 specific fields. + EXPECT_FALSE(l->contains("client-id")); + } +}; + +// Simple test that checks the library really registers the commands. +TEST_F(LeaseCmdsTest, commands) { + + vector<string> cmds = { "lease4-add", "lease6-add", + "lease4-get", "lease6-get", + "lease4-del", "lease6-del", + "lease4-update", "lease6-update", + "lease4-wipe", "lease6-wipe" }; + testCommands(cmds); +} + +// Check that the library can be loaded and unloaded multiple times. +TEST_F(LeaseCmdsTest, multipleLoads) { + testMultipleLoads(); +} + +using namespace isc::dhcp; + +// Check that lease4-add with missing parameters will fail. +TEST_F(LeaseCmdsTest, Lease4AddMissingParams) { + + // Initialize lease manager (false = v4, false = don't add leases) + initLeaseMgr(false, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Everything missing. What sort of crap is that? + string txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just ip is not enough (subnet-id and hwaddr missing). + txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.123\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Better, but still no luck. (hwaddr missing). + txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.202\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'hw-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Close, but no cigars. (ip-address missing). + txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verify that lease4-add can be rejected if parameters are specified, but +// have incorrect values. +TEST_F(LeaseCmdsTest, Lease4AddBadParams) { + + // Initialize lease manager (false = v4, false = don't add leases) + initLeaseMgr(false, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // All params are there, but there's no subnet-id 123 configured. + // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6). + string txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"ip-address\": \"192.0.2.202\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + string exp_rsp = "Invalid subnet-id: No IPv4 subnet with subnet-id=123 currently configured."; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // This time the IP address does not belong to the subnet. + txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"10.0.0.1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "The address 10.0.0.1 does not belong to subnet 192.0.2.0/24, subnet-id=44"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // We don't use any of that bleeding edge nonsense in this museum. v4 only. + txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"2001:db8::1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "Non-IPv4 address specified: 2001:db8::1"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // currently defined states are 0,1 and 2. 123 is junk. + txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"state\": 123\n" + " }\n" + "}"; + exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 " + "(declined) and 2 (expired-reclaimed)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Check that a simple, well formed lease4 can be added. +TEST_F(LeaseCmdsTest, Lease4Add) { + + // Initialize lease manager (false = v4, false = don't add leases) + initLeaseMgr(false, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.202\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + string exp_rsp = "Lease added."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is really there. + Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202")); + ASSERT_TRUE(l); + + // Make sure the lease have proper value set. + ASSERT_TRUE(l->hwaddr_); + EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false)); + EXPECT_EQ(3, l->valid_lft_); // taken from subnet configuration + EXPECT_EQ(false, l->fqdn_fwd_); + EXPECT_EQ(false, l->fqdn_rev_); + EXPECT_EQ("", l->hostname_); + + // Test execution is fast. The cltt should be set to now. In some rare + // cases we could have the seconds counter to tick, so having a value off + // by one is ok. + EXPECT_LE(abs(l->cltt_ - time(NULL)), 1); + EXPECT_EQ(0, l->state_); + +} + +// Check that a well formed lease4 with tons of parameters can be added. +TEST_F(LeaseCmdsTest, Lease4AddFull) { + + // Initialize lease manager (false = v4, false = don't add leases) + initLeaseMgr(false, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease4-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.202\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"client-id\": \"01:02:03:04:05:06:07:08\",\n" + " \"valid-lft\": 1000,\n" + " \"expire\": 12345678,\n" + " \"fqdn-fwd\": true,\n" + " \"fqdn-rev\": true,\n" + " \"hostname\": \"urania.example.org\"" + " }\n" + "}"; + string exp_rsp = "Lease added."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is really there. + Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202")); + ASSERT_TRUE(l); + EXPECT_EQ("192.0.2.202", l->addr_.toText()); + ASSERT_TRUE(l->hwaddr_); + EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false)); + ASSERT_TRUE(l->client_id_); + EXPECT_EQ("01:02:03:04:05:06:07:08", l->client_id_->toText()); + EXPECT_EQ(l->cltt_, 12344678); // expire (12345678) - valid_lft(1000) + EXPECT_EQ(true, l->fqdn_fwd_); + EXPECT_EQ(true, l->fqdn_rev_); + EXPECT_EQ("urania.example.org", l->hostname_); +} + +// Check that lease6-add with missing parameters will fail. +TEST_F(LeaseCmdsTest, Lease6AddMissingParams) { + + // Initialize lease manager (true = v6, false = don't add leases) + initLeaseMgr(true, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Everything missing. What sort of nonsense is that? + string txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just ip is not enough (subnet-id and duid missing). + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"ip-address\": \"2001:db8::3\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just subnet-id and ip is not enough (duid missing). + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::3\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'duid' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just subnet-id and duid is not enough (ip, iaid missing). + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just subnet-id, duid and iaid is not enough (ip missing). + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Close, but no cigars. Still missing iaid. + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"ip-address\": \"2001:db8::3\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'iaid' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verify that lease6-add can be rejected if parameters specified, but +// have incorrect values. +TEST_F(LeaseCmdsTest, Lease6AddBadParams) { + + // Initialize lease manager (true = v6, false = don't add leases) + initLeaseMgr(true, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Invalid subnet-id. Only 66 is configured. + string txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"ip-address\": \"2001:db8::3\",\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n" + " }\n" + "}"; + string exp_rsp = "Invalid subnet-id: No IPv6 subnet with subnet-id=123 currently configured."; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // This time the IP address does not belong to the subnet. + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"3000::3\",\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n" + " }\n" + "}"; + exp_rsp = "The address 3000::3 does not belong to subnet 2001:db8::/48, subnet-id=66"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // v4? You're a time traveler from early 80s or what? + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"192.0.2.1\",\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n" + " }\n" + "}"; + exp_rsp = "Non-IPv6 address specified: 192.0.2.1"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Invalid state: the only supported values are 0,1,2. + txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::1\",\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n," + " \"state\": 123\n" + " }\n" + "}"; + exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 " + "(declined) and 2 (expired-reclaimed)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Check that a simple, well formed lease6 can be added. +TEST_F(LeaseCmdsTest, Lease6Add) { + + // Initialize lease manager (true = v6, false = don't add leases) + initLeaseMgr(true, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::3\",\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n" + " }\n" + "}"; + string exp_rsp = "Lease added."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is really there. + EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::3"))); +} + +// Check that a simple, well formed prefix lease can be added. +TEST_F(LeaseCmdsTest, Lease6AddPrefix) { + + // Initialize lease manager (true = v6, false = don't add leases) + initLeaseMgr(true, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8:abcd::\",\n" + " \"prefix-len\": 48,\n" + " \"type\": \"IA_PD\",\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"iaid\": 1234\n" + " }\n" + "}"; + string exp_rsp = "Lease added."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is really there. + Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:abcd::")); + ASSERT_TRUE(l); + EXPECT_EQ(Lease::TYPE_PD, l->type_); + EXPECT_EQ(48, l->prefixlen_); +} + +// Check that a well formed lease6 with tons of parameters can be added. +TEST_F(LeaseCmdsTest, Lease6AddFullAddr) { + + // Initialize lease manager (true = v6, false = don't add leases) + initLeaseMgr(true, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease6-add\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::3\",\n" + " \"duid\": \"01:02:03:04:05:06:07:08\",\n" + " \"iaid\": 1234,\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"preferred-lft\": 500,\n" + " \"valid-lft\": 1000,\n" + " \"expire\": 12345678,\n" + " \"fqdn-fwd\": true,\n" + " \"fqdn-rev\": true,\n" + " \"hostname\": \"urania.example.org\"" + " }\n" + "}"; + string exp_rsp = "Lease added."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is really there. + Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::3")); + ASSERT_TRUE(l); + EXPECT_EQ(Lease::TYPE_NA, l->type_); + EXPECT_EQ("2001:db8::3", l->addr_.toText()); + ASSERT_TRUE(l->hwaddr_); + EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false)); + ASSERT_TRUE(l->duid_); + EXPECT_EQ("01:02:03:04:05:06:07:08", l->duid_->toText()); + EXPECT_EQ(l->cltt_, 12344678); // expire (12345678) - valid_lft(1000) + EXPECT_EQ(true, l->fqdn_fwd_); + EXPECT_EQ(true, l->fqdn_rev_); + EXPECT_EQ("urania.example.org", l->hostname_); +} + +// Checks that lease6-get can handle a situation when the query is +// broken (some required parameters are missing). +TEST_F(LeaseCmdsTest, Lease4GetMissingParams) { + + // No parameters whatsoever. You want just a lease, any lease? + string cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "Mandatory 'subnet-id' parameter missing."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Just the subnet-id won't cut it, either. + cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 123" + " }\n" + "}"; + exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // We can't identify your laptop by color. Sorry, buddy. + cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier-type\": \"color\",\n" + " \"identifier\": \"blue\"\n" + " }\n" + "}"; + exp_rsp = "Incorrect identifier type: color, the only supported values are: " + "address, hw-address, duid"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Query by DUID is not supported in v4. Sorry. + cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier-type\": \"duid\",\n" + " \"identifier\": \"01:01:01:01:01:01\"\n" + " }\n" + "}"; + exp_rsp = "Query by duid is not allowed in v4."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Identifier value is missing. + cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier-type\": \"hw-address\"\n" + " }\n" + "}"; + exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Identifier-type is missing. + cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier\": \"01:02:03:04:05\"\n" + " }\n" + "}"; + exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease4-get sanitizes its input. +TEST_F(LeaseCmdsTest, Lease4GetByAddrBadParam) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Invalid family + string cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"ip-address\": \"2001:db8::1\"" + " }\n" + "}"; + string exp_rsp = "Invalid IPv4 address specified: 2001:db8::1"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // This is way off + cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"ip-address\": \"221B Baker St.\"" + " }\n" + "}"; + exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease4-get can handle a situation when the query is +// valid, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease4GetByAddrNotFound) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Invalid + string cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.5\"," + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "Lease not found."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease4-get can return a lease by address. +TEST_F(LeaseCmdsTest, Lease4GetByAddr) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.1\"" + " }\n" + "}"; + string exp_rsp = "IPv4 lease found."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease parameters were indeed returned. + ASSERT_TRUE(rsp); + ConstElementPtr lease = rsp->get("arguments"); + ASSERT_TRUE(lease); + + // Let's check if the response makes any sense. + checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false); +} + +// Checks that lease4-get can handle a situation when the query is +// well formed, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease4GetByHWAddrNotFound) { + + // Initialize lease manager (false = v4, false = don't add a lease) + initLeaseMgr(false, false); + + // No such lease. + string cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"identifier-type\": \"hw-address\"," + " \"identifier\": \"01:02:03:04:05:06\"," + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "Lease not found."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease4-get can find a lease by hardware address. +TEST_F(LeaseCmdsTest, Lease4GetByHWAddr) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Invalid + string cmd = + "{\n" + " \"command\": \"lease4-get\",\n" + " \"arguments\": {" + " \"identifier-type\": \"hw-address\"," + " \"identifier\": \"08:08:08:08:08:08\"," + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "IPv4 lease found."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease parameters were indeed returned. + ASSERT_TRUE(rsp); + ConstElementPtr lease = rsp->get("arguments"); + ASSERT_TRUE(lease); + + // Let's check if the response makes any sense. + checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false); +} + +// Checks that lease6-get(addr) can handle a situation when +// the query is correctly formed, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease6GetByAddr6NotFound) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 1,\n" + " \"ip-address\": \"2001:db8::2\"\n" + " }\n" + "}"; + string exp_rsp = "Lease not found."; + + // Note the status expected is empty. The query completed correctly, + // just didn't found the lease. + testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease6-get(subnet-id, addr) can handle a situation when +// the query is correctly formed, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease6GetByDuidNotFound) { + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 1,\n" + " \"identifier-type\": \"duid\"," + " \"identifier\": \"00:01:02:03:04:05:06:07\"\n" + " }\n" + "}"; + string exp_rsp = "Lease not found."; + + // Note the status expected is empty. The query completed correctly, + // just didn't found the lease. + testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease6-get(subnet-id, addr6) can handle a situation when +// the query is correctly formed and the lease is returned. +TEST_F(LeaseCmdsTest, Lease6GetByAddr) { + + initLeaseMgr(true, true); // (true = v6, true = create a lease) + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {\n" + " \"ip-address\": \"2001:db8::1\"\n" + " }\n" + "}"; + string exp_rsp = "IPv6 lease found."; + + // The status expected is success. The lease should be returned. + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + ASSERT_TRUE(rsp); + + ConstElementPtr lease = rsp->get("arguments"); + ASSERT_TRUE(lease); + + // Now check that the lease was indeed returned. + checkLease6(lease, "2001:db8::1", 0, 66, "77:77:77:77:77:77:77:77", false); +} + +// Checks that lease6-get sanitizes its input. +TEST_F(LeaseCmdsTest, Lease6GetByAddrBadParam) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Invalid family + string cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.1\"" + " }\n" + "}"; + string exp_rsp = "Invalid IPv6 address specified: 192.0.2.1"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // This is way off + cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {" + " \"ip-address\": \"221B Baker St.\"" + " }\n" + "}"; + exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease6-get(subnet-id, type, addr6) can handle a situation when +// the query is correctly formed and the lease is returned. +TEST_F(LeaseCmdsTest, Lease6GetByAddrPrefix) { + + // We need to get a prefix lease. We need to create it by hand. + initLeaseMgr(true, false); // (true = v6, true = create a lease) + + // Let's start with regular address lease and make it a prefix lease. + Lease6Ptr l = createLease6(); + l->addr_ = IOAddress("2001:db8:1234:ab::"); + l->type_ = Lease::TYPE_PD; + l->prefixlen_ = 56; + lmptr_->addLease(l); + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {" + " \"type\": \"IA_PD\"," + " \"ip-address\": \"2001:db8:1234:ab::\"" + " }\n" + "}"; + string exp_rsp = "IPv6 lease found."; + + // The status expected is success. The lease should be returned. + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + ASSERT_TRUE(rsp); + + ConstElementPtr lease = rsp->get("arguments"); + ASSERT_TRUE(lease); + + // Now check that the lease was indeed returned. + checkLease6(lease, "2001:db8:1234:ab::", 56, 66, "77:77:77:77:77:77:77:77", false); +} + +// Checks that lease6-get(subnet-id, iaid, identifier-type, identifier) can handle +// a situation when the query returns a lease. +TEST_F(LeaseCmdsTest, Lease6GetByDUID) { + + initLeaseMgr(true, true); // (true = v6, true = create a lease) + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-get\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"iaid\": 42," + " \"identifier-type\": \"duid\"," + " \"identifier\": \"77:77:77:77:77:77:77:77\"\n" + " }\n" + "}"; + string exp_rsp = "IPv6 lease found."; + + // The status expected is success. The lease should be returned. + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + ASSERT_TRUE(rsp); + + ConstElementPtr lease = rsp->get("arguments"); + ASSERT_TRUE(lease); + + // Now check that the lease was indeed returned. + checkLease6(lease, "2001:db8::1", 0, 66, "77:77:77:77:77:77:77:77", false); +} + +// Test checks if lease4-update handler refuses calls with missing parameters. +TEST_F(LeaseCmdsTest, Lease4UpdateMissingParams) { + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Everything missing. What sort of crap is that? + string txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just ip is not enough (subnet-id and hwaddr missing). + txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.123\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Better, but still no luck. (hwaddr missing). + txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.202\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'hw-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Close, but no cigars. (ip-address missing). + txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verify that lease4-update can be rejected if parameters are specified, but +// have incorrect values. +TEST_F(LeaseCmdsTest, Lease4UpdateBadParams) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // All params are there, but there's no subnet-id 123 configured. + // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6). + string txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"ip-address\": \"192.0.2.202\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + string exp_rsp = "Invalid subnet-id: No IPv4 subnet with subnet-id=123 currently configured."; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // This time the new IP address does not belong to the subnet. + txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"10.0.0.1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "The address 10.0.0.1 does not belong to subnet 192.0.2.0/24, subnet-id=44"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // We don't use any of that bleeding edge nonsense in this museum. v4 only. + txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"2001:db8::1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "Non-IPv4 address specified: 2001:db8::1"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Check that lease4-update correctly handles case when there is +// no lease to be updated. +TEST_F(LeaseCmdsTest, Lease4UpdateNoLease) { + + // Initialize lease manager (false = v4, false = don't add any lease) + initLeaseMgr(false, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"hostname\": \"newhostname.example.org\"" + " }\n" + "}"; + string exp_rsp = "failed to update the lease with address 192.0.2.1 - no such lease"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Check that a lease4 can be updated. We're changing hw-address +// and a hostname. +TEST_F(LeaseCmdsTest, Lease4Update) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease4-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"192.0.2.1\",\n" + " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n" + " \"hostname\": \"newhostname.example.org\"" + " }\n" + "}"; + string exp_rsp = "IPv4 lease updated."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is still there. + Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1")); + ASSERT_TRUE(l); + + // Make sure it's been updated. + ASSERT_TRUE(l->hwaddr_); + EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false)); + EXPECT_EQ("newhostname.example.org", l->hostname_); +} + +// Test checks if lease6-update handler refuses calls with missing parameters. +TEST_F(LeaseCmdsTest, Lease6UpdateMissingParams) { + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Everything missing. What sort of crap is that? + string txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Just ip is not enough (subnet-id and hwaddr missing). + txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"ip-address\": \"2001:db8::1\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Better, but still no luck. (duid missing). + txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"ip-address\": \"2001:db8::1\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'duid' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // ip-address and identifier-type missing. + txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 44,\n" + " \"duid\": \"1a:1b:1c:1d:1e:1f\"\n" + " }\n" + "}"; + exp_rsp = "missing parameter 'ip-address' (<string>:3:19)"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Verify that lease6-update can be rejected if parameters are specified, but +// have incorrect values. +TEST_F(LeaseCmdsTest, Lease6UpdateBadParams) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // All params are there, but there's no subnet-id 123 configured. + // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6). + string txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"ip-address\": \"2001:db8::1\",\n" + " \"duid\": \"88:88:88:88:88:88:88:88\"\n" + " }\n" + "}"; + string exp_rsp = "Invalid subnet-id: No IPv6 subnet with subnet-id=123 currently configured."; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // This time the new IP address does not belong to the subnet. + txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"3000::1\",\n" + " \"duid\": \"88:88:88:88:88:88:88:88\"\n" + " }\n" + "}"; + exp_rsp = "The address 3000::1 does not belong to subnet 2001:db8::/48, subnet-id=66"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); + + // Nope, can't do v4 address in v6 lease. + txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"192.0.2.1\",\n" + " \"duid\": \"88:88:88:88:88:88:88:88\"\n" + " }\n" + "}"; + exp_rsp = "Non-IPv6 address specified: 192.0.2.1"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Check that a lease6 can be updated. We're changing hw-address +// and a hostname. +TEST_F(LeaseCmdsTest, Lease6Update) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::1\",\n" + " \"iaid\": 7654321,\n" + " \"duid\": \"88:88:88:88:88:88:88:88\",\n" + " \"hostname\": \"newhostname.example.org\"" + " }\n" + "}"; + string exp_rsp = "IPv6 lease updated."; + testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Now check that the lease is really there. + Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1")); + ASSERT_TRUE(l); + + // Make sure the lease has been updated. + ASSERT_TRUE(l->duid_); + EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText()); + EXPECT_EQ("newhostname.example.org", l->hostname_); + EXPECT_EQ(7654321, l->iaid_); +} + + +// Check that lease6-update correctly handles case when there is +// no lease to be updated. +TEST_F(LeaseCmdsTest, Lease6UpdateNoLease) { + + // Initialize lease manager (true = v6, false = don't add any lease) + initLeaseMgr(true, false); + + // Check that the lease manager pointer is there. + ASSERT_TRUE(lmptr_); + + // Now send the command. + string txt = + "{\n" + " \"command\": \"lease6-update\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::1\",\n" + " \"iaid\": 7654321,\n" + " \"duid\": \"88:88:88:88:88:88:88:88\",\n" + " \"hostname\": \"newhostname.example.org\"" + " }\n" + "}"; + string exp_rsp = "failed to update the lease with address 2001:db8::1 - no such lease"; + testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease6-del can handle a situation when the query is +// broken (some required parameters are missing). +TEST_F(LeaseCmdsTest, Lease4DelMissingParams) { + + // No parameters whatsoever. You want just a lease, any lease? + string cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "Mandatory 'subnet-id' parameter missing."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Just the subnet-id won't cut it, either. + cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 123" + " }\n" + "}"; + exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // We can't identify your laptop by color. Sorry, buddy. + cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier-type\": \"color\",\n" + " \"identifier\": \"blue\"\n" + " }\n" + "}"; + exp_rsp = "Incorrect identifier type: color, the only supported values are: " + "address, hw-address, duid"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Query by DUID is not supported in v4. Sorry. + cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier-type\": \"duid\",\n" + " \"identifier\": \"01:01:01:01:01:01\"\n" + " }\n" + "}"; + exp_rsp = "Delete by duid is not allowed in v4."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Identifier value is missing. + cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier-type\": \"hw-address\"\n" + " }\n" + "}"; + exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // Identifier-type is missing. + cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 123,\n" + " \"identifier\": \"01:02:03:04:05\"\n" + " }\n" + "}"; + exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string."; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease4-del can handle a situation when the query is +// valid, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease4DelByAddrNotFound) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Invalid + string cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.5\"," + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "IPv4 lease not found."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease4-del can return a lease by address. +TEST_F(LeaseCmdsTest, Lease4DelByAddr) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.1\"" + " }\n" + "}"; + string exp_rsp = "IPv4 lease deleted."; + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1"))); +} + + +// Checks that lease4-del sanitizes its input. +TEST_F(LeaseCmdsTest, Lease4DelByAddrBadParam) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Invalid family + string cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"ip-address\": \"2001:db8::1\"" + " }\n" + "}"; + string exp_rsp = "Invalid IPv4 address specified: 2001:db8::1"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // This is way off + cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"ip-address\": \"221B Baker St.\"" + " }\n" + "}"; + exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease4-del can handle a situation when the query is +// well formed, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease4DelByHWAddrNotFound) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // No such lease. + string cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"identifier-type\": \"hw-address\"," + " \"identifier\": \"01:02:03:04:05:06\"," + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "IPv4 lease not found."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); + + // Make sure the lease is still there. + EXPECT_TRUE(lmptr_->getLease4(IOAddress("192.0.2.1"))); +} + +// Checks that lease4-del can find a lease by hardware address. +TEST_F(LeaseCmdsTest, Lease4DelByHWAddr) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Invalid + string cmd = + "{\n" + " \"command\": \"lease4-del\",\n" + " \"arguments\": {" + " \"identifier-type\": \"hw-address\"," + " \"identifier\": \"08:08:08:08:08:08\"," + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "IPv4 lease deleted."; + ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1"))); +} + +// Checks that lease6-del(addr) can handle a situation when +// the query is correctly formed, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease6DelByAddr6NotFound) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 1,\n" + " \"ip-address\": \"2001:db8::2\"\n" + " }\n" + "}"; + string exp_rsp = "IPv6 lease not found."; + + // Note the status expected is empty. The query completed correctly, + // just didn't found the lease. + testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease6-del(subnet-id, addr) can handle a situation when +// the query is correctly formed, but the lease is not there. +TEST_F(LeaseCmdsTest, Lease6DelByDuidNotFound) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 1,\n" + " \"identifier-type\": \"duid\"," + " \"identifier\": \"00:01:02:03:04:05:06:07\"\n" + " }\n" + "}"; + string exp_rsp = "IPv6 lease not found."; + + // Note the status expected is empty. The query completed correctly, + // just didn't found the lease. + testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); + + // Make sure the lease is still there. + EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1"))); +} + +// Checks that lease6-del(subnet-id, addr6) can handle a situation when +// the query is correctly formed and the lease is returned. +TEST_F(LeaseCmdsTest, Lease6DelByAddr) { + + initLeaseMgr(true, true); // (true = v6, true = create a lease) + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"ip-address\": \"2001:db8::1\"" + " }\n" + "}"; + string exp_rsp = "IPv6 lease deleted."; + + // The status expected is success. The lease should be deleted. + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1"))); +} + +// Checks that lease6-del sanitizes its input. +TEST_F(LeaseCmdsTest, Lease6DelByAddrBadParam) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Invalid family + string cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"ip-address\": \"192.0.2.1\"" + " }\n" + "}"; + string exp_rsp = "Invalid IPv6 address specified: 192.0.2.1"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); + + // This is way off + cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"ip-address\": \"221B Baker St.\"" + " }\n" + "}"; + exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease6-del(subnet-id, type, addr6) can handle a situation when +// the query is correctly formed and the lease is deleted. +TEST_F(LeaseCmdsTest, Lease6DelByAddrPrefix) { + + initLeaseMgr(true, false); // (true = v6, false = don't add any leases) + + // Let's start with regular address lease and make it a prefix lease. + Lease6Ptr l = createLease6(); + l->addr_ = IOAddress("2001:db8:1234:ab::"); + l->type_ = Lease::TYPE_PD; + l->prefixlen_ = 56; + lmptr_->addLease(l); + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"type\": \"IA_PD\"," + " \"ip-address\": \"2001:db8:1234:ab::\"" + " }\n" + "}"; + string exp_rsp = "IPv6 lease deleted."; + + // The status expected is success. The lease should be deleted. + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:1234:ab::"))); +} + +// Checks that lease6-del(subnet-id, iaid, identifier-type, identifier) can handle +// a situation when the query finds a lease. +TEST_F(LeaseCmdsTest, Lease6DelByDUID) { + + initLeaseMgr(true, true); // (true = v6, true = create a lease) + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-del\",\n" + " \"arguments\": {" + " \"subnet-id\": 66,\n" + " \"iaid\": 42," + " \"identifier-type\": \"duid\"," + " \"identifier\": \"77:77:77:77:77:77:77:77\"\n" + " }\n" + "}"; + string exp_rsp = "IPv6 lease deleted."; + + // The status expected is success. The lease should be deleted. + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1"))); +} + +// Checks that lease4-wipe detects missing parmameter properly. +TEST_F(LeaseCmdsTest, Lease4WipeMissingParam) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease4-wipe\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease4-wipe can remove leases. +TEST_F(LeaseCmdsTest, Lease4Wipe) { + + // Initialize lease manager (false = v4, true = add a lease) + initLeaseMgr(false, true); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease4-wipe\",\n" + " \"arguments\": {" + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "Deleted 1 IPv4 lease(s)."; + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1"))); +} + +// Checks that lease4-wipe properly reports when no leases were deleted. +TEST_F(LeaseCmdsTest, Lease4WipeNoLeases) { + + // Initialize lease manager (false = v4, false = no leases) + initLeaseMgr(false, false); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease4-wipe\",\n" + " \"arguments\": {" + " \"subnet-id\": 44" + " }\n" + "}"; + string exp_rsp = "Deleted 0 IPv4 lease(s)."; + testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +// Checks that lease4-wipe detects missing parmameter properly. +TEST_F(LeaseCmdsTest, Lease6WipeMissingParam) { + + // Initialize lease manager (true = v6, true = add a lease) + initLeaseMgr(true, true); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease6-wipe\",\n" + " \"arguments\": {" + " }\n" + "}"; + string exp_rsp = "missing parameter 'subnet-id' (<string>:3:19)"; + testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp); +} + +// Checks that lease4-wipe can remove leases. +TEST_F(LeaseCmdsTest, Lease6Wipe) { + + initLeaseMgr(true, true); // (true = v6, true = create a lease) + + // Now send the command. + string cmd = + "{\n" + " \"command\": \"lease6-wipe\",\n" + " \"arguments\": {" + " \"subnet-id\": 66\n" + " }\n" + "}"; + string exp_rsp = "Deleted 1 IPv6 lease(s)."; + + // The status expected is success. The lease should be deleted. + testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp); + + // Make sure the lease is really gone. + EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8::1"))); +} + +// Checks that lease4-wipe properly reports when no leases were deleted. +TEST_F(LeaseCmdsTest, Lease6WipeNoLeases) { + + // Initialize lease manager (false = v4, false = no leases) + initLeaseMgr(true, false); + + // Query for valid, existing lease. + string cmd = + "{\n" + " \"command\": \"lease6-wipe\",\n" + " \"arguments\": {" + " \"subnet-id\": 66" + " }\n" + "}"; + string exp_rsp = "Deleted 0 IPv6 lease(s)."; + testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp); +} + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc new file mode 100644 index 0000000000..600d522379 --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc @@ -0,0 +1,17 @@ +// Copyright (C) 2017 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 <log/logger_support.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/hooks/dhcp/lease_cmds/version.cc b/src/hooks/dhcp/lease_cmds/version.cc new file mode 100644 index 0000000000..d3f705769f --- /dev/null +++ b/src/hooks/dhcp/lease_cmds/version.cc @@ -0,0 +1,17 @@ +// Copyright (C) 2017 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 <hooks/hooks.h> + +extern "C" { + +/// @brief returns Kea hooks version. +int version() { + return (KEA_HOOKS_VERSION); +} + +} diff --git a/src/hooks/dhcp/user_chk/pkt_send_co.cc b/src/hooks/dhcp/user_chk/pkt_send_co.cc index c36c83cf1f..22164c049c 100644 --- a/src/hooks/dhcp/user_chk/pkt_send_co.cc +++ b/src/hooks/dhcp/user_chk/pkt_send_co.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2015,2017 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 @@ -259,7 +259,7 @@ void add4Option(Pkt4Ptr& response, uint8_t opt_code, std::string& opt_value) { /// - DOCSIS3_V6_CONFIG_FILE from user property "bootfile" /// - DOCSIS3_V6_TFTP_SERVERS from user property "tftp_server" /// -/// @param response IPv5 reponse packet +/// @param response IPv5 response packet /// @param user User from whom properties are sourced void add6Options(Pkt6Ptr& response, const UserPtr& user) { if (!user) { diff --git a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc index cdc25cb947..da8e0c8d30 100644 --- a/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc +++ b/src/hooks/dhcp/user_chk/tests/user_file_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2017 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 @@ -41,11 +41,11 @@ TEST(UserFile, construction) { TEST(UserFile, openFile) { UserFilePtr user_file; - // Construct a user file that refers to a non existant file. + // Construct a user file that refers to a non existing file. ASSERT_NO_THROW(user_file.reset(new UserFile("NoSuchFile"))); EXPECT_FALSE(user_file->isOpen()); - // Verify a non-existant file fails to open + // Verify a non-existing file fails to open ASSERT_THROW(user_file->open(), UserFileError); EXPECT_FALSE(user_file->isOpen()); @@ -62,7 +62,7 @@ TEST(UserFile, openFile) { // Verify that we cannot open an already open file. ASSERT_THROW(user_file->open(), UserFileError); - // Verifyt we can close it. + // Verify we can close it. ASSERT_NO_THROW(user_file->close()); EXPECT_FALSE(user_file->isOpen()); @@ -75,7 +75,7 @@ TEST(UserFile, openFile) { /// @brief Tests makeUser with invalid user strings TEST(UserFile, makeUser) { const char* invalid_strs[]= { - // Missinge type element. + // Missing type element. "{ \"id\" : \"01AC00F03344\" }", // Invalid id type string value. "{ \"type\" : \"BOGUS\", \"id\" : \"01AC00F03344\"}", diff --git a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc index 9eb83d0f39..c2660ab6d4 100644 --- a/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc +++ b/src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2017 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 @@ -61,7 +61,7 @@ TEST(UserRegistry, userBasics) { EXPECT_TRUE(found_user); EXPECT_EQ(found_user->getUserId(), *id); - // Verify that searching for a non-existant user returns empty user pointer. + // Verify that searching for a non-existing user returns empty user pointer. UserIdPtr id2; ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, "02020202"))); ASSERT_NO_THROW(found_user = reg->findUser(*id2)); diff --git a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc index 45702da97c..27b8b85cc5 100644 --- a/src/hooks/dhcp/user_chk/tests/userid_unittests.cc +++ b/src/hooks/dhcp/user_chk/tests/userid_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2017 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 @@ -50,21 +50,21 @@ TEST(UserIdTest, hwAddress_type) { EXPECT_EQ(id->getType(), UserId::HW_ADDRESS); EXPECT_TRUE(bytes == id->getId()); - // Check relational oeprators when a == b. + // Check relational operators when a == b. UserIdPtr id2; ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, id->toText()))); EXPECT_TRUE(*id == *id2); EXPECT_FALSE(*id != *id2); EXPECT_FALSE(*id < *id2); - // Check relational oeprators when a < b. + // Check relational operators when a < b. ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, "01FF02AC030B0709"))); EXPECT_FALSE(*id == *id2); EXPECT_TRUE(*id != *id2); EXPECT_TRUE(*id < *id2); - // Check relational oeprators when a > b. + // Check relational operators when a > b. ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, "01FF02AC030B0707"))); EXPECT_FALSE(*id == *id2); @@ -98,20 +98,20 @@ TEST(UserIdTest, duid_type) { EXPECT_EQ(id->getType(), UserId::DUID); EXPECT_TRUE(bytes == id->getId()); - // Check relational oeprators when a == b. + // Check relational operators when a == b. UserIdPtr id2; ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, id->toText()))); EXPECT_TRUE(*id == *id2); EXPECT_FALSE(*id != *id2); EXPECT_FALSE(*id < *id2); - // Check relational oeprators when a < b. + // Check relational operators when a < b. ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0709"))); EXPECT_FALSE(*id == *id2); EXPECT_TRUE(*id != *id2); EXPECT_TRUE(*id < *id2); - // Check relational oeprators when a > b. + // Check relational operators when a > b. ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0707"))); EXPECT_FALSE(*id == *id2); EXPECT_TRUE(*id != *id2); diff --git a/src/hooks/dhcp/user_chk/user.cc b/src/hooks/dhcp/user_chk/user.cc index 776c8fedc9..64f99fc0f1 100644 --- a/src/hooks/dhcp/user_chk/user.cc +++ b/src/hooks/dhcp/user_chk/user.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2015,2017 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 @@ -38,7 +38,7 @@ UserId::UserId(UserIdType id_type, const std::string & id_str) : // Input is expected to be 2-digits per bytes, no delimiters. std::vector<uint8_t> addr_bytes; - // Strip out colon delimeters, decodeHex doesn't like them. + // Strip out colon delimiters, decodeHex doesn't like them. std::string clean_id_str = id_str; std::string::iterator end_pos = std::remove(clean_id_str.begin(), clean_id_str.end(), ':'); diff --git a/src/hooks/dhcp/user_chk/user_file.h b/src/hooks/dhcp/user_chk/user_file.h index 88b68cf8f8..513e95acd0 100644 --- a/src/hooks/dhcp/user_chk/user_file.h +++ b/src/hooks/dhcp/user_chk/user_file.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2015,2017 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 @@ -43,7 +43,7 @@ public: /// /// Each entry must have a valid entry for "type" and a valid entry or "id". /// -/// If an entry contains duplicate option names, that option will be assigend +/// If an entry contains duplicate option names, that option will be assigned /// the last value found. This is typical JSON behavior. /// Currently, only string option values (i.e. enclosed in quotes) are /// supported. diff --git a/src/hooks/dhcp/user_chk/user_registry.cc b/src/hooks/dhcp/user_chk/user_registry.cc index f4acbf0231..c232253b71 100644 --- a/src/hooks/dhcp/user_chk/user_registry.cc +++ b/src/hooks/dhcp/user_chk/user_registry.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2015,2017 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 @@ -85,7 +85,7 @@ void UserRegistry::refresh() { addUser(user); } } catch (const std::exception& ex) { - // Source was compromsised so restore registry from backup. + // Source was compromised so restore registry from backup. users_ = backup; // Close the source. source_->close(); |