From: Francis Dupont Date: Tue, 1 Oct 2019 15:37:21 +0000 (+0200) Subject: [219-allow-an-option-value-to-be-set-from-an-expression] Checkpoint: wrote flex optio... X-Git-Tag: Kea-1.7.1~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=248b058afcedd8001c5fee6eaa5c2b1725afb5d6;p=thirdparty%2Fkea.git [219-allow-an-option-value-to-be-set-from-an-expression] Checkpoint: wrote flex option code, to do tests, to finish doc --- diff --git a/configure.ac b/configure.ac index 420fecd1f5..ff3d33f705 100755 --- a/configure.ac +++ b/configure.ac @@ -1694,6 +1694,8 @@ AC_CONFIG_FILES([Makefile src/bin/shell/tests/shell_unittest.py src/hooks/Makefile src/hooks/dhcp/Makefile + src/hooks/dhcp/flex_option/Makefile + src/hooks/dhcp/flex_option/tests/Makefile src/hooks/dhcp/high_availability/Makefile src/hooks/dhcp/high_availability/tests/Makefile src/hooks/dhcp/lease_cmds/Makefile diff --git a/src/hooks/dhcp/Makefile.am b/src/hooks/dhcp/Makefile.am index 0bb9b878e3..334e3bbfa0 100644 --- a/src/hooks/dhcp/Makefile.am +++ b/src/hooks/dhcp/Makefile.am @@ -4,4 +4,4 @@ if HAVE_MYSQL SUBDIRS += mysql_cb endif -SUBDIRS += stat_cmds user_chk +SUBDIRS += stat_cmds flex_option user_chk diff --git a/src/hooks/dhcp/flex_option/.gitignore b/src/hooks/dhcp/flex_option/.gitignore new file mode 100644 index 0000000000..35b5e99aee --- /dev/null +++ b/src/hooks/dhcp/flex_option/.gitignore @@ -0,0 +1 @@ +/html diff --git a/src/hooks/dhcp/flex_option/Makefile.am b/src/hooks/dhcp/flex_option/Makefile.am new file mode 100644 index 0000000000..8917366118 --- /dev/null +++ b/src/hooks/dhcp/flex_option/Makefile.am @@ -0,0 +1,82 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file and doxygen file is included in the distribution +EXTRA_DIST = flex_option_messages.mes +EXTRA_DIST += flex_option.dox + +CLEANFILES = *.gcno *.gcda + +# convenience archive + +noinst_LTLIBRARIES = libflex_option.la + +libflex_option_la_SOURCES = flex_option.cc flex_option.h +libflex_option_la_SOURCES += flex_option_callouts.cc +libflex_option_la_SOURCES += flex_option_log.cc flex_option_log.h +libflex_option_la_SOURCES += flex_option_messages.cc flex_option_messages.h +libflex_option_la_SOURCES += version.cc + +libflex_option_la_CXXFLAGS = $(AM_CXXFLAGS) +libflex_option_la_CPPFLAGS = $(AM_CPPFLAGS) + +# install the shared object into $(libdir)/kea/hooks +lib_hooksdir = $(libdir)/kea/hooks +lib_hooks_LTLIBRARIES = libdhcp_flex_option.la + +libdhcp_flex_option_la_SOURCES = +libdhcp_flex_option_la_LDFLAGS = $(AM_LDFLAGS) +libdhcp_flex_option_la_LDFLAGS += -avoid-version -export-dynamic -module +libdhcp_flex_option_la_LIBADD = libflex_option.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libdhcp_flex_option_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libdhcp_flex_option_la_LIBADD += $(LOG4CPLUS_LIBS) +libdhcp_flex_option_la_LIBADD += $(CRYPTO_LIBS) +libdhcp_flex_option_la_LIBADD += $(BOOST_LIBS) + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f flex_option_messages.h flex_option_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: flex_option_messages.h flex_option_messages.cc + @echo Message files regenerated + +flex_option_messages.h flex_option_messages.cc: flex_option_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes + +else + +messages flex_option_messages.h flex_option_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + diff --git a/src/hooks/dhcp/flex_option/flex_option.cc b/src/hooks/dhcp/flex_option/flex_option.cc new file mode 100644 index 0000000000..d36cf48052 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option.cc @@ -0,0 +1,223 @@ +// Copyright (C) 2019 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::eval; +using namespace std; + +namespace isc { +namespace flex_option { + +FlexOptionImpl::OptionConfig::OptionConfig(uint16_t code) : code_(code) { +} + +FlexOptionImpl::OptionConfig::~OptionConfig() { +} + +FlexOptionImpl::FlexOptionImpl() { +} + +FlexOptionImpl::~FlexOptionImpl() { + option_config_map_.clear(); +} + +void +FlexOptionImpl::configure(ConstElementPtr options) { + if (!options) { + isc_throw(BadValue, "'options' parameter is mandatory"); + } + if (options->getType() != Element::list) { + isc_throw(BadValue, "'options' parameter must be a list"); + } + if (options->empty()) { + return; + } + for (auto option : options->listValue()) { + parseOptionConfig(option); + } +} + +void +FlexOptionImpl::parseOptionConfig(ConstElementPtr option) { + uint16_t family = CfgMgr::instance().getFamily(); + if (!option) { + isc_throw(BadValue, "null option element"); + } + if (option->getType() != Element::map) { + isc_throw(BadValue, "option element is not a map"); + } + ConstElementPtr code_elem = option->get("code"); + ConstElementPtr name_elem = option->get("name"); + if (!code_elem && !name_elem) { + isc_throw(BadValue, "'code' or 'name' must be specified: " + << option->str()); + } + uint16_t code; + if (code_elem) { + if (code_elem->getType() != Element::integer) { + isc_throw(BadValue, "'code' must be an integer: " + << code_elem->str()); + } + int64_t value = code_elem->intValue(); + int64_t max_code; + if (family == AF_INET) { + max_code = numeric_limits::max(); + } else { + max_code = numeric_limits::max(); + } + if ((value < 0) || (value > max_code)) { + isc_throw(OutOfRange, "invalid 'code' value " << value + << " not in [0.." << max_code << "]"); + } + if (family == AF_INET) { + if (value == DHO_PAD) { + isc_throw(BadValue, + "invalid 'code' value 0: reserved for PAD"); + } else if (value == DHO_END) { + isc_throw(BadValue, + "invalid 'code' value 255: reserved for END"); + } + } else { + if (value == 0) { + isc_throw(BadValue, "invalid 'code' value 0: reserved"); + } + } + code = static_cast(value); + } + if (name_elem) { + if (name_elem->getType() != Element::string) { + isc_throw(BadValue, "'name' must be a string: " + << name_elem->str()); + } + string name = name_elem->stringValue(); + if (name.empty()) { + isc_throw(BadValue, "'name' must not be empty"); + } + string space; + if (family == AF_INET) { + space = DHCP4_OPTION_SPACE; + } else { + space = DHCP6_OPTION_SPACE; + } + OptionDefinitionPtr def = LibDHCP::getOptionDef(space, name); + if (!def) { + def = LibDHCP::getRuntimeOptionDef(space, name); + } + if (!def) { + def = LibDHCP::getLastResortOptionDef(space, name); + } + if (!def) { + isc_throw(BadValue, "no known '" << name << "' option in '" + << space << "' space"); + } + if (code_elem && (def->getCode() != code)) { + isc_throw(BadValue, "option '" << name << "' has code " + << def->getCode() << " but 'code' is " << code); + } + code = def->getCode(); + } + + if (option_config_map_.count(code)) { + isc_throw(BadValue, "option " << code << " was already specified"); + } + + Option::Universe universe; + if (family == AF_INET) { + universe = Option::V4; + } else { + universe = Option::V6; + } + + OptionConfigPtr opt_cfg(new OptionConfig(code)); + ConstElementPtr add_elem = option->get("add"); + if (add_elem) { + if (add_elem->getType() != Element::string) { + isc_throw(BadValue, "'add' must be a string: " + << add_elem->str()); + } + string add = add_elem->stringValue(); + if (add.empty()) { + isc_throw(BadValue, "'add' must not be empty"); + } + opt_cfg->setAction(ADD); + opt_cfg->setText(add); + try { + EvalContext eval_ctx(universe); + eval_ctx.parseString(add, EvalContext::PARSER_STRING); + ExpressionPtr expr(new Expression(eval_ctx.expression)); + opt_cfg->setExpr(expr); + } catch (const std::exception& ex) { + isc_throw(BadValue, "can't parse add expression [" + << add << "] error: " << ex.what()); + } + } + ConstElementPtr supersede_elem = option->get("supersede"); + if (supersede_elem) { + if (supersede_elem->getType() != Element::string) { + isc_throw(BadValue, "'supersede' must be a string: " + << supersede_elem->str()); + } + string supersede = supersede_elem->stringValue(); + if (supersede.empty()) { + isc_throw(BadValue, "'supersede' must not be empty"); + } + if (opt_cfg->getAction() != NONE) { + isc_throw(BadValue, "multiple actions: " << option->str()); + } + opt_cfg->setAction(SUPERSEDE); + opt_cfg->setText(supersede); + try { + EvalContext eval_ctx(universe); + eval_ctx.parseString(supersede, EvalContext::PARSER_STRING); + ExpressionPtr expr(new Expression(eval_ctx.expression)); + opt_cfg->setExpr(expr); + } catch (const std::exception& ex) { + isc_throw(BadValue, "can't parse supersede expression [" + << supersede << "] error: " << ex.what()); + } + } + ConstElementPtr remove_elem = option->get("remove"); + if (remove_elem) { + if (remove_elem->getType() != Element::string) { + isc_throw(BadValue, "'remove' must be a string: " + << remove_elem->str()); + } + string remove = remove_elem->stringValue(); + if (remove.empty()) { + isc_throw(BadValue, "'remove' must not be empty"); + } + if (opt_cfg->getAction() != NONE) { + isc_throw(BadValue, "multiple actions: " << option->str()); + } + opt_cfg->setAction(REMOVE); + opt_cfg->setText(remove); + try { + EvalContext eval_ctx(universe); + eval_ctx.parseString(remove, EvalContext::PARSER_BOOL); + ExpressionPtr expr(new Expression(eval_ctx.expression)); + opt_cfg->setExpr(expr); + } catch (const std::exception& ex) { + isc_throw(BadValue, "can't parse remove expression [" + << remove << "] error: " << ex.what()); + } + } +} + +} // end of namespace flex_option +} // end of namespace isc diff --git a/src/hooks/dhcp/flex_option/flex_option.dox b/src/hooks/dhcp/flex_option/flex_option.dox new file mode 100644 index 0000000000..22691c56d4 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option.dox @@ -0,0 +1,32 @@ +// Copyright (C) 2019 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/. + +/** + +@page libdhcp_flex_option Kea Flexible Option Hooks Library + +@section libdhcp_flex_optionIntro Introduction + +Welcome to Kea Flexible Option Hooks Library. This documentation is +addressed to developers who are interested in the internal operation +of the Flexible Option library. This file provides information needed +to understand and perhaps extend this library. + +This documentation is stand-alone: you should have read and understood +the Kea +Developer's Guide and in particular its section about hooks. + +@section flex_option Flexible Option Overview + +Flexible Option (or flex_option) is a Hook library that can be loaded by +either kea-dhcp4 and kea-dhcp6 servers to extend them with additional +option value setting mechanisms. + +@section flex_optionCode Flexible Option Code Overview + +Todo + +*/ diff --git a/src/hooks/dhcp/flex_option/flex_option.h b/src/hooks/dhcp/flex_option/flex_option.h new file mode 100644 index 0000000000..ffb4a62fd0 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option.h @@ -0,0 +1,214 @@ +// Copyright (C) 2019 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 FLEX_OPTION_H +#define FLEX_OPTION_H + +#include +#include +#include +#include +#include + +namespace isc { +namespace flex_option { + +/// @brief Flex Option implementation. +class FlexOptionImpl { +public: + + /// @brief Action. + /// + /// Currently supported actions are: + /// - add (if not already existing) + /// - supersede (as add but also when already existing) + /// - remove + enum Action { + NONE, + ADD, + SUPERSEDE, + REMOVE + }; + + /// @brief Option configuration. + /// + /// Per option configuration. + class OptionConfig { + public: + /// @brief Constructor. + /// + /// @param option code. + OptionConfig(uint16_t code); + + /// @brief Destructor. + virtual ~OptionConfig(); + + /// @brief Return option code. + /// + /// @return option code. + uint16_t getCode() const { + return (code_); + } + + /// @brief Set action. + /// + /// @param action the action. + void setAction(Action action) { + action_ = action; + } + + /// @brief Return action. + /// + /// @return action. + Action getAction() const { + return (action_); + } + + /// @brief Set textual expression. + /// + /// @param text the textual expression. + void setText(const std::string& text) { + text_ = text; + }; + + /// @brief Get textual expression. + /// + /// @return textual expression. + const std::string& getText() const { + return (text_); + } + + /// @brief Set match expression. + /// + /// @param expr the match expression. + void setExpr(const isc::dhcp::ExpressionPtr& expr) { + expr_ = expr; + } + + /// @brief Get match expression. + /// + /// @return the match expression. + const isc::dhcp::ExpressionPtr& getExpr() const { + return (expr_); + } + + private: + /// @brief The code. + uint16_t code_; + + /// @brief The action. + Action action_; + + /// @brief The textual expression. + std::string text_; + + /// @brief The match expression. + isc::dhcp::ExpressionPtr expr_; + }; + + /// @brief The type of shared pointers to option config. + typedef boost::shared_ptr OptionConfigPtr; + + /// @brief The type of the option config map. + typedef std::map OptionConfigMap; + + /// @brief Constructor. + FlexOptionImpl(); + + /// @brief Destructor. + ~FlexOptionImpl(); + + /// @brief Get the option config map. + /// + /// @return The option config map. + const OptionConfigMap& getOptionConfigMap() const { + return (option_config_map_); + } + + /// @brief Configure the Flex Option implementation. + /// + /// @param options The element with option config list. + /// @throw BadValue and similar exceptions on error. + void configure(isc::data::ConstElementPtr options); + + /// @brief Process a query / response pair. + /// + /// @tparam PktType The type of pointers to packets: Pkt4Ptr or Pkt6Ptr. + /// @param universe The option universe: Option::V4 or Option::V6. + /// @param query The query packet. + /// @param response The response packet. + template + void process(isc::dhcp::Option::Universe universe, + PktType query, PktType response) { + for (auto pair : getOptionConfigMap()) { + const OptionConfigPtr& opt_cfg = pair.second; + std::string value; + isc::dhcp::OptionBuffer buffer; + isc::dhcp::OptionPtr opt = response->getOption(opt_cfg->getCode()); + switch (opt_cfg->getAction()) { + case NONE: + break; + case ADD: + if (opt) { + break; + } + value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), *query); + if (value.empty()) { + break; + } + buffer.assign(value.begin(), value.end()); + opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(), + buffer)); + response->addOption(opt); + break; + case SUPERSEDE: + value = isc::dhcp::evaluateString(*opt_cfg->getExpr(), *query); + if (value.empty()) { + break; + } + while (opt) { + response->delOption(opt_cfg->getCode()); + opt = response->getOption(opt_cfg->getCode()); + } + buffer.assign(value.begin(), value.end()); + opt.reset(new isc::dhcp::Option(universe, opt_cfg->getCode(), + buffer)); + response->addOption(opt); + break; + case REMOVE: + if (!opt) { + break; + } + if (!isc::dhcp::evaluateBool(*opt_cfg->getExpr(), *query)) { + break; + } + while (opt) { + response->delOption(opt_cfg->getCode()); + opt = response->getOption(opt_cfg->getCode()); + } + break; + } + } + } + +private: + /// @brief The option config map (code and pointer to option config). + OptionConfigMap option_config_map_; + + /// @brief Parse an option config. + /// + /// @param option The element with option config. + /// @throw BadValue and similar exceptionson error. + void parseOptionConfig(isc::data::ConstElementPtr option); + +}; + +/// @brief The type of shared pointers to Flex Option implementations. +typedef boost::shared_ptr FlexOptionImplPtr; + +} // end of namespace flex_option +} // end of namespace isc +#endif diff --git a/src/hooks/dhcp/flex_option/flex_option_callouts.cc b/src/hooks/dhcp/flex_option/flex_option_callouts.cc new file mode 100644 index 0000000000..63626836f2 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_callouts.cc @@ -0,0 +1,115 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the End User License +// Agreement. See COPYING file in the premium/ directory. + +#include + +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace flex_option { + +FlexOptionImplPtr impl; + +} // end of namespace flex_option +} // end of namespace isc + +using namespace isc; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::hooks; +using namespace isc::flex_option; + +// Functions accessed by the hooks framework use C linkage to avoid the name +// mangling that accompanies use of the C++ compiler as well as to avoid +// issues related to namespaces. +extern "C" { + +/// @brief This callout is called at the "pkt4_send" hook. +/// +/// It retrieves v4 query and response packets, and then adds, supersedes +/// or removes option values in the response according to expressions +/// evaluated on the query. +/// +/// @param handle CalloutHandle. +/// +/// @return 0 upon success, non-zero otherwise +int pkt4_send(CalloutHandle& handle) { + // Get the parameters. + Pkt4Ptr query; + Pkt4Ptr response; + handle.getArgument("query4", query); + handle.getArgument("response4", response); + + try { + impl->process(Option::V4, query, response); + } catch (const std::exception& ex) { + LOG_ERROR(flex_option_logger, FLEX_OPTION_PROCESS_ERROR) + .arg(query->getLabel()) + .arg(ex.what()); + } + + return (0); +} + +/// @brief This callout is called at the "pkt6_send" hook. +/// +/// It retrieves v6 query and response packets, and then adds, supersedes +/// or removes option values in the response according to expressions +/// evaluated on the query. +/// +/// @param handle CalloutHandle. +/// +/// @return 0 upon success, non-zero otherwise +int pkt6_send(CalloutHandle& handle) { + // Get the parameters. + Pkt6Ptr query; + Pkt6Ptr response; + handle.getArgument("query6", query); + handle.getArgument("response6", response); + + try { + impl->process(Option::V6, query, response); + } catch (const std::exception& ex) { + LOG_ERROR(flex_option_logger, FLEX_OPTION_PROCESS_ERROR) + .arg(query->getLabel()) + .arg(ex.what()); + } + + return (0); +} + +/// @brief This function is called when the library is loaded. +/// +/// @param handle library handle +/// @return 0 when initialization is successful, 1 otherwise +int load(LibraryHandle& handle) { + try { + impl.reset(new FlexOptionImpl()); + ConstElementPtr options = handle.getParameter("options"); + impl->configure(options); + } catch (const std::exception& ex) { + LOG_ERROR(flex_option_logger, FLEX_OPTION_LOAD_ERROR) + .arg(ex.what()); + return (1); + } + + return (0); +} + +/// @brief This function is called when the library is unloaded. +/// +/// @return always 0. +int unload() { + impl.reset(); + LOG_INFO(flex_option_logger, FLEX_OPTION_UNLOAD); + return (0); +} + +} // end extern "C" diff --git a/src/hooks/dhcp/flex_option/flex_option_log.cc b/src/hooks/dhcp/flex_option/flex_option_log.cc new file mode 100644 index 0000000000..6ff0106747 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_log.cc @@ -0,0 +1,17 @@ +// Copyright (C) 2019 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 + +#include + +namespace isc { +namespace flex_option { + +isc::log::Logger flex_option_logger("flex-option-hooks"); + +} // namespace flex_option +} // namespace isc diff --git a/src/hooks/dhcp/flex_option/flex_option_log.h b/src/hooks/dhcp/flex_option/flex_option_log.h new file mode 100644 index 0000000000..9fc3361b67 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_log.h @@ -0,0 +1,21 @@ +// Copyright (C) 2019 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 FLEX_OPTION_LOG_H +#define FLEX_OPTION_LOG_H + +#include +#include +#include + +namespace isc { +namespace flex_option { + +extern isc::log::Logger flex_option_logger; + +} // end of namespace flex_option +} // end of namespace isc +#endif diff --git a/src/hooks/dhcp/flex_option/flex_option_messages.cc b/src/hooks/dhcp/flex_option/flex_option_messages.cc new file mode 100644 index 0000000000..3dcb13015c --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_messages.cc @@ -0,0 +1,23 @@ +// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes on Tue Oct 01 2019 14:08 + +#include +#include +#include + +extern const isc::log::MessageID FLEX_OPTION_LOAD_ERROR = "FLEX_OPTION_LOAD_ERROR"; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_ERROR = "FLEX_OPTION_PROCESS_ERROR"; +extern const isc::log::MessageID FLEX_OPTION_UNLOAD = "FLEX_OPTION_UNLOAD"; + +namespace { + +const char* values[] = { + "FLEX_OPTION_LOAD_ERROR", "loading Flex Option hooks library failed: %1", + "FLEX_OPTION_PROCESS_ERROR", "An error occurred processing query %1: %2", + "FLEX_OPTION_UNLOAD", "Flex Option hooks library has been unloaded", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/hooks/dhcp/flex_option/flex_option_messages.h b/src/hooks/dhcp/flex_option/flex_option_messages.h new file mode 100644 index 0000000000..224936e03e --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_messages.h @@ -0,0 +1,12 @@ +// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes on Tue Oct 01 2019 14:08 + +#ifndef FLEX_OPTION_MESSAGES_H +#define FLEX_OPTION_MESSAGES_H + +#include + +extern const isc::log::MessageID FLEX_OPTION_LOAD_ERROR; +extern const isc::log::MessageID FLEX_OPTION_PROCESS_ERROR; +extern const isc::log::MessageID FLEX_OPTION_UNLOAD; + +#endif // FLEX_OPTION_MESSAGES_H diff --git a/src/hooks/dhcp/flex_option/flex_option_messages.mes b/src/hooks/dhcp/flex_option/flex_option_messages.mes new file mode 100644 index 0000000000..5b3b1617e7 --- /dev/null +++ b/src/hooks/dhcp/flex_option/flex_option_messages.mes @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") + +% FLEX_OPTION_LOAD_ERROR loading Flex Option hooks library failed: %1 +This error message indicates an error during loading the Flex Option +hooks library. The details of the error are provided as argument of +the log message. + +% FLEX_OPTION_PROCESS_ERROR An error occurred processing query %1: %2 +This error message indicates an error during processing of a query +by the Flex Option hooks library. The client identification information +from the query and the details of the error are provided as arguments +of the log message. + +% FLEX_OPTION_UNLOAD Flex Option hooks library has been unloaded +This info message indicates that the Flex Option hooks library has been +unloaded. + diff --git a/src/hooks/dhcp/flex_option/tests/.gitignore b/src/hooks/dhcp/flex_option/tests/.gitignore new file mode 100644 index 0000000000..35b5e99aee --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/.gitignore @@ -0,0 +1 @@ +/html diff --git a/src/hooks/dhcp/flex_option/tests/Makefile.am b/src/hooks/dhcp/flex_option/tests/Makefile.am new file mode 100644 index 0000000000..1623fe888d --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/Makefile.am @@ -0,0 +1,55 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/flex_option -I$(top_srcdir)/src/hooks/dhcp/flex_option +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DFLEX_OPTION_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/flex_option/.libs/libdhcp_flex_option.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 += flex_option_unittests + +flex_option_unittests_SOURCES = run_unittests.cc +flex_option_unittests_SOURCES += callout_unittests.cc +flex_option_unittests_SOURCES += load_unload_unittests.cc + +flex_option_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) + +flex_option_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) + +flex_option_unittests_CXXFLAGS = $(AM_CXXFLAGS) + +flex_option_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +flex_option_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +flex_option_unittests_LDADD += $(LOG4CPLUS_LIBS) +flex_option_unittests_LDADD += $(CRYPTO_LIBS) +flex_option_unittests_LDADD += $(BOOST_LIBS) +flex_option_unittests_LDADD += $(GTEST_LDADD) +endif +noinst_PROGRAMS = $(TESTS) diff --git a/src/hooks/dhcp/flex_option/tests/callout_unittests.cc b/src/hooks/dhcp/flex_option/tests/callout_unittests.cc new file mode 100644 index 0000000000..cbb38f7526 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/callout_unittests.cc @@ -0,0 +1,40 @@ +// Copyright (C) 2019 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 This file contains tests which verify flexible option callouts. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::hooks; +using namespace isc::flex_option; + +extern "C" { +extern int pkt4_send(CalloutHandle& handle); +extern int pkt6_send(CalloutHandle& handle); +} + +namespace { + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/tests/load_unload_unittests.cc b/src/hooks/dhcp/flex_option/tests/load_unload_unittests.cc new file mode 100644 index 0000000000..f0c339dcf1 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/load_unload_unittests.cc @@ -0,0 +1,79 @@ +// Copyright (C) 2019 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 This file contains tests which exercise the load and unload +/// functions in the flexible option hook library. In order to test the load +/// function, one must be able to pass it hook library parameters. The +/// the only way to populate these parameters is by actually loading the +/// library via HooksManager::loadLibraries(). + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace hooks; +using namespace isc::data; + +namespace { + +/// @brief Test fixture for testing loading and unloading the flex-id library +class LibLoadTest : public ::testing::Test { +public: + /// @brief Constructor + LibLoadTest() { + reset(); + } + + /// @brief Destructor + /// Removes files that may be left over from previous tests + virtual ~LibLoadTest() { + reset(); + } + + /// @brief Removes files that may be left over from previous tests + virtual void reset() { + HooksManager::unloadLibraries(); + } + + void addLib(const std::string& lib, ConstElementPtr params) { + libraries_.push_back(make_pair(lib, params)); + } + + void loadLibs() { + EXPECT_TRUE(HooksManager::loadLibraries(libraries_)); + } + + void unloadLibs() { + EXPECT_NO_THROW(HooksManager::unloadLibraries()); + } + + HookLibsCollection libraries_; +}; + +// Simple test that checks the library can be loaded and unloaded several times. +TEST_F(LibLoadTest, validLoad) { + + // Prepare parameters for the callout parameters library. + ElementPtr params = Element::createMap(); + ElementPtr options = Element::createList(); + params->set("options", options); + + addLib(FLEX_OPTION_LIB_SO, params); + + loadLibs(); + unloadLibs(); + + loadLibs(); + unloadLibs(); +} + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/flex_option/tests/run_unittests.cc b/src/hooks/dhcp/flex_option/tests/run_unittests.cc new file mode 100644 index 0000000000..5805b42c51 --- /dev/null +++ b/src/hooks/dhcp/flex_option/tests/run_unittests.cc @@ -0,0 +1,19 @@ +// Copyright (C) 2019 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 + +#include +#include + +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/flex_option/version.cc b/src/hooks/dhcp/flex_option/version.cc new file mode 100644 index 0000000000..425019353b --- /dev/null +++ b/src/hooks/dhcp/flex_option/version.cc @@ -0,0 +1,17 @@ +// Copyright (C) 2019 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 +#include + +extern "C" { + +/// @brief returns Kea hooks version. +int version() { + return (KEA_HOOKS_VERSION); +} + +}