hooks_component_developer.dox 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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 a dynamic shared object called here a shared library
  21. (even the concepts are different they are implemented in the same
  22. way with a common naming). The library is specified in the Kea
  23. configuration and at run time Kea dynamically loads the library
  24. into its address space. At various points in the processing, the component
  25. "calls out" to functions in the library, passing to them the data is it
  26. currently working on. They can examine and modify the data as required.
  27. This guide is aimed at Kea developers who want to write or modify a
  28. Kea component to use hooks. It shows how the component should be written
  29. to load a shared library at run-time and how to call functions in it.
  30. For information about writing a hooks library containing functions called by Kea
  31. during its execution, see the document @ref hooksdgDevelopersGuide.
  32. @subsection hooksComponentTerminology Terminology
  33. In the remainder of this guide, the following terminology is used:
  34. - Component - a Kea process, e.g. the DHCPv4 or DHCPv6 server.
  35. - Hook/Hook Point - used interchangeably, this is a point in the code at
  36. which a call to user-written functions is made. Each hook has a name and
  37. each hook can have any number (including 0) of user-written functions
  38. attached to it.
  39. - Callout - a user-written function called by the component at a hook
  40. point. This is so-named because the component "calls out" to the library
  41. to execute a user-written function.
  42. - User code/user library - non-Kea code that is compiled into a
  43. shared library and loaded by Kea into its address space. Multiple
  44. user libraries can be loaded at the same time, each containing callouts for
  45. the same hooks. The hooks framework calls these libraries one after the
  46. other. (See the document @ref hooksdgDevelopersGuide for more details.)
  47. @subsection hooksComponentLanguages Languages
  48. The core of Kea is written in C++ with some remaining legacy parts in Python.
  49. While it is the intention to provide the hooks framework for all languages,
  50. the initial version is for C++. All examples in this guide are in that language.
  51. @section hooksComponentBasicIdeas Basic Ideas
  52. From the point of view of the component author, the basic ideas of the hooks
  53. framework are quite simple:
  54. - The location of hook points in the code need to be determined.
  55. - Name the hook points and register them.
  56. - At each hook point, the component needs to complete the following steps to
  57. execute callouts registered by the user-library:
  58. -# copy data into the object used to pass information to the callout.
  59. -# call the callout.
  60. -# copy data back from the object used to exchange information.
  61. -# take action based on information returned.
  62. Of course, to set up the system the libraries need to be loaded in the first
  63. place. The component also needs to:
  64. - Define the configuration item that specifies the user libraries for this
  65. component.
  66. - Handle configuration changes and load/unload the user libraries.
  67. The following sections will describe these tasks in more detail.
  68. @section hooksComponentDefinition Determining the Hook Points
  69. Before any other action takes place, the location of the hook points
  70. in the code need to be determined. This of course depends on the
  71. component but as a general guideline, hook locations should be chosen
  72. where a callout is able to obtain useful information from Kea and/or
  73. affect processing. Typically this means at the start or end of a major
  74. step in the processing of a request, at a point where either useful
  75. information can be passed to a callout and/or the callout can affect
  76. the processing of the component. The latter is achieved in either or both
  77. of the following ways:
  78. - Setting the "skip" flag. This is a boolean flag that the callout can set
  79. and is a quick way of passing information back to the component. It is used
  80. to indicate that the component should skip the processing step associated with
  81. the hook. The exact action is up to the component, but is likely to be one
  82. of skipping the processing step (probably because the callout has
  83. done its own processing for the action) or dropping the current packet
  84. and starting on a new request.
  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 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 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 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 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 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 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) getArgument and 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 "getArgument" is used to retrieve data from the
  199. CalloutHandle, and "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 getArgument must
  205. match the data type of the variable passed to the corresponding
  206. 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 "int" and the component
  209. attempted to retrieve it as a "long", an exception would be thrown even
  210. though any value that can be stored in an "int" will fit into a "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 "char*",
  213. an exception will be thrown if an attempt is made to retrieve it into
  214. a variable of type "const char*". (However, if an argument is set as a
  215. "const int", it can be retrieved into an "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
  223. Although information is passed back to the component from callouts through
  224. CalloutHandle arguments, a common action for callouts is to inform the component
  225. that its flow of control should be altered. For example:
  226. - In the DHCP servers, there is a hook at the point at which a lease is
  227. about to be assigned. Callouts attached to this hooks may handle the
  228. lease assignment in special cases, in which case they set the skip flag
  229. to indicate that the server should not perform lease assignment in this
  230. case.
  231. - A server may define a hook just after a packet is received. A callout
  232. attached to the hook might inspect the source address and compare it
  233. against a blacklist. If the address is on the list, the callout could set
  234. the skip flag to indicate to the server that the packet should be dropped.
  235. For ease of processing, the CalloutHandle contains
  236. two methods, isc::hooks::CalloutHandle::getSkip() and
  237. isc::hooks::CalloutHandle::setSkip(). It is only meaningful for the
  238. component to use the "get" method. The skip flag is cleared by the hooks
  239. framework when the component requests that callouts be executed, so any
  240. value set by the component is lost. Callouts can both inspect the flag (it
  241. might have been set by callouts earlier in the callout list for the hook)
  242. and set it. Note that the setting of the flag by a callout does not
  243. prevent callouts later in the list from being called: the skip flag is
  244. just a boolean flag - the only significance comes from its interpretation
  245. by the component.
  246. An example of use could be:
  247. @code
  248. // Set up arguments for DHCP lease assignment.
  249. handle->setArgument("query", query);
  250. handle->setArgument("response", response);
  251. HooksManager::callCallouts(lease_hook_index, *handle_ptr);
  252. if (! handle_ptr->getSkip()) {
  253. // Skip flag not set, do the address allocation
  254. :
  255. }
  256. @endcode
  257. @subsection hooksComponentGettingHandle Getting the Callout Handle
  258. The CalloutHandle object is linked to the loaded libraries
  259. for lifetime reasons (described below). Components
  260. should retrieve a isc::hooks::CalloutHandle using
  261. isc::hooks::HooksManager::createCalloutHandle():
  262. @code
  263. CalloutHandlePtr handle_ptr = HooksManager::createCalloutHandle();
  264. @endcode
  265. (isc::hooks::CalloutHandlePtr is a typedef for a Boost shared pointer to a
  266. CalloutHandle.) The CalloutHandle so retrieved may be used for as
  267. long as the libraries are loaded.
  268. The handle is deleted by resetting the pointer:
  269. @code
  270. handle_ptr.reset();
  271. @endcode
  272. ... or by letting the handle pointer go out of scope. The actual deletion
  273. occurs when the CallHandle's reference count goes to zero. (The
  274. current version of the hooks framework does not maintain any other
  275. pointer to the returned CalloutHandle, so it gets destroyed when the
  276. shared pointer to it is cleared or destroyed. However, this may change
  277. in a future version.)
  278. @subsection hooksComponentCallingCallout Calling the Callout
  279. Calling the callout is a simple matter of executing the
  280. isc::hooks::HooksManager::callCallouts() method for the hook index in
  281. question. For example, with the hook index pkt_sent defined as above,
  282. the hook can be executed by:
  283. @code
  284. HooksManager::callCallouts(pkt_sent, *handle_ptr);
  285. @endcode
  286. ... where "*handle_ptr" is a reference (note: not a pointer) to the
  287. isc::hooks::CalloutHandle object holding the arguments. No status code
  288. is returned. If a component needs to get data returned (other than that
  289. provided by the "skip" flag), it should define an argument through which
  290. the callout can do so.
  291. @subsubsection hooksComponentConditionalCallout Conditionally Calling Hook Callouts
  292. Most hooks in a component will not have callouts attached to them. To
  293. avoid the overhead of setting up arguments in the CalloutHandle, a
  294. component can check for callouts before doing that processing using
  295. isc::hooks::HooksManager::calloutsPresent(). Taking the index of a
  296. hook as its sole argument, the function returns true if there are any
  297. callouts attached to the hook and false otherwise.
  298. With this check, the code in the component for calling a hook would look
  299. something like:
  300. @code
  301. if (HooksManager::calloutsPresent(lease_hook_index)) {
  302. // Set up arguments for lease assignment
  303. handle->setArgument("query", query);
  304. handle->setArgument("response", response);
  305. HooksManager::callCallouts(lease_hook_index, *handle);
  306. if (! handle->getSkip()) {
  307. // Skip flag not set, do the address allocation
  308. :
  309. }
  310. }
  311. @endcode
  312. @section hooksComponentLoadLibraries Loading the User Libraries
  313. Once hooks are defined, all the hooks code described above will
  314. work, even if no libraries are loaded (and even if the library
  315. loading method is not called). The CalloutHandle returned by
  316. isc::hooks::HooksManager::createCalloutHandle() will be valid,
  317. isc::hooks::HooksManager::calloutsPresent() will return false for every
  318. index, and isc::hooks::HooksManager::callCallouts() will be a no-op.
  319. However, if user libraries are specified in the Kea configuration,
  320. the component should load them. (Note the term "libraries": the hooks
  321. framework allows multiple user libraries to be loaded.) This should take
  322. place after the component's configuration has been read, and is achieved
  323. by the isc::hooks::HooksManager::loadLibraries() method. The method is
  324. passed a vector of strings, each giving the full file specification of
  325. a user library:
  326. @code
  327. std::vector<std::string> libraries = ... // Get array of libraries
  328. bool success = HooksManager::loadLibraries(libraries);
  329. @endcode
  330. loadLibraries() returns a boolean status which is true if all libraries
  331. loaded successfully or false if one or more failed to load. Appropriate
  332. error messages will have been logged in the latter case, the status
  333. being more to allow the developer to decide whether the execution
  334. should proceed in such circumstances.
  335. If loadLibraries() is called a second or subsequent time (as a result
  336. of a reconfiguration), all existing libraries are unloaded and a new
  337. set loaded. Libraries can be explicitly unloaded either by calling
  338. isc::hooks::HooksManager::unloadLibraries() or by calling
  339. loadLibraries() with an empty vector as an argument.
  340. @subsection hooksComponentUnloadIssues Unload and Reload Issues
  341. Unloading a shared library works by unmapping the part of the process's
  342. virtual address space in which the library lies. This may lead to
  343. problems if there are still references to that address space elsewhere
  344. in the process.
  345. In many operating systems, heap storage allowed by a shared library will
  346. lie in the virtual address allocated to the library. This has implications
  347. in the hooks framework because:
  348. - Argument information stored in a CalloutHandle by a callout in a library
  349. may lie in the library's address space.
  350. - Data modified in objects passed as arguments may lie in the address
  351. space. For example, it is common for a DHCP callout to add "options"
  352. to a packet: the memory allocated for those options will most likely
  353. lie in library address space.
  354. The problem really arises because of the extensive use by Kea of boost
  355. smart pointers. When the pointer is destroyed, the pointed-to memory is
  356. deallocated. If the pointer points to address space that is unmapped because
  357. a library has been unloaded, the deletion causes a segmentation fault.
  358. The hooks framework addresses the issue for CalloutHandles by keeping in
  359. that object a shared pointer to the object controlling library unloading.
  360. Although a library can be unloaded at any time, it is only when all
  361. CalloutHandles that could possibly reference address space in the library
  362. have been deleted that the library will actually be unloaded and the
  363. address space unmapped.
  364. The hooks framework cannot solve the second issue as the objects in
  365. question are under control of the component. It is up to the component
  366. developer to ensure that all such objects have been destroyed before
  367. libraries are reloaded. In extreme cases this may mean the component
  368. suspending all processing of incoming requests until all currently
  369. executing requests have completed and data object destroyed, reloading
  370. the libraries, then resuming processing.
  371. @section hooksComponentCallouts Component-Defined Callouts
  372. Previous sections have discussed callout registration by user libraries.
  373. It is possible for a component to register its own functions (i.e. within
  374. its own address space) as hook callouts. These functions are called
  375. in exactly the same way as user callouts, being passed their arguments
  376. though a CalloutHandle object. (Guidelines for writing callouts can be
  377. found in @ref hooksdgDevelopersGuide.)
  378. A component can associate with a hook callouts that run either before
  379. user-registered callouts or after them. Registration is done via a
  380. isc::hooks::LibraryHandle object, a reference to one being obtained
  381. through the methods isc::hooks::HooksManager::preCalloutLibraryHandle()
  382. (for a handle to register callouts to run before the user library
  383. callouts) or isc::hooks::HooksManager::postCalloutLibraryHandle() (for
  384. a handle to register callouts to run after the user callouts). Use of
  385. the LibraryHandle to register and deregister callouts is described in
  386. @ref hooksdgLibraryHandle.
  387. Finally, it should be noted that callouts registered in this way only
  388. remain registered until the next call to isc::hooks::loadLibraries().
  389. It is up to the component to re-register the callouts after this
  390. method has been called.
  391. */