hooks_component_developer.dox 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. // Copyright (C) 2013, 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. /**
  15. @page hooksComponentDeveloperGuide Guide to Hooks for the Kea Component Developer
  16. @section hooksComponentIntroduction Introduction
  17. The hooks framework is a Kea system that simplifies the way that
  18. users can write code to modify the behavior of Kea. Instead of
  19. altering the Kea source code, they write functions that are compiled
  20. and linked into one or more dynamic shared objects, called here (for
  21. historical reasons), shared libraries. The library is specified in the Kea
  22. configuration and at run time Kea dynamically loads the library
  23. into its address space. At various points in the processing, the component
  24. "calls out" to functions in the library, passing to them the data is it
  25. currently working on. They can examine and modify the data as required.
  26. This guide is aimed at Kea developers who want to write or modify a
  27. Kea component to use hooks. It shows how the component should be written
  28. to load a shared library at run-time and how to call functions in it.
  29. For information about writing a hooks library containing functions called by Kea
  30. during its execution, see the document @ref hooksdgDevelopersGuide.
  31. @subsection hooksComponentTerminology Terminology
  32. In the remainder of this guide, the following terminology is used:
  33. - Component - a Kea process, e.g. the DHCPv4 or DHCPv6 server.
  34. - Hook/Hook Point - used interchangeably, this is a point in the code at
  35. which a call to user-written functions is made. Each hook has a name and
  36. each hook can have any number (including 0) of user-written functions
  37. attached to it.
  38. - Callout - a user-written function called by the component at a hook
  39. point. This is so-named because the component "calls out" to the library
  40. to execute a user-written function.
  41. - User code/user library - non-Kea code that is compiled into a
  42. shared library and loaded by Kea into its address space. Multiple
  43. user libraries can be loaded at the same time, each containing callouts for
  44. the same hooks. The hooks framework calls these libraries one after the
  45. other. (See the document @ref hooksdgDevelopersGuide for more details.)
  46. @subsection hooksComponentLanguages Languages
  47. The core of Kea is written in C++ with some remaining legacy parts in Python.
  48. While it is the intention to provide the hooks framework for all languages,
  49. the initial version is for C++. All examples in this guide are in that language.
  50. @section hooksComponentBasicIdeas Basic Ideas
  51. From the point of view of the component author, the basic ideas of the hooks
  52. framework are quite simple:
  53. - The location of hook points in the code need to be determined.
  54. - Name the hook points and register them.
  55. - At each hook point, the component needs to complete the following steps to
  56. execute callouts registered by the user-library:
  57. -# copy data into the object used to pass information to the callout.
  58. -# call the callout.
  59. -# copy data back from the object used to exchange information.
  60. -# take action based on information returned.
  61. Of course, to set up the system the libraries need to be loaded in the first
  62. place. The component also needs to:
  63. - Define the configuration item that specifies the user libraries for this
  64. component.
  65. - Handle configuration changes and load/unload the user libraries.
  66. The following sections will describe these tasks in more detail.
  67. @section hooksComponentDefinition Determining the Hook Points
  68. Before any other action takes place, the location of the hook points
  69. in the code need to be determined. This, of course, depends on the
  70. component but, as a general guideline, hook locations should be located
  71. where a callout is able to obtain useful information from Kea and/or
  72. affect processing. Typically this means at the start or end of a major
  73. step in the processing of a request, at a point where either useful
  74. information can be passed to a callout and/or the callout can affect
  75. the processing of the component. The latter is achieved in either or both
  76. of the following ways:
  77. - Setting the nest step status. This is an enum that the callout can set
  78. and is a quick way of passing information back to the component. It is used
  79. to indicate that the component should perform certain actions. Currently
  80. there are three statuses defined: CONTINUE (this is the default, the server
  81. is expected to continue as usual), SKIP (the server is expected to skip the
  82. next processing step, but otherwise continue as usual) and DROP (the server
  83. is expected to drop the packet or request completely. The exact action is up
  84. to the component.
  85. - Modifying data passed to it. The component should be prepared to continue
  86. processing with the data returned by the callout. It is up to the component
  87. author whether the data is validated before being used, but doing so will
  88. have performance implications.
  89. @section hooksComponentRegistration Naming and Registering the Hooks Points
  90. Once the location of the hook point has been determined, it should be
  91. given a name. This name should be unique amongst all hook points and is
  92. subject to certain restrictions (see below).
  93. Before the callouts at any hook point are called and any user libraries
  94. loaded - so typically during component initialization - the component must
  95. register the names of all the hooks. The registration is done using
  96. the static method @c isc::hooks::HooksManager::registerHook():
  97. @code
  98. #include <hooks/hooks_manager.h>
  99. :
  100. int example_index = HooksManager::registerHook("lease_allocate");
  101. @endcode
  102. The name of the hook is passed as the sole argument to the @c registerHook()
  103. method. The value returned is the index of that hook point and should
  104. be retained - it is needed to call the callouts attached to that hook.
  105. Note that a hook only needs to be registered once. There is no mechanism for
  106. unregistering a hook and there is no need to do so.
  107. @subsection hooksComponentAutomaticRegistration Automatic Registration of Hooks
  108. In some components, it may be convenient to set up a single initialization
  109. function that registers all hooks. For others, it may be more convenient
  110. for each module within the component to perform its own initialization.
  111. Since the @c isc::hooks::HooksManager object is a singleton and is created when first
  112. accessed, a useful trick is to automatically register the hooks when
  113. the module is loaded.
  114. This technique involves declaring an object outside of any execution
  115. unit in the module. When the module is loaded, the object's constructor
  116. is run. By placing the hook registration calls in the constructor,
  117. the hooks in the module are defined at load time, before any function in
  118. the module is run. The code for such an initialization sequence would
  119. be similar to:
  120. @code
  121. #include <hooks/hooks_manager.h>
  122. namespace {
  123. // Declare structure to perform initialization and store the hook indexes.
  124. //
  125. struct MyHooks {
  126. int pkt_rcvd; // Index of "packet received" hook
  127. int pkt_sent; // Index of "packet sent" hook
  128. // Constructor
  129. MyHooks() {
  130. pkt_rcvd = HooksManager::registerHook("pkt_rcvd");
  131. pkt_sent = HooksManager::registerHook("pkt_sent");
  132. }
  133. };
  134. // Declare a "MyHooks" object. As this is outside any function or method, it
  135. // will be instantiated (and the constructor run) when the module is loaded.
  136. // As a result, the hook indexes will be defined before any method in this
  137. // module is called.
  138. MyHooks my_hooks;
  139. } // Anonymous namespace
  140. void Someclass::someFunction() {
  141. :
  142. // Check if any callouts are defined on the pkt_rcvd hook.
  143. if (HooksManager::calloutPresent(my_hooks.pkt_rcvd)) {
  144. :
  145. }
  146. :
  147. }
  148. @endcode
  149. @subsection hooksComponentHookNames Hook Names
  150. Hook names are strings and in principle, any string can be used as the
  151. name of a hook, even one containing spaces and non-printable characters.
  152. However, the following guidelines should be observed:
  153. - The names <b>context_create</b> and <b>context_destroy</b> are reserved to
  154. the hooks system and are automatically registered: an attempt to register
  155. one of these will lead to a @c isc::hooks::DuplicateHook exception being thrown.
  156. - The hook name should be a valid "C" function name. If a user gives a
  157. callout the same name as one of the hooks, the hooks framework will
  158. automatically load that callout and attach it to the hook: the user does not
  159. have to explicitly register it.
  160. - The hook name should not conflict with the name of a function in any of
  161. the system libraries (e.g. naming a hook "sqrt" could lead to the
  162. square-root function in the system's maths library being attached to the hook
  163. as a callout).
  164. - Although hook names can be in any case (including mixed case), the Kea
  165. convention is that they are lower-case.
  166. @section hooksComponentCallingCallouts Calling Callouts on a Hook
  167. @subsection hooksComponentArgument The Callout Handle
  168. Before describing how to call user code at a hook point, we must first consider
  169. how to pass data to it.
  170. Each user callout has the signature:
  171. @code
  172. int callout_name(isc::hooks::CalloutHandle& handle);
  173. @endcode
  174. The @c isc::hooks::CalloutHandle object is the object used to pass data to
  175. and from the callout. This holds the data as a set of name/value pairs,
  176. each pair being considered an argument to the callout. If there are
  177. multiple callouts attached to a hook, the @c CalloutHandle is passed to
  178. each in turn. Should a callout modify an argument, the updated data is
  179. passed subsequent callouts (each of which could also modify it) before
  180. being returned to the component.
  181. Two methods are provided to get and set the arguments passed to
  182. the callout called (naturally enough) @c getArgument and @c setArgument.
  183. Their usage is illustrated by the following code snippets.
  184. @code
  185. int count = 10;
  186. boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value
  187. // Assume that "handle_ptr" has been created and is a pointer to a
  188. // CalloutHandle.
  189. handle_ptr->setArgument("data_count", count);
  190. handle_ptr->setArgument("inpacket", pktptr);
  191. // Call the hook code. lease_assigned_index is the value returned from
  192. // HooksManager::registerHook() when the hook was registered.
  193. HooksManager::callCallouts(lease_assigned_index, *handle_ptr);
  194. // Retrieve the modified values
  195. handle_ptr->getArgument("data_count", count);
  196. handle_ptr->getArgument("inpacket", pktptr);
  197. @endcode
  198. As can be seen @c getArgument is used to retrieve data from the
  199. @c CalloutHandle, and @c setArgument used to put data into it. If a callout
  200. wishes to alter data and pass it back to the component, it should retrieve
  201. the data with getArgument, modify it, and call setArgument to send
  202. it back.
  203. There are a couple points to be aware of:
  204. - The data type of the variable in the call to @c getArgument must
  205. match the data type of the variable passed to the corresponding
  206. @c setArgument <B>exactly</B>: using what would normally be considered
  207. to be a "compatible" type is not enough. For example, if the callout
  208. passed an argument back to the component as an @c int and the component
  209. attempted to retrieve it as a @c long, an exception would be thrown even
  210. though any value that can be stored in an @c int will fit into a @c long.
  211. This restriction also applies the "const" attribute but only as applied to
  212. data pointed to by pointers, e.g. if an argument is defined as a @c char*,
  213. an exception will be thrown if an attempt is made to retrieve it into
  214. a variable of type @c const @c char*. (However, if an argument is set as a
  215. @c const @c int, it can be retrieved into an @c int.) The documentation of
  216. a hook point should detail the exact data type of each argument.
  217. - If a pointer to an object is passed to a callout (either a "raw"
  218. pointer, or a boost smart pointer (as in the example above), and the
  219. underlying object is altered through that pointer, the change will be
  220. reflected in the component even if the callout makes no call to setArgument.
  221. This can be avoided by passing a pointer to a "const" object.
  222. @subsection hooksComponentSkipFlag The Skip Flag (obsolete)
  223. @subsection hooksComponentNextStep The next step status
  224. Although information is passed back to the component from callouts through
  225. @c CalloutHandle arguments, a common action for callouts is to inform the component
  226. that its flow of control should be altered. For example:
  227. - In the DHCP servers, there is a hook at the point at which a lease is
  228. about to be assigned. Callouts attached to this hooks may handle the
  229. lease assignment in special cases, in which case they set the next step
  230. status to SKIP to indicate that the server should not perform lease assignment
  231. in this case.
  232. - A server may define a hook just after a packet is received. A callout
  233. attached to the hook might inspect the source address and compare it
  234. against a blacklist. If the address is on the list, the callout could set
  235. the DROP flag to indicate to the server that the packet should be dropped.
  236. For ease of processing, the @c CalloutHandle contains
  237. two methods, @c isc::hooks::CalloutHandle::getStatus() and
  238. @c isc::hooks::CalloutHandle::setStatus(). It is only meaningful for the
  239. component to use the "get" method. The next step status is cleared (set to
  240. the default value of CONTINUE) by the hooks framework when the component
  241. requests that callouts be executed, so any
  242. value set by the component is lost. Callouts can both inspect the status (it
  243. might have been set by callouts earlier in the callout list for the hook)
  244. and set it. Note that the setting of the status by a callout does not
  245. prevent callouts later in the list from being called: the next step status is
  246. just an enum value - the only significance comes from its interpretation
  247. by the component.
  248. An example of use could be:
  249. @code
  250. // Set up arguments for DHCP lease assignment.
  251. handle->setArgument("query", query);
  252. handle->setArgument("response", response);
  253. HooksManager::callCallouts(lease_hook_index, *handle_ptr);
  254. if (! handle_ptr->getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
  255. // Skip flag not set, do the address allocation
  256. :
  257. }
  258. @endcode
  259. @subsection hooksComponentGettingHandle Getting the Callout Handle
  260. The @c CalloutHandle object is linked to the loaded libraries
  261. for lifetime reasons (described below). Components
  262. should retrieve a @c isc::hooks::CalloutHandle using
  263. @c isc::hooks::HooksManager::createCalloutHandle():
  264. @code
  265. CalloutHandlePtr handle_ptr = HooksManager::createCalloutHandle();
  266. @endcode
  267. (@c isc::hooks::CalloutHandlePtr is a typedef for a Boost shared pointer to a
  268. CalloutHandle.) The CalloutHandle so retrieved may be used for as
  269. long as the libraries are loaded.
  270. The handle is deleted by resetting the pointer:
  271. @code
  272. handle_ptr.reset();
  273. @endcode
  274. ... or by letting the handle pointer go out of scope. The actual deletion
  275. occurs when the CallHandle's reference count goes to zero. (The
  276. current version of the hooks framework does not maintain any other
  277. pointer to the returned CalloutHandle, so it gets destroyed when the
  278. shared pointer to it is cleared or destroyed. However, this may change
  279. in a future version.)
  280. @subsection hooksComponentCallingCallout Calling the Callout
  281. Calling the callout is a simple matter of executing the
  282. @c isc::hooks::HooksManager::callCallouts() method for the hook index in
  283. question. For example, with the hook index "pkt_sent" defined as above,
  284. the hook can be executed by:
  285. @code
  286. HooksManager::callCallouts(pkt_sent, *handle_ptr);
  287. @endcode
  288. ... where "*handle_ptr" is a reference (note: not a pointer) to the
  289. @c isc::hooks::CalloutHandle object holding the arguments. No status code
  290. is returned. If a component needs to get data returned (other than that
  291. provided by the next step status), it should define an argument through which
  292. the callout can do so.
  293. @subsubsection hooksComponentConditionalCallout Conditionally Calling Hook Callouts
  294. Most hooks in a component will not have callouts attached to them. To
  295. avoid the overhead of setting up arguments in the @c CalloutHandle, a
  296. component can check for callouts before doing that processing using
  297. @c isc::hooks::HooksManager::calloutsPresent(). Taking the index of a
  298. hook as its sole argument, the function returns true if there are any
  299. callouts attached to the hook and false otherwise.
  300. With this check, the code in the component for calling a hook would look
  301. something like:
  302. @code
  303. if (HooksManager::calloutsPresent(lease_hook_index)) {
  304. // Set up arguments for lease assignment
  305. handle->setArgument("query", query);
  306. handle->setArgument("response", response);
  307. HooksManager::callCallouts(lease_hook_index, *handle);
  308. if ( handle->getStatus() != CalloutHandle::NEXT_STEP_DROP ) {
  309. // Next step allows us to continue, do the address allocation
  310. :
  311. }
  312. }
  313. @endcode
  314. @section hooksComponentLoadLibraries Loading the User Libraries
  315. Once hooks are defined, all the hooks code described above will
  316. work, even if no libraries are loaded (and even if the library
  317. loading method is not called). The @c CalloutHandle returned by
  318. @c isc::hooks::HooksManager::createCalloutHandle() will be valid,
  319. @c isc::hooks::HooksManager::calloutsPresent() will return false for every
  320. index, and @c isc::hooks::HooksManager::callCallouts() will be a no-op.
  321. However, if user libraries are specified in the Kea configuration,
  322. the component should load them. (Note the term "libraries": the hooks
  323. framework allows multiple user libraries to be loaded.) This should take
  324. place after the component's configuration has been read, and is achieved
  325. by the @c isc::hooks::HooksManager::loadLibraries() method. The method is
  326. passed a vector of strings, each giving the full file specification of
  327. a user library:
  328. @code
  329. std::vector<std::string> libraries = ... // Get array of libraries
  330. bool success = HooksManager::loadLibraries(libraries);
  331. @endcode
  332. @c loadLibraries() returns a boolean status which is true if all libraries
  333. loaded successfully or false if one or more failed to load. Appropriate
  334. error messages will have been logged in the latter case, the status
  335. being more to allow the developer to decide whether the execution
  336. should proceed in such circumstances.
  337. If @c loadLibraries() is called a second or subsequent time (as a result
  338. of a reconfiguration), all existing libraries are unloaded and a new
  339. set loaded. Libraries can be explicitly unloaded either by calling
  340. @c isc::hooks::HooksManager::unloadLibraries() or by calling
  341. @c loadLibraries() with an empty vector as an argument.
  342. @subsection hooksComponentUnloadIssues Unload and Reload Issues
  343. Unloading a shared library works by unmapping the part of the process's
  344. virtual address space in which the library lies. This may lead to
  345. problems if there are still references to that address space elsewhere
  346. in the process.
  347. In many operating systems, heap storage allowed by a shared library will
  348. lie in the virtual address allocated to the library. This has implications
  349. in the hooks framework because:
  350. - Argument information stored in a @c CalloutHandle by a callout in a library
  351. may lie in the library's address space.
  352. - Data modified in objects passed as arguments may lie in the address
  353. space. For example, it is common for a DHCP callout to add "options"
  354. to a packet: the memory allocated for those options will most likely
  355. lie in library address space.
  356. The problem really arises because of the extensive use by Kea of boost
  357. smart pointers. When the pointer is destroyed, the pointed-to memory is
  358. deallocated. If the pointer points to address space that is unmapped because
  359. a library has been unloaded, the deletion causes a segmentation fault.
  360. The hooks framework addresses the issue for CalloutHandles by keeping in
  361. that object a shared pointer to the object controlling library unloading.
  362. Although a library can be unloaded at any time, it is only when all
  363. CalloutHandles that could possibly reference address space in the library
  364. have been deleted that the library will actually be unloaded and the
  365. address space unmapped.
  366. The hooks framework cannot solve the second issue as the objects in
  367. question are under control of the component. It is up to the component
  368. developer to ensure that all such objects have been destroyed before
  369. libraries are reloaded. In extreme cases this may mean the component
  370. suspending all processing of incoming requests until all currently
  371. executing requests have completed and data object destroyed, reloading
  372. the libraries, then resuming processing.
  373. @section hooksComponentCallouts Component-Defined Callouts
  374. Previous sections have discussed callout registration by user libraries.
  375. It is possible for a component to register its own functions (i.e. within
  376. its own address space) as hook callouts. These functions are called
  377. in exactly the same way as user callouts, being passed their arguments
  378. though a CalloutHandle object. (Guidelines for writing callouts can be
  379. found in @ref hooksdgDevelopersGuide.)
  380. A component can associate with a hook callouts that run either before
  381. user-registered callouts or after them. Registration is done via a
  382. @c isc::hooks::LibraryHandle object, a reference to one being obtained
  383. through the methods @c isc::hooks::HooksManager::preCalloutLibraryHandle()
  384. (for a handle to register callouts to run before the user library
  385. callouts) or @c isc::hooks::HooksManager::postCalloutLibraryHandle() (for
  386. a handle to register callouts to run after the user callouts). Use of
  387. the @c LibraryHandle to register and deregister callouts is described in
  388. @ref hooksdgLibraryHandle.
  389. Finally, it should be noted that callouts registered in this way only
  390. remain registered until the next call to @c isc::hooks::loadLibraries().
  391. It is up to the component to re-register the callouts after this
  392. method has been called.
  393. */