src/hooks/dhcp/user_chk/Makefile
src/hooks/dhcp/user_chk/tests/Makefile
src/hooks/dhcp/user_chk/tests/test_data_files_config.h
+ src/hooks/dhcp/scripts/Makefile
+ src/hooks/dhcp/scripts/tests/Makefile
src/hooks/dhcp/stat_cmds/Makefile
src/hooks/dhcp/stat_cmds/tests/Makefile
src/lib/Makefile
SUBDIRS += mysql_cb
endif
-SUBDIRS += stat_cmds user_chk
+SUBDIRS += scripts stat_cmds 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)
+
+EXTRA_DIST = libdhcp_scripts.dox
+# Ensure that the message file is included in the distribution
+EXTRA_DIST += scripts_messages.mes
+
+CLEANFILES = *.gcno *.gcda
+
+# convenience archive
+
+noinst_LTLIBRARIES = libscripts.la
+
+libscripts_la_SOURCES =
+libscripts_la_SOURCES += load_unload.cc
+libscripts_la_SOURCES += pkt_send_co.cc
+libscripts_la_SOURCES += lease_expire_co.cc
+libscripts_la_SOURCES += scripts_cfg.cc scripts_cfg.h
+libscripts_la_SOURCES += scripts_log.cc scripts_log.h
+libscripts_la_SOURCES += scripts_messages.cc scripts_messages.h
+libscripts_la_SOURCES += version.cc
+
+libscripts_la_CXXFLAGS = $(AM_CXXFLAGS)
+libscripts_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+noinst_LTLIBRARIES += libdhcp_scripts.la
+
+libdhcp_scripts_la_SOURCES =
+libdhcp_scripts_la_LDFLAGS = $(AM_LDFLAGS)
+libdhcp_scripts_la_LDFLAGS += -avoid-version -export-dynamic -module
+# -rpath /nowhere is a hack to trigger libtool to not create a
+# convenience archive, resulting in shared modules
+libdhcp_scripts_la_LDFLAGS += -rpath /nowhere
+libdhcp_scripts_la_LIBADD = libscripts.la
+libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
+libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libdhcp_scripts_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libdhcp_scripts_la_LIBADD += $(LOG4CPLUS_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 scripts_messages.h scripts_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: scripts_messages.h scripts_messages.cc
+ @echo Message files regenerated
+
+scripts_messages.h scripts_messages.cc: scripts_messages.mes
+ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/scripts/scripts_messages.mes
+
+else
+
+messages scripts_messages.h scripts_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/.
+
+/// @file lease_expire_co.cc Defines the lease4_send and lease6_send callout functions.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <hooks/hooks.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option_string.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+#include <dhcp/docsis3_option_defs.h>
+#include <dhcpsrv/lease.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <scripts_cfg.h>
+#include <scripts.h>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace hooks::scripts;
+using namespace std;
+
+// 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 "lease6_expire" hook.
+///
+/// @return 0 upon success, non-zero otherwise.
+int lease6_expire(CalloutHandle& handle) {
+ try {
+ Lease6Ptr lease;
+ handle.getArgument("lease6", lease);
+
+ if (!lease) {
+ // Something is very wrong here.
+ return (1);
+ }
+
+ Variables vars;
+
+ stringstream tmp;
+ switch (lease->type_) {
+ case Lease::TYPE_NA:
+ case Lease::TYPE_TA:
+ case Lease::TYPE_V4:
+ tmp << "ADDRESS1=" << lease->addr_.toText();
+ break;
+ case Lease::TYPE_PD:
+ tmp << "PREFIX1=" << lease->addr_.toText() << "/" << static_cast<int>(lease->prefixlen_);
+ break;
+ default:
+ // Unknown lease type.
+ return (1);
+ }
+ vars.push_back(tmp.str());
+
+ if (lease->duid_) {
+ tmp.str("");
+ tmp << "DUID=" << lease->duid_->toText();
+ vars.push_back(tmp.str());
+ }
+
+ tmp.str("");
+ tmp << "HOSTNAME=" << lease->hostname_;
+ vars.push_back(tmp.str());
+
+ callScript(vars);
+
+ } catch (const std::exception& ex) {
+ std::cout << "DHCP Scripts Hook : lease6_expire unexpected error: "
+ << ex.what() << std::endl;
+ return (1);
+ }
+
+ return (0);
+}
+
+} // 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/.
+
+/// @file load_unload.cc Defines the load and unload hooks library functions.
+
+#include <config.h>
+
+#include <hooks/hooks.h>
+#include <cc/data.h>
+#include <scripts_log.h>
+#include <scripts_cfg.h>
+
+#include <sstream>
+#include <vector>
+#include <errno.h>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::log;
+using namespace hooks::scripts;
+
+// 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 Called by the Hooks library manager when the library is loaded.
+///
+/// Instantiates the UserRegistry and opens the outcome file. Failure in
+/// either results in a failed return code.
+///
+/// @return Returns 0 upon success, non-zero upon failure.
+int load(LibraryHandle& handle) {
+ // non-zero indicates an error.
+ int ret_val = 0;
+
+ ScriptsConfigPtr cfg = getScriptsConfig();
+ cfg->clear();
+
+ try {
+ // zero out the errno to be safe
+ errno = 0;
+
+ ConstElementPtr scripts = handle.getParameter("scripts");
+ if (!scripts) {
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR)
+ .arg("Mandatory parameter 'scripts' missing");
+ return (1);
+ }
+ if (scripts->getType() != Element::list) {
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR)
+ .arg("Parameter 'scripts' specified, but it's not a list");
+ return (1);
+ }
+
+ if (scripts->size() == 0) {
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR)
+ .arg("Parameter 'scripts' list specified, but it is empty.");
+ return (1);
+ }
+
+ for (int i = 0; i < scripts->size(); i++) {
+ auto s = scripts->get(i);
+ if (!s || s->getType() != Element::string) {
+ stringstream tmp;
+ tmp << "Element " << i << " of the 'scripts' list is either empty or is not a string";
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR)
+ .arg(tmp.str());
+ }
+ // Store the script name in our config.
+ cfg->scripts_.push_back(s->stringValue());
+ LOG_DEBUG(scripts_logger, DBGLVL_START_SHUT, SCRIPTS_HOOK_SCRIPT_ENTRY)
+ .arg(s->stringValue());
+ }
+ }
+ catch (const std::exception& ex) {
+ // Log the error and return failure.
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR).arg(ex.what());
+ ret_val = 1;
+ }
+
+ try {
+ ConstElementPtr async = handle.getParameter("async");
+ if (async && async->getType() != Element::boolean) {
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_LOAD_ERROR)
+ .arg("Parameter 'async' specified, but it's not a boolean");
+ return (1);
+ }
+ cfg->async_ = async->boolValue();
+ } catch (...) {
+ // We don't care. The async parameter is optional.
+ }
+
+ return (ret_val);
+}
+
+/// @brief Called by the Hooks library manager when the library is unloaded.
+///
+/// Destroys the UserRegistry and closes the outcome file.
+///
+/// @return Always returns 0.
+int unload() {
+ try {
+ // We only clean the configuration. There's nothing else to clean up.
+ ScriptsConfigPtr cfg = getScriptsConfig();
+ cfg->clear();
+ } catch (const std::exception& ex) {
+ // On the off chance something goes awry, catch it and log it.
+ // @todo Not sure if we should return a non-zero result or not.
+ LOG_ERROR(scripts_logger, SCRIPTS_HOOK_UNLOAD_ERROR).arg(ex.what());
+ }
+
+ return (0);
+}
+
+}
--- /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 pkt_send_co.cc Defines the pkt4_send and pkt6_send callout functions.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <hooks/hooks.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/dhcp6.h>
+#include <scripts_cfg.h>
+#include <scripts.h>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace hooks::scripts;
+using namespace std;
+
+// 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 "pkt6_send" hook.
+///
+/// This function generates environment variables based on the Pkt6 content.
+/// It then calls the external script.
+///
+/// @return 0 upon success, non-zero otherwise.
+int pkt6_send(CalloutHandle& handle) {
+
+
+ try {
+ Pkt6Ptr query;
+ handle.getArgument("query6", query);
+
+ Pkt6Ptr response;
+ handle.getArgument("response6", response);
+
+ Variables vars;
+
+ if (!query || !response) {
+ // Something is very wrong.
+ return (1);
+ }
+
+ stringstream tmp;
+ tmp << "ACTION=" << Pkt6::getName(query->getType());
+ vars.push_back(tmp.str());
+
+ auto ia_list = response->getOptions(D6O_IA_NA);
+ int addr_cnt = 1;
+ for (auto ia : ia_list) {
+ auto opts = ia.second->getOptions();
+
+ for (auto opt : opts) {
+ if (opt.second->getType() != D6O_IAADDR) {
+ continue;
+ }
+
+ Option6IAAddrPtr addr = boost::dynamic_pointer_cast<Option6IAAddr>(opt.second);
+ if (!addr) {
+ // something is wrong. But let's ignore it and move forward.
+ continue;
+ }
+
+ tmp.str("");
+ tmp << "ADDRESS" << (addr_cnt) << "=" << addr->getAddress();
+ vars.push_back(tmp.str());
+
+
+ tmp.str("");
+ tmp << "ADDRESS" << (addr_cnt) << "_PREFERRED=" << addr->getPreferred();
+ vars.push_back(tmp.str());
+
+ tmp.str("");
+ tmp << "ADDRESS" << (addr_cnt++) << "_VALID=" << addr->getValid();
+ vars.push_back(tmp.str());
+ }
+ }
+
+ auto pd_list = response->getOptions(D6O_IA_PD);
+ int prefix_cnt = 1;
+
+ for (auto pd: pd_list) {
+ auto opts = pd.second->getOptions();
+
+ for (auto opt : opts) {
+ if (opt.second->getType() != D6O_IAPREFIX) {
+ continue;
+ }
+
+ Option6IAPrefixPtr prefix = boost::dynamic_pointer_cast<Option6IAPrefix>(opt.second);
+ if (!prefix) {
+ // something is wrong. But let's ignore it and move forward.
+ continue;
+ }
+
+ stringstream tmp;
+ tmp << "PREFIX" << (prefix_cnt) << "=" << prefix->getAddress() << "/"
+ << prefix->getLength();
+ vars.push_back(tmp.str());
+
+ tmp.str("");
+ tmp << "PREFIX" << (prefix_cnt) << "_PREFERRED=" << prefix->getPreferred();
+ vars.push_back(tmp.str());
+
+ tmp.str("");
+ tmp << "PREFIX" << (prefix_cnt++) << "_VALID=" << prefix->getValid();
+ vars.push_back(tmp.str());
+ }
+
+ }
+
+ // Ok, all variables are set, call the scripts!
+ callScript(vars);
+
+ } catch (const std::exception& ex) {
+ std::cout << "DHCP Scripts Hook : pkt6_send unexpected error: "
+ << ex.what() << std::endl;
+ return (1);
+ }
+
+
+ return (0);
+}
+
+} // extern C
--- /dev/null
+
+#include <scripts.h>
+#include <scripts_cfg.h>
+
+namespace hooks {
+namespace scripts {
+
+bool callScript(const Variables& vars) {
+
+ ScriptsConfigPtr cfg = getScriptsConfig();
+ try {
+
+
+ } catch (const std::exception& e) {
+
+ return (false);
+ }
+
+ return (true);
+}
+
+};
+};
--- /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 VARIABLES_H
+#define VARIABLES_H
+
+#include <string>
+#include <vector>
+
+namespace hooks {
+namespace scripts {
+
+ /// @brief keeps a list of variables.
+ ///
+ /// Each entry is supposed to have a string containing VARIABLE=VALUE
+ typedef std::vector<std::string> Variables;
+
+ /// @brief calls the actual scripts
+ ///
+ /// @param vars environment variables to be passed to the script.
+ /// @return true if successful, false otherwise
+ bool callScript(const Variables& vars);
+};
+};
+
+#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 <scripts_cfg.h>
+
+namespace {
+hooks::scripts::ScriptsConfigPtr config;
+};
+
+namespace hooks {
+namespace scripts {
+
+ScriptsConfig::ScriptsConfig() {
+ clear();
+}
+
+void
+ScriptsConfig::clear() {
+ async_ = true;
+ scripts_.clear();
+}
+
+ScriptsConfigPtr getScriptsConfig() {
+ if (!config) {
+ config.reset(new ScriptsConfig());
+ }
+ return (config);
+}
+
+
+
+};
+};
--- /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 SCRIPTS_HOOK_H
+#define SCRIPTS_HOOK_H
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <string>
+
+namespace hooks {
+namespace scripts {
+
+/// @brief Represents scripts hook configuration
+class ScriptsConfig {
+ public:
+ /// @brief default constructor
+ ScriptsConfig();
+
+ /// @brief Clears configuration.
+ void clear();
+
+ /// @brief scripts to be called.
+ std::vector<std::string> scripts_;
+
+ /// @brief define whether scripts should be called async (true) or sync (false)
+ bool async_;
+};
+
+/// @brief pointer to ScriptsConfig
+typedef boost::shared_ptr<ScriptsConfig> ScriptsConfigPtr;
+
+/// @brief returns current Scripts hook configuration
+ScriptsConfigPtr getScriptsConfig();
+
+};
+};
+
+#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/.
+
+/// Defines the logger used by the user check hooks library.
+#include <config.h>
+
+#include <scripts_log.h>
+
+namespace hooks {
+namespace scripts {
+
+isc::log::Logger scripts_logger("scripts");
+
+} // namespace hooks::scripts
+} // namespace hooks
--- /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 SCRIPTS_LOG_H
+#define SCRIPTS_LOG_H
+
+#include <log/message_initializer.h>
+#include <log/macros.h>
+#include <scripts_messages.h>
+
+namespace hooks {
+namespace scripts {
+
+/// @brief Scripts Logger
+///
+/// Define the logger used to log messages.
+extern isc::log::Logger scripts_logger;
+
+} // end of namespace scripts
+} // end of namespace hooks
+
+#endif // SCRIPTS_LOG_H
--- /dev/null
+// File created from ../../../../src/hooks/dhcp/scripts/scripts_messages.mes on Thu Sep 19 2019 12:34
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+extern const isc::log::MessageID SCRIPTS_HOOK_LOAD_ERROR = "SCRIPTS_HOOK_LOAD_ERROR";
+extern const isc::log::MessageID SCRIPTS_HOOK_SCRIPT_ENTRY = "SCRIPTS_HOOK_SCRIPT_ENTRY";
+extern const isc::log::MessageID SCRIPTS_HOOK_UNLOAD_ERROR = "SCRIPTS_HOOK_UNLOAD_ERROR";
+
+namespace {
+
+const char* values[] = {
+ "SCRIPTS_HOOK_LOAD_ERROR", "DHCP ScriptsHook could not be loaded: %1",
+ "SCRIPTS_HOOK_SCRIPT_ENTRY", "DHCP Scripts Hook will use the following script: %1",
+ "SCRIPTS_HOOK_UNLOAD_ERROR", "DHCP Scripts Hook an error occurred unloading the library: %1",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
--- /dev/null
+// File created from ../../../../src/hooks/dhcp/scripts/scripts_messages.mes on Thu Sep 19 2019 12:34
+
+#ifndef SCRIPTS_MESSAGES_H
+#define SCRIPTS_MESSAGES_H
+
+#include <log/message_types.h>
+
+extern const isc::log::MessageID SCRIPTS_HOOK_LOAD_ERROR;
+extern const isc::log::MessageID SCRIPTS_HOOK_SCRIPT_ENTRY;
+extern const isc::log::MessageID SCRIPTS_HOOK_UNLOAD_ERROR;
+
+#endif // SCRIPTS_MESSAGES_H
--- /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/.
+
+% SCRIPTS_HOOK_LOAD_ERROR DHCP ScriptsHook could not be loaded: %1
+This is an error message issued when the DHCP Scripts Hook could not be loaded.
+The exact cause should be explained in the log message. User subnet selection
+will revert to default processing.
+
+% SCRIPTS_HOOK_UNLOAD_ERROR DHCP Scripts Hook an error occurred unloading the library: %1
+This is an error message issued when an error occurs while unloading the
+Scripts Hook library. This is unlikely to occur and normal operations of the
+library will likely resume when it is next loaded.
+
+% SCRIPTS_HOOK_SCRIPT_ENTRY DHCP Scripts Hook will use the following script: %1
+This debug message is printed when Scripts hook library is loaded and it parses
+a script name.
\ No newline at end of file
--- /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/.
+// version.cc
+
+#include <config.h>
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+/// @brief Version function required by Hooks API for compatibility checks.
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+}