Commit a2924fcc authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[5272] lease_cmds in progress

parent 63e8df70
......@@ -1630,6 +1630,8 @@ AC_CONFIG_FILES([Makefile
src/bin/shell/tests/shell_unittest.py
src/hooks/Makefile
src/hooks/dhcp/Makefile
src/hooks/dhcp/lease_cmds/Makefile
src/hooks/dhcp/lease_cmds/tests/Makefile
src/hooks/dhcp/user_chk/Makefile
src/hooks/dhcp/user_chk/tests/Makefile
src/hooks/dhcp/user_chk/tests/test_data_files_config.h
......
SUBDIRS = user_chk
SUBDIRS = user_chk lease_cmds
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/cc/libkea-cc.la
libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.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
EXTRA_DIST += lease_cmds.dox
// 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:
/// - subnet-id, address
/// - subnet-id, identifier type, identifier value
///
/// 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 or IPv6 address (used when query_by_addr is true)
IOAddress addr;
/// @brief Specifies identifier type (usually FLEX_ID, used when
/// query_by_addr is false)
HWAddrPtr hwaddr;
/// @brief Specifies identifier value (used when query_by_addr is false)
isc::dhcp::DuidPtr duid;
/// @brief specifies parameter types (true = query by address, false =
/// query by indetifier-type,identifier)
Type query_type;
/// @brief Default contstructor.
Parameters()
:addr("::"), query_type(TYPE_ADDR) {
}
};
private:
/// @brief Registers commands:
///
/// Registers:
/// - 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 CommandMgr is not available (should not happen)
void registerCommands();
/// @brief Dergisters commands:
///
/// Deregisters:
/// - 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 CommandMgr is not available (should not happen)
void deregisterCommands();
/// @brief reservation-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 v4 lease only.
///
/// Example command
/// {
/// "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": 1499282530,
/// "subnet-id": 1,
/// "fdqn_fwd": true,
/// "fqdn_rev": true,
/// "hostname": "myhost.example.org",
/// "state": 0
/// }
/// }
///
/// @param command should be 'reservation-add' (but it's ignored)
/// @param args must contain host reservation definition.
/// @return result of the operation
static ConstElementPtr
leaseAddHandler(const string& command, ConstElementPtr args);
/// @brief reservation-get command handler
///
/// This command attempts to retrieve a host that match selected criteria.
/// Two types of parameters are supported: (subnet-id, address) or
/// (subnet-id, identifier-type, identifier).
///
/// Example command for query by (subnet-id, address):
/// {
/// "command": "reservation-get",
/// "arguments": {
/// "subnet-id": 1,
/// "ip-address": "192.0.2.202"
/// }
/// }
///
/// Example command for query by (subnet-id, identifier-type, identifier)
/// {
/// "command": "reservation-get",
/// "arguments": {
/// "subnet-id": 1,
/// "identifier-type": "hw-address",
/// "identifier": "00:01:02:03:04:05"
/// }
/// }";
/// @param command should be 'reservation-get' (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
leaseGetHandler(const string& command, ConstElementPtr args);
/// @brief reservation-del command handler
///
/// This command attempts to delete a host that match selected criteria.
/// Two types of parameters are supported: (subnet-id, address) or
/// (subnet-id, identifier-type, identifier).
///
/// Note: for this operation to work, hosts-database must be specified
/// in your configuration file (or from code point of view, alternate_source_
/// must be set in HostMgr).
///
/// Example command for query by (subnet-id, address):
/// {
/// "command": "reservation-get",
/// "arguments": {
/// "subnet-id": 1,
/// "ip-address": "192.0.2.202"
/// }
/// }
///
/// Example command for query by (subnet-id, identifier-type, identifier)
/// {
/// "command": "reservation-get",
/// "arguments": {
/// "subnet-id": 1,
/// "identifier-type": "hw-address",
/// "identifier": "00:01:02:03:04:05"
/// }
/// }";
/// @param command should be 'reservation-add' (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
leaseDelHandler(const string& command, ConstElementPtr args);
static ConstElementPtr
leaseUpdateHandler(const string& command, ConstElementPtr args);
static ConstElementPtr
leaseWipeHandler(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 args - arguments passed to command
/// @return parsed parameters
/// @throw BadValue if input arguments don't make sense.
static Parameters getParameters(const ConstElementPtr& args);
/// @brief Covenience pointer used to access database storage
static HostDataSourcePtr db_storage_;
/// @brief Protocol family (IPv4 or IPv6)
static uint16_t family_;
};
LeaseCmdsImpl::LeaseCmdsImpl() {
family_ = CfgMgr::instance().getFamily();
registerCommands();
}
LeaseCmdsImpl::~LeaseCmdsImpl() {
deregisterCommands();
}
void LeaseCmdsImpl::registerCommands() {
/// @todo: Use registration mechanism once #5321 discussion is done
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::leaseDelHandler, _1, _2));
CommandMgr::instance().registerCommand("lease6-del",
boost::bind(&LeaseCmdsImpl::leaseDelHandler, _1, _2));
CommandMgr::instance().registerCommand("lease4-update",
boost::bind(&LeaseCmdsImpl::leaseUpdateHandler, _1, _2));
CommandMgr::instance().registerCommand("lease6-update",
boost::bind(&LeaseCmdsImpl::leaseUpdateHandler, _1, _2));
CommandMgr::instance().registerCommand("lease4-del-all",
boost::bind(&LeaseCmdsImpl::leaseWipeHandler, _1, _2));
CommandMgr::instance().registerCommand("lease6-del-all",
boost::bind(&LeaseCmdsImpl::leaseWipeHandler, _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-del-all");
CommandMgr::instance().deregisterCommand("lease6-del-all");
}
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");
}
ConstElementPtr lease_data = params->get("lease");
if (!lease_data) {
isc_throw(isc::BadValue, "'lease' parameters must be specified");
}
Lease4Ptr lease4;
Lease6Ptr lease6;
if (v4) {
LeaseDataParser4 parser;
lease4 = parser.parse(lease_data);
checkLeaseIntegrity(lease4);
if (lease4) {
LeaseMgrFactory::instance().add(lease4);
}
} else {
LeaseDataParser6 parser;
lease6 = parser.parse(lease_data);
checkLeaseIntegrity(lease6);
if (lease6) {
LeaseMgrFactory::instance().add(lease6);
}
}
} catch (const std::exception& ex) {
LOG_ERROR(lease_cmds_logger, LEASE_CMDS_RESERV_ADD_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(const ConstElementPtr& params) {
Parameters x;
if (!params || params->getType() != Element::map) {
isc_throw(BadValue, "Parameters missing or are not a map.");
}
// We support 2 sets of parameters for lease-get/lease-del:
// lease-get(subnet-id, address)
// lease-get(subnet-id, interifier-type, identifier)
ConstElementPtr 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();
tmp = params->get("ip-address");
ConstLeasePtr host;
if (tmp) {
if (tmp->getType() != Element::string) {
isc_throw(BadValue, "'ip-address' is not a string.");
}
x.addr = IOAddress(tmp->stringValue());
x.query_by_addr = true;
return (x);
}
// 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 parse the identifier value first.
try {
x.ident = util::str::quotedStringToBinary(ident->stringValue());
if (x.ident.empty()) {
util::str::decodeFormattedHexString(ident->stringValue(),
x.ident);
}
} catch (...) {
// The string doesn't match any known pattern, so we have to
// report an error at this point.
isc_throw(BadValue, "Unable to parse 'identifier' value.");
}
if (x.ident.empty()) {
isc_throw(BadValue, "Unable to query for empty 'identifier'.");
}
// Next, try to convert identifier-type
try {
x.type = Lease::getIdentifierType(type->stringValue());
} catch (const std::exception& ex) {
isc_throw(BadValue, "Value of 'identifier-type' was not recognized.");
}
x.query_by_addr = false;
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(params);
switch (p.query_type) {
case TYPE_ADDR: {
// Query by address
if (v4) {
lease4 = LeaseMgr::instance().getLease4(p.subnet_id, p.addr);
} else {
lease6 = LeaseMgr::instance().getLease6(p.subnet_id, p.addr);
}
break;
}
case TYPE_HWADDR:
if (v4) {
lease4 = LeaseMgr::instance().getLease4(p.subnet_id, p.hwaddr);
} else {
return (createAnswer(CONTROL_RESULT_ERROR,
"Query by hw-address is not allowed in v6."));
}
case TYPE_DUID:
if (v6) {
lease6 = LeaseMgr::instance().getLease6(p.subnet_id, p.duid);
} else {
return (createAnswer(CONTROL_RESULT_ERROR,
"Query by duid is not allowed in v4."));
}
default:
return (createAnswer(CONTROL_RESULT_ERROR,
"Unknown query type: " << static_cast<int>(p.query_type)));
}
} catch (const std::exception& ex) {
return (createAnswer(CONTROL_RESULT_ERROR,
"Failure during leaseX-get: " << ex.what()));
}
ElementPtr lease_json;
if (v4 && lease4) {
lease_json = lease4->toElement4();
}
if (!v4 && lease6) {
lease_json = lease6->toElement6();
}
if (lease) {
return (createAnswer(CONTROL_RESULT_SUCCESS, "Lease found.", lease_json));
} else {
return (createAnswer(CONTROL_RESULT_SUCCESS, "Lease not found."));
}
}
ConstElementPtr
LeaseCmdsImpl::reservationDelHandler(const std::string& /*name*/,
ConstElementPtr params) {
Parameters p;
bool deleted;
try {
p = getParameters(params);
if (p.query_by_addr) {
// try to delete by address
deleted = LeaseMgr::instance().del(p.subnet_id, p.addr);
} else {
// try to delete by identifier
if (family_ == AF_INET) {
deleted = LeaseMgr::instance().del4(p.subnet_id, p.type,
&p.ident[0], p.ident.size());
} else {
deleted = LeaseMgr::instance().del6(p.subnet_id, p.type,
&p.ident[0], p.ident.size());
}
}
} catch (const std::exception& ex) {
return (createAnswer(CONTROL_RESULT_ERROR, ex.what()));
}
if (deleted) {
return (createAnswer(CONTROL_RESULT_SUCCESS, "Lease deleted."));
} else {
return (createAnswer(CONTROL_RESULT_ERROR,
"Lease not deleted (not found)."));
}
}
uint16_t LeaseCmdsImpl::family_ = AF_INET;
LeaseDataSourcePtr LeaseCmdsImpl::db_storage_;
LeaseCmds::LeaseCmds()
:impl_(new LeaseCmdsImpl()) {
}
LeaseCmds::~LeaseCmds() {
impl_.reset();
}
};
};
// 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