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
SUBDIRS += mysql_cb
endif
-SUBDIRS += stat_cmds user_chk
+SUBDIRS += stat_cmds flex_option user_chk
--- /dev/null
+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
+
--- /dev/null
+// 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 <config.h>
+
+#include <flex_option.h>
+#include <cc/simple_parser.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/option_space.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <eval/eval_context.h>
+
+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<uint8_t>::max();
+ } else {
+ max_code = numeric_limits<uint16_t>::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<uint16_t>(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
--- /dev/null
+// 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 <a href="https://jenkins.isc.org/job/Kea_doc/doxygen/">Kea
+Developer's Guide</a> 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
+
+*/
--- /dev/null
+// 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 <cc/data.h>
+#include <eval/evaluate.h>
+#include <eval/token.h>
+#include <string>
+#include <map>
+
+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<OptionConfig> OptionConfigPtr;
+
+ /// @brief The type of the option config map.
+ typedef std::map<uint16_t, OptionConfigPtr> 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 <typename PktType>
+ 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<FlexOptionImpl> FlexOptionImplPtr;
+
+} // end of namespace flex_option
+} // end of namespace isc
+#endif
--- /dev/null
+// 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 <config.h>
+
+#include <flex_option.h>
+#include <flex_option_log.h>
+#include <cc/command_interpreter.h>
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+
+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<Pkt4Ptr>(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<Pkt6Ptr>(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"
--- /dev/null
+// 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 <config.h>
+
+#include <flex_option_log.h>
+
+namespace isc {
+namespace flex_option {
+
+isc::log::Logger flex_option_logger("flex-option-hooks");
+
+} // namespace flex_option
+} // namespace isc
--- /dev/null
+// 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 <log/logger_support.h>
+#include <log/macros.h>
+#include <flex_option_messages.h>
+
+namespace isc {
+namespace flex_option {
+
+extern isc::log::Logger flex_option_logger;
+
+} // end of namespace flex_option
+} // end of namespace isc
+#endif
--- /dev/null
+// File created from ../../../../src/hooks/dhcp/flex_option/flex_option_messages.mes on Tue Oct 01 2019 14:08
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+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
+
--- /dev/null
+// 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 <log/message_types.h>
+
+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
--- /dev/null
+# 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.
+
--- /dev/null
+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)
--- /dev/null
+// 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 <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <dhcpsrv/host.h>
+#include <dhcp/option.h>
+#include <dhcp/option_string.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks.h>
+#include <flex_option.h>
+#include <flex_option_log.h>
+
+#include <gtest/gtest.h>
+
+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
--- /dev/null
+// 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 <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
+#include <gtest/gtest.h>
+#include <cc/data.h>
+#include <errno.h>
+
+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
--- /dev/null
+// 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 <config.h>
+
+#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);
+}
--- /dev/null
+// 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 <config.h>
+#include <hooks/hooks.h>
+
+extern "C" {
+
+/// @brief returns Kea hooks version.
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+}