hooks_component_developer.dox 21 KB

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