--- /dev/null
+#KEASRC = /tmp/kea/src/lib
+
+# Kea includes and boost
+CPPFLAGS = -I../../.. -I$(KEASRC) -I/usr/local/include
+# lua 5.3
+CPPFLAGS += -I/usr/local/include/lua-5.3
+
+CXXFLAGS = -DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_SYSTEM_NO_DEPRECATED
+CXXFLAGS += -g -fPIC -dynamic
+
+# Kea dhcp library
+LIBS = -lkea-dhcp++ -lkea-hooks -lkea-exceptions
+# lua 5.3
+LIBS += -llua.5.3 -lm
+
+# Kea dependencies
+LDFLAGS = -L/usr/local/Cellar/botan/1.10.12/lib
+LDFLAGS += -L$(KEASRC)/dhcp/.libs
+LDFLAGS += -L$(KEASRC)/hooks/.libs
+LDFLAGS += -L$(KEASRC)/exceptions/.libs
+# lua 5.3
+LDFLAGS += -L/usr/local/lib
+
+SRCS = dso.cc loption.h loption.cc lpkt4.h lpkt4.cc
+
+OBJS = dso.o loption.o lpkt4.o
+
+all: kea.so tests
+
+kea.so: $(OBJS)
+ g++ -shared $(OBJS) $(LDFLAGS) $(LIBS) -o kea.so
+
+TESTLIBS = -lkea-dhcpsrv -lkea-dhcp++ -lkea-asiolink -lkea-cc
+TESTLIBS += -lkea-hooks -lkea-log -lkea-exceptions
+
+TESTLDFLAGS = -L/usr/local/Cellar/botan/1.10.12/lib
+TESTLDFLAGS += -L$(KEASRC)/dhcpsrv/.libs
+TESTLDFLAGS += -L$(KEASRC)/dhcp/.libs
+TESTLDFLAGS += -L$(KEASRC)/asiolink/.libs
+TESTLDFLAGS += -L$(KEASRC)/cc/.libs
+TESTLDFLAGS += -L$(KEASRC)/hooks/.libs
+TESTLDFLAGS += -L$(KEASRC)/log/.libs
+TESTLDFLAGS += -L$(KEASRC)/exceptions/.libs
+
+tests: tests.o
+ g++ tests.o $(TESTLDFLAGS) $(TESTLIBS) -o tests
+
+clean:
+ rm -f *.o *.so tests
--- /dev/null
+Implementation notes for lua
+
+Manifest:
+ - loption.h loption.cc: the C++ OptionPtr encapsulated into a lua full
+ userdata with a few methods ported.
+
+ - ppkt4.h ppkt4.cc: the C++ Pkt4Ptr encapsulated into a lua full userdata
+ with a few methods ported.
+
+ one can complete these lua types or/and new lua type.
+
+ - dso.cc: the kea framework and hook glue: on the kea / C side it is
+ a dynamic shared object providing the framework and hook entry points,
+ on the lua side it embeds an interpreter which imports the lua script
+ hook.lua.
+
+ loption.o, lpkt4.o and dso.o are compiled into kea.so.
+
+ - hook.lua: the lua script which implements the lua part of hook
+ hook handlers using lua option and pkt4 modules.
+
+ - tests.cc: source of test program which loads the kea.so hook-library
+ and exercise the pkt4_receive hook. It gives an independent executable.
+
+ - cshenv: C-shell script setting environment variables for a kea
+ distrib in /tmp/kea on OS X. Note I brewed a recent (vs standard)
+ version of lua (last one is 5.3.3).
+
+ - Makefile: make config file for OS X.
+
+ - NOTES: this file.
+
+I was afraid to have to recompile lua for C++ but it was not (yet?)
+necessary.
+
+The preload stuff from linit.c doesn't work.
+
+A possible bug issue: the allocator (lua_newuserdata) does NOT
+clear the memory?!
--- /dev/null
+setenv KEATOP /tmp/kea
+
+setenv KEASRC $KEATOP/src/lib
+
+setenv DYLD_LIBRARY_PATH $KEASRC/dhcpsrv/.libs:$KEASRC/eval/.libs:$KEASRC/dhcp_ddns/.libs:$KEASRC/stats/.libs:$KEASRC/hooks/.libs:$KEASRC/config/.libs:$KEASRC/dhcp/.libs:$KEASRC/asiolink/.libs:$KEASRC/dns/.libs:$KEASRC/cc/.libs:$KEASRC/cryptolink/.libs:$KEASRC/log/.libs:$KEASRC/util/threads/.libs:$KEASRC/util/.libs:$KEASRC/exceptions/.libs
--- /dev/null
+// Copyright (C) 2016 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 <lua.hpp>
+
+#include <hooks/hooks.h>
+
+#include <hooks/external/lua/loption.h>
+#include <hooks/external/lua/lpkt4.h>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::kea;
+
+namespace {
+ // Lua state
+ lua_State* L;
+};
+
+extern "C" {
+
+// Framework functions
+
+// version
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+// load
+int load(LibraryHandle& handle) {
+ // Get a new state
+ L = luaL_newstate();
+ if (!L) {
+ cerr << "can't get new state\n";
+ return (1);
+ }
+
+ // Check against version mismatch
+ luaL_checkversion(L);
+
+ // Add option and pkt4 as to be preloaded modules
+ static_cast<void>(luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"));
+ lua_pushcfunction(L, luaopen_option);
+ lua_setfield(L, -2, "option");
+ lua_pushcfunction(L, luaopen_pkt4);
+ lua_setfield(L, -2, "pkt4");
+ lua_pop(L, 1);
+
+ // Load librairies
+ luaL_openlibs(L);
+
+ // Load option
+ luaL_requiref(L, LUA_OPTIONLIBNAME, luaopen_option, 1);
+ lua_pop(L, 1);
+
+ // Load pkt4
+ luaL_requiref(L, LUA_PKT4LIBNAME, luaopen_pkt4, 1);
+ lua_pop(L, 1);
+
+ // Get the script module name (default "hook.lua" file)
+ ConstElementPtr script = handle.getParameter("script");
+ string scptname = "hook.lua";
+ if (script && script->getType() == Element::string) {
+ scptname = script->stringValue();
+ } else {
+ cout << "no \"script\" parameter: using \"hook.hua\"\n";
+ }
+ int ret = luaL_loadfile(L, scptname.c_str());
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRSYNTAX:
+ cerr << "syntax error in \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (2);
+ case LUA_ERRFILE:
+ cerr << "bad file \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (2);
+ case LUA_ERRMEM:
+ case LUA_ERRGCMM:
+ cerr << "memory problem loading \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (2);
+ default:
+ cerr << "unspecified error (" << ret
+ << ") loading \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (2);
+ }
+
+ // Run the script
+ ret = lua_pcall(L, 0, 0, 0);
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRRUN:
+ cerr << "runtime error from \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (3);
+ case LUA_ERRMEM:
+ case LUA_ERRGCMM:
+ cerr << "memory problem running \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (3);
+ default:
+ cerr << "unspecified error (" << ret
+ << ") running \"" << scptname << "\": "
+ << lua_tostring(L, -1) << "\n";
+ return (3);
+ }
+
+ // Get pkt4_receive handler
+ ret = lua_getglobal(L, "pkt4_receive");
+ switch (ret) {
+ case LUA_TNIL:
+ cerr << "can't find \"pkt4_receive\"\n";
+ return (4);
+ case LUA_TFUNCTION:
+ break;
+ default:
+ cerr << "\"pkt4_receive\" is not a function, it is a "
+ << lua_typename(L, ret) << "\n";
+ return (4);
+ }
+ lua_pop(L, 1);
+
+ return (0);
+}
+
+// unload
+int unload() {
+ // Release the state
+ lua_close(L);
+ L = NULL;
+
+ return (0);
+}
+
+// pkt4_receive hook
+int pkt4_receive(CalloutHandle& handle) {
+ if (lua_getglobal(L, "pkt4_receive") != LUA_TFUNCTION) {
+ return (0);
+ }
+ cout << "pkt4_receive: enter\n";
+
+ Pkt4Ptr query4;
+ handle.getArgument("query4", query4);
+ if (!query4) {
+ cerr << "pkt4_receive: null query4\n";
+ return (0);
+ }
+
+ void *query = lua_newuserdata(L, sizeof(l_pkt4));
+ if (!query) {
+ cerr << "lua_newuserdata failed\n";
+ return (0);
+ }
+ memset(query, 0, sizeof(l_pkt4));
+ (static_cast<l_pkt4*>(query))->object = query4;
+ if (luaL_getmetatable(L, LUA_KEAPKT4) == LUA_TNIL) {
+ cerr << "no metatable for " << LUA_KEAPKT4 << "\n";
+ }
+ lua_pop(L, 1);
+ luaL_setmetatable(L, LUA_KEAPKT4);
+
+ int ret = lua_pcall(L, 1, 1, 0);
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRRUN:
+ cerr << "runtime error in pkt4_receive: "
+ << lua_tostring(L, -1) << "\n";
+ lua_pop(L, 1);
+ return (0);
+ case LUA_ERRMEM:
+ case LUA_ERRGCMM:
+ cerr << "memory problem in pkt4_receive: "
+ << lua_tostring(L, -1) << "\n";
+ lua_pop(L, 1);
+ return (0);
+ default:
+ cerr << "unspecified error (" << ret
+ << ") in pkt4_receive: "
+ << lua_tostring(L, -1) << "\n";
+ lua_pop(L, 1);
+ return (0);
+ }
+ if (lua_type(L, -1) != LUA_TNUMBER) {
+ cerr << "pkt4_receive didn't return a number\n";
+ lua_pop(L, 1);
+ return (0);
+ }
+ ret = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ cout << "pkt4_receive: return " << ret << "\n";
+ return (ret);
+}
+
+}
--- /dev/null
+-- Copyright (C) 2016 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/.
+
+-- Support for Kea hook in lua
+
+print("hook.lua is loading")
+
+-- require kea
+
+NEXT_STEP_CONTINUE = 0
+NEXT_STEP_SKIP = 1
+NEXT_STEP_DROP = 2
+
+-- pkt4_receive hook point
+--
+-- parameter: inout Pkt4Ptr query4
+-- return: next step
+
+function pkt4_receive(query4)
+ print("pkt4_receive: handler is called with", tostring(query4))
+ return NEXT_STEP_CONTINUE
+end
+
+print("hook.lua loaded")
--- /dev/null
+// Copyright (C) 2016 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 <lua.hpp>
+
+#include <hooks/external/lua/loption.h>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::kea;
+
+// Constructor
+l_option::l_option() {}
+
+namespace { // anonymous namespace
+
+// factory(universe, type, data)
+int factory(lua_State* L) {
+ // Require 3 arguments
+ if (lua_gettop(L) - 1 != 3) {
+ return (luaL_error(L, "option.factory takes 3 arguments"));
+ }
+ if (lua_type(L, -3) != LUA_TNUMBER) {
+ return (luaL_error(L, "universe must be a number"));
+ }
+ if (lua_type(L, -2) != LUA_TNUMBER) {
+ return (luaL_error(L, "type must be a number"));
+ }
+ if (lua_type(L, -1) != LUA_TSTRING) {
+ return (luaL_error(L, "data must be a string"));
+ }
+ unsigned char u = static_cast<unsigned char>(lua_tonumber(L, -3));
+ unsigned short t = static_cast<unsigned short>(lua_tonumber(L, -2));
+ Option::Universe universe;
+ switch (u) {
+ case 4:
+ universe = Option::V4;
+ if (t > 255) {
+ return (luaL_error(L, "out of range type for DHCPv4: %d",
+ static_cast<int>(t)));
+ }
+ break;
+ case 6:
+ universe = Option::V6;
+ break;
+ default:
+ return (luaL_error(L, "universe must be 4 or 6 (not %d)",
+ static_cast<int>(u)));
+ }
+
+ size_t len = 0;
+ const char* d = lua_tolstring(L, -1, &len);
+ OptionBuffer data;
+ data.resize(len);
+ if (len) {
+ memmove(&data[0], d, len);
+ }
+
+ l_option* const self =
+ static_cast<l_option*>(lua_newuserdata(L, sizeof(l_option)));
+ memset(self, 0, sizeof(l_option));
+ self->object.reset(new Option(universe, t, data));
+ luaL_setmetatable(L, LUA_KEAOPTION);
+ return (1);
+}
+
+// __gc
+int option__gc(lua_State* L) {
+ // This is a critical code to avoid memory leaks
+ cout << "option__gc called\n";
+ l_option* const self =
+ static_cast<l_option*>(luaL_checkudata(L, 1, LUA_KEAOPTION));
+ self->object.reset();
+ return (0);
+}
+
+// __tostring
+int option__tostring(lua_State* L) {
+ l_option* const self =
+ static_cast<l_option*>(luaL_checkudata(L, 1, LUA_KEAOPTION));
+ lua_pushstring(L, self->object->toText(0).c_str());
+ return (1);
+}
+
+// getUniverse() method
+int getUniverse(lua_State* L) {
+ l_option* const self =
+ static_cast<l_option*>(luaL_checkudata(L, 1, LUA_KEAOPTION));
+ switch (self->object->getUniverse()) {
+ case Option::V4:
+ lua_pushinteger(L, 4);
+ return (1);
+ case Option::V6:
+ lua_pushinteger(L, 6);
+ return (1);
+ default:
+ return (luaL_error(L, "getUniverse"));
+ }
+}
+
+// Method table
+const luaL_Reg option_method[] = {
+ { "__gc", option__gc },
+ { "__tostring", option__tostring },
+ { "getUniverse", getUniverse },
+ { NULL, NULL }
+};
+
+// Function table
+const luaL_Reg option_function[] = {
+ { NULL, NULL }
+};
+
+// Create the module metadata
+void createmeta(lua_State* L) {
+ // create metatable
+ luaL_newmetatable(L, LUA_KEAOPTION);
+ // push metatable
+ lua_pushvalue(L, -1);
+ // metatable.__index = metatable
+ lua_setfield(L, -2, "__index");
+ // add methods
+ luaL_setfuncs(L, option_method, 0);
+ // pop new metatable
+ lua_pop(L, 1);
+}
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace kea {
+
+// Initialize the module
+int luaopen_option(lua_State* L) {
+ // new module
+ luaL_newlib(L, option_function);
+ createmeta(L);
+
+ // constants
+
+ return (1);
+}
+
+} // namespace lua
+} // namespace isc
--- /dev/null
+// Copyright (C) 2016 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 LOPTION_H
+#define LOPTION_H 1
+
+#include <dhcp/option.h>
+
+namespace isc {
+namespace kea {
+
+// Lua option class
+class l_option {
+ l_option();
+
+public:
+ isc::dhcp::OptionPtr object;
+};
+
+#define LUA_KEAOPTION "kea-option"
+
+#define LUA_OPTIONLIBNAME "option"
+
+LUAMOD_API int luaopen_option(lua_State* L);
+
+} // namespace kea
+} // namespace isc
+
+#endif // LOPTION_H
--- /dev/null
+// Copyright (C) 2016 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 <lua.hpp>
+
+#include <hooks/external/lua/loption.h>
+#include <hooks/external/lua/lpkt4.h>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::kea;
+
+// Constructor
+l_pkt4::l_pkt4() {}
+
+namespace { // anonymous namespace
+
+// __gc
+int pkt4__gc(lua_State* L) {
+ // This is a critical code to avoid memory leaks
+ cout << "pkt4__gc called\n";
+ l_pkt4* const self =
+ static_cast<l_pkt4*>(luaL_checkudata(L, 1, LUA_KEAPKT4));
+ self->object.reset();
+ return (0);
+}
+
+// __tostring
+int pkt4__tostring(lua_State* L) {
+ l_pkt4* const self =
+ static_cast<l_pkt4*>(luaL_checkudata(L, 1, LUA_KEAPKT4));
+ lua_pushstring(L, self->object->toText().c_str());
+ return (1);
+}
+
+// addOption(const OptionPtr opt) method
+int addOption(lua_State* L) {
+ l_pkt4* const self =
+ static_cast<l_pkt4*>(luaL_checkudata(L, 1, LUA_KEAPKT4));
+ l_option* const sub =
+ static_cast<l_option*>(luaL_checkudata(L, 2, LUA_KEAOPTION));
+ self->object->addOption(sub->object);
+ return (0);
+}
+
+// Method table
+const luaL_Reg pkt4_method[] = {
+ { "__gc", pkt4__gc },
+ { "__tostring", pkt4__tostring },
+ { "addOption", addOption },
+ { NULL, NULL }
+};
+
+// Function table
+const luaL_Reg pkt4_function[] = {
+ { NULL, NULL }
+};
+
+// Create the module metadata
+void createmeta(lua_State* L) {
+ // create metatable
+ luaL_newmetatable(L, LUA_KEAPKT4);
+ // push metatable
+ lua_pushvalue(L, -1);
+ // metatable.__index = metatable
+ lua_setfield(L, -2, "__index");
+ // add methods
+ luaL_setfuncs(L, pkt4_method, 0);
+ // pop new metatable
+ lua_pop(L, 1);
+}
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace kea {
+
+// Initialize the module
+int luaopen_pkt4(lua_State* L) {
+ // new module
+ luaL_newlib(L, pkt4_function);
+ createmeta(L);
+
+ // constants
+
+ return (1);
+}
+
+} // namespace lua
+} // namespace isc
--- /dev/null
+// Copyright (C) 2016 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 LPKT4_H
+#define LPKT4_H 1
+
+#include <dhcp/pkt4.h>
+
+namespace isc {
+namespace kea {
+
+// Lua DHCPv4 packet class
+class l_pkt4 {
+ l_pkt4();
+
+public:
+ isc::dhcp::Pkt4Ptr object;
+};
+
+#define LUA_KEAPKT4 "kea-pkt4"
+
+#define LUA_PKT4LIBNAME "pkt4"
+
+LUAMOD_API int luaopen_pkt4(lua_State* L);
+
+} // namespace kea
+} // namespace isc
+
+#endif // LPKT4_H
--- /dev/null
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <cc/data.h>
+#include <dhcp/pkt4.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/callout_handle_store.h>
+#include <hooks/hooks_manager.h>
+#include <log/logger_support.h>
+
+#include <boost/foreach.hpp>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace isc::log;
+
+// config fragment for hooks-libraries
+const string config =
+ "{ \"hooks-libraries\": ["
+ " { \"library\": \"kea.so\", "
+ " \"parameters\": "
+ " { \"script\": \"hook.lua\" }"
+ " }] }";
+
+// main routine
+int main() {
+ // must be first
+ int hi_pkt4_receive = HooksManager::registerHook("pkt4_receive");
+ cout << "pkt4_receive is hook#" << hi_pkt4_receive << "\n";
+
+ initLogger();
+
+ // check if there is a library already loaded
+ vector<string> hooks_libraries = HooksManager::getLibraryNames();
+ if (!hooks_libraries.empty()) {
+ cerr << "hooks_libraries is not empty\n";
+ }
+
+ // parse config into json
+ ElementPtr json = Element::fromJSON(config);
+ if (!json) {
+ cerr << "fatal: fromJSON failed\n";
+ exit(-1);
+ }
+ cout << "config parsed\n";
+
+ // call the hooks-libraries parser
+ boost::shared_ptr<HooksLibrariesParser> parser;
+ try {
+ const map<string, ConstElementPtr>& cmap = json->mapValue();
+ if (cmap.empty()) {
+ cerr << "fatal: config map is empty\n";
+ exit(-1);
+ }
+ if (cmap.size() > 1) {
+ cerr << "config map has more than one element\n";
+ }
+ if (cmap.count("hooks-libraries") == 0) {
+ cerr << "fatal: no \"hooks-libraries\" in config\n";
+ exit(-1);
+ }
+ const ConstElementPtr& hl_value = cmap.find("hooks-libraries")->second;
+ if (!hl_value) {
+ cerr << "fatal: empty \"hooks-libraries\" value\n";
+ exit(-1);
+ }
+ parser.reset(new HooksLibrariesParser("hooks-libraries"));
+ parser->build(hl_value);
+ parser->commit();
+ cout << "config committed\n";
+ } catch (const Exception& ex) {
+ cerr << "fatal: config parsing failed: " << ex.what() << "\n";
+ exit(-1);
+ }
+
+ // check if the library was loaded
+ HookLibsCollection libraries;
+ bool changed = false;
+ parser->getLibraries(libraries, changed);
+ if (!changed) {
+ cerr << "commit didn't change libraries\n";
+ }
+ if (libraries.empty()) {
+ cerr << "fatal: no libraries\n";
+ exit(-1);
+ }
+ if (libraries.size() > 1) {
+ cerr << "more than one library\n";
+ }
+ cout << "library is \"" + libraries[0].first + "\"\n";
+ if (libraries[0].first != "kea.so") {
+ cerr << "fatal: library is not \"kea.so\"\n";
+ exit(-1);
+ }
+ ConstElementPtr params = libraries[0].second;
+ if (!params) {
+ cerr << "no parameters\n";
+ } else {
+ cout << "got " << params->size() << " parameters\n";
+ }
+
+ // note we can't know this way if it was successfully loaded
+
+ // get the callout
+ if (!HooksManager::calloutsPresent(hi_pkt4_receive)) {
+ cerr << "fatal: no callout present for pkt4_receive\n";
+ exit(-1);
+ }
+
+ // from pkt4_unittests.cc
+ Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 0x12345678));
+ const uint8_t macAddr[] = {0, 1, 2, 3, 4, 5};
+ vector<uint8_t> vectorMacAddr(macAddr, macAddr + sizeof(macAddr));
+ pkt->setHWAddr(6, 6, vectorMacAddr);
+ pkt->setHops(13);
+ // Transaction-id is already set.
+ pkt->setSecs(42);
+ pkt->setFlags(BOOTP_BROADCAST);
+ pkt->setCiaddr(IOAddress("192.0.2.1"));
+ pkt->setYiaddr(IOAddress("1.2.3.4"));
+ pkt->setSiaddr(IOAddress("192.0.2.255"));
+ pkt->setGiaddr(IOAddress("255.255.255.255"));
+ // Chaddr already set with setHWAddr().
+
+ // from dhcp4_srv.cc
+ CalloutHandlePtr co_handle = getCalloutHandle(pkt);
+ co_handle->deleteAllArguments();
+ co_handle->setArgument("query4", pkt);
+ cout << "calling pkt4_receive callout\n";
+ HooksManager::callCallouts(hi_pkt4_receive, *co_handle);
+ cout << "pkt4_receive callout status " << co_handle->getStatus() << "\n";
+ co_handle->getArgument("query4", pkt);
+
+ // TODO...
+
+ exit(0);
+}
ocaml is statically typed so there is no type check in the runtime
even in the C++ code. So if you bug an external declaration you
likely bug the whole thing!
+
+To finish ocaml has a standard type named option (None | Some of)
+so I used the name opt (vs option) for the kea option type.