hook_user.dox 41.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
17
18
// Note: the prefix "hooksdg" to all labels is an abbreviation for "Hooks
// Developer's Guide" and is used to prevent a clash with symbols in any
// other Doxygen file.

19
/**
20
 @page hooksdgDevelopersGuide Hook Developer's Guide
21

22
 @section hooksdgIntroduction Introduction
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

Although the BIND 10 framework and its associated DNS and DHCP programs
provide comprehensive functionality, there will be times when it does
not quite do what you require: the processing has to be extended in some
way to solve your problem.

Since the BIND 10 source code is freely available (BIND 10 being an
open-source project), one option is to modify it to do what
you want.  Whilst perfectly feasible, there are drawbacks:

- Although well-documented, BIND 10 is a large program.  Just
understanding how it works will take a significant amount of time. In
addition, despite the fact that its object-oriented design keeps the
coupling between modules to a minimum, an inappropriate change to one
part of the program during the extension could cause another to
38
behave oddly or to stop working altogether.
39
40
41
42
43
44
45

- The change may need to be re-applied or re-written with every new
version of BIND 10.  As new functionality is added or bugs are fixed,
the code or algorithms in the core software may change - and may change
significantly.

To overcome these problems, BIND 10 provides the "Hooks" interface -
46
47
48
49
50
51
52
53
a defined interface for third-party or user-written code. (For ease of
reference in the rest of this document, all such code will be referred
to as "user code".)  At specific points in its processing
("hook points") BIND 10 will make a call to this code.  The call passes
data that the user code can examine and, if required, modify.
BIND 10 uses the modified data in the remainder of its processing.

In order to minimise the interaction between BIND 10 and the user
54
55
56
57
58
59
code, the latter is built independently of BIND 10 in the form of
a shared library (or libraries).  These are made known to BIND 10
through its configuration mechanism, and BIND 10 loads the library at
run time. Libraries can be unloaded and reloaded as needed while BIND
10 is running.

60
61
62
63
64
Use of a defined API and the BIND 10 configuration mechanism means that
as new versions of BIND 10 are released, there is no need to modify
the user code.  Unless there is a major change in an interface
(which will be clearly documented), all that will be required is a rebuild
of the libraries.
65
66

@note Although the defined interface should not change, the internals
67
68
69
of some of the classes and structures referenced by the user code may
change between versions of BIND 10.  These changes have to be reflected
in the compiled version of the software, hence the need for a rebuild.
70

71
@subsection hooksdgLanguages Languages
72
73

The core of BIND 10 is written in C++.  While it is the intention to
74
75
76
provide interfaces into user code written in other languages, the initial
versions of the Hooks system requires that user code be written in C++.
All examples in this guide are in that language.
77

78
@subsection hooksdgTerminology Terminology
79
80
81
82

In the remainder of this guide, the following terminology is used:

- Hook/Hook Point - used interchageably, this is a point in the code at
83
84
which a call to user functions is made. Each hook has a name and
each hook can have any number (including 0) of user functions
85
86
attached to it.

87
- Callout - a user function called by the server at a hook
88
point. This is so-named because the server "calls out" to the library
89
to execute a user function.
90

91
92
- Framework function - the functions that a user library needs to
supply in order for the hooks framework to load and unload the library.
93
94
95
96
97

- User code/user library - non-BIND 10 code that is compiled into a
shared library and loaded by BIND 10 into its address space.


98
@section hooksdgTutorial Tutorial
99
100
101
102
103
104
105
106
107
108
109

To illustrate how to write code that integrates with BIND 10, we will
use the following (rather contrived) example:

The BIND 10 DHCPv4 server is used to allocate IPv4 addresses to clients
(as well as to pass them other information such as the address of DNS
servers).  We will suppose that we need to classify clients requesting
IPv4 addresses according to their hardware address, and want to log both
the hardware address and allocated IP address for the clients of interest.

The following sections describe how to implement these requirements.
110
111
112
The code presented here is not efficient and there are better ways of
doing the task.  The aim however, is to illustrate the main features of
user hook code not to provide an optimal solution.
113
114


115
@subsection hooksdgFrameworkFunctions Framework Functions
116

117
Loading and initializing a library holding user code makes use
118
119
120
121
122
123
124
of three (user-supplied) functions:

- version - defines the version of BIND 10 code with which the user-library
is built
- load - called when the library is loaded by the server.
- unload - called when the library is unloaded by the server.

125
Of these, only "version" is mandatory, although our in our example, all three
126
127
are used.

128
@subsubsection hooksdgVersionFunction The "version" Function
129
130
131
132
133
134
135

"version" is used by the hooks framework to check that the libraries
it is loading are compatible with the version of BIND 10 being run.
Although the hooks system allows BIND 10 and user code to interface
through a defined API, the relationship is somewhat tight in that the
user code will depend on the internal structures of BIND 10.  If these
change - as they can between BIND 10 releases - and BIND 10 is run with
136
a version of user code built against an earlier version of BIND
137
138
10, a program crash could result.

139
140
141
142
143
To guard against this, the "version" function must be provided in every
library.  It returns a constant defined in header files of the version
of BIND 10 against which it was built.  The hooks framework checks this
for compatibility with the running version of BIND 10 before loading
the library.
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

In this tutorial, we'll put "version" in its own file, version.cc.  The
contents are:

@code
// version.cc

#include <hooks/hooks.h>

extern "C" {

int version() {
    return (BIND10_HOOKS_VERSION);
}

};
@endcode

162
163
164
165
166
The file "hooks/hooks.h" is specified relative to the BIND 10 libraries
source directory - this is covered later in the section @ref hooksdgBuild.
It defines the symbol BIND10_HOOKS_VERSION, which has a value that changes
on every release of BIND 10: this is the value that needs to be returned
to the hooks framework.
167
168
169
170
171
172
173

A final point to note is that the definition of "version" is enclosed
within 'extern "C"' braces.  All functions accessed by the hooks
framework use C linkage, mainly to avoid the name mangling that
accompanies use of the C++ compiler, but also to avoid issues related
to namespaces.

174
@subsubsection hooksdgLoadUnloadFunctions The "load" and "unload" Functions
175
176

As the names suggest, "load" is called when a library is loaded and
177
178
179
180
181
182
183
"unload" called when it is unloaded.  (It is always guaranteed that
"load" is called: "unload" may not be called in some circumstances,
e.g. if the system shuts down abnormally.)  These functions are the
places where any library-wide resources are allocated and deallocated.
"load" is also the place where any callouts with non-standard names
(names that are not hook point names) can be registered:
this is covered further in the section @ref hooksdgCalloutRegistration.
184
185
186
187

The example does not make any use callouts with non-standard names.  However,
as our design requires that the log file be open while BIND 10 is active
and the library loaded, we'll open the file in the "load" function and close
188
189
190
it in "unload".

We create two files, one for the file handle declaration:
191
192
193
194
195
196

@code
// library_common.h

#ifndef LIBRARY_COMMON_H
#define LIBRARY_COMMON_H
197

198
199
200
201
#include <fstream>

// "Interesting clients" log file handle declaration.
extern std::fstream interesting;
202
203

#endif // LIBRARY_COMMON_H
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
@endcode

... and one to hold the "load" and "unload" functions:

@code
// load_unload.cc

#include <hooks/hooks.h>
#include "library_common.h"

// "Interesting clients" log file handle definition.
std::fstream interesting;

extern "C" {

int load(LibraryHandle&) {
    interesting.open("/data/clients/interesting.log",
                     std::fstream::out | std::fstream::app);
    return (interesting ? 0 : 1);
}

int unload() {
    if (interesting) {
        interesting.close();
    }
    return (0);
}

};
@endcode

Notes:
- The file handle ("interesting") is declared in a header file and defined
outside of any function.  This means it can be accessed by any function
within the user library.  For convenience, the definition is in the
load_unload.cc file.
240
241
242
243
244
245
- "load" is called with a LibraryHandle argument, this being used in
the registration of functions.  As no functions are being registered
in this example, the argument specification omits the variable name
(whilst retaining the type) to avoid an "unused variable" compiler
warning. (The LibraryHandle and its use is discussed in the section
@ref hooksdgLibraryHandle.)
246
247
- In the current version of the hooks framework, it is not possible to pass
any configuration information to the "load" function.  The name of the log
248
249
250
file must therefore be hard-coded as an absolute path name or communicated
to the user code by some other means.
- "load" must 0 on success and non-zero on error.  The hooks framework
251
252
253
254
will abandon the loading of the library if "load" returns an error status.
(In this example, "interesting" can be tested as a boolean value,
returning "true" if the file opened successfully.)
- "unload" closes the log file if it is open and is a no-op otherwise. As
255
with "load", a zero value must be returned on success and a non-zero value
256
257
258
259
on an error.  The hooks framework will record a non-zero status return
as an error in the current BIND 10 log but otherwise ignore it.
- As before, the function definitions are enclosed in 'extern "C"' braces.

260
@subsection hooksdgCallouts Callouts
261
262
263

Having sorted out the framework, we now come to the functions that
actually do something.  These functions are known as "callouts" because
264
265
266
267
the BIND 10 code "calls out" to them.  Each BIND 10 server has a number of
hooks to which callouts can be attached: server-specific documentation
describes in detail the points in the server at which the hooks are
present together with the data passed to callouts attached to them.
268
269

Before we continue with the example, we'll discuss how arguments are
270
271
passed to callouts and information is returned to the server.  We will
also discuss how information can be moved between callouts.
272

273
@subsubsection hooksdgCalloutSignature The Callout Signature
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

All callouts are declared with the signature:
@code
extern "C" {
int callout(CalloutHandle& handle);
};
@endcode

(As before, the callout is declared with "C" linkage.)  Information is passed
between BIND 10 and the callout through name/value pairs in the CalloutHandle
object. The object is also used to pass information between callouts on a
per-request basis. (Both of these concepts are explained below.)

A callout returns an "int" as a status return.  A value of 0 indicates
success, anything else signifies an error.  The status return has no
effect on server processing; the only difference between a success
and error code is that if the latter is returned, the server will
log an error, specifying both the library and hook that generated it.
Effectively the return status provides a quick way for a callout to log
error information to the BIND 10 logging system.

295
@subsubsection hooksdgArguments Callout Arguments
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311

The CalloutHandle object provides two methods to get and set the
arguments passed to the callout.  These methods are called (naturally
enough) getArgument and SetArgument.  Their usage is illustrated by the
following code snippets.

@code
    // Server-side code snippet to show the setting of arguments

    int count = 10;
    boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value

    // Assume that "handle" has been created
    handle.setArgument("data_count", count);
    handle.setArgument("inpacket", pktptr);

312
    // Call the callouts attached to the hook
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    ...

    // Retrieve the modified values
    handle.getArgument("data_count", count);
    handle.getArgument("inpacket", pktptr);
@endcode

In the callout

@code
    int number;
    boost::shared_ptr<Pkt4> packet;

    // Retrieve data set by the server.
    handle.getArgument("data_count", number);
    handle.getArgument("inpacket", packet);

    // Modify "number"
    number = ...;

    // Update the arguments to send the value back to the server.
    handle.setArgument("data_count", number);
@endcode

As can be seen "getArgument" is used to retrieve data from the
CalloutHandle, and setArgument used to put data into it.  If a callout
wishes to alter data and pass it back to the server, it should retrieve
the data with getArgument, modify it, and call setArgument to send
it back.

There are several points to be aware of:

- the data type of the variable in the call to getArgument must match
the data type of the variable passed to the corresponding setArgument
<B>exactly</B>: using what would normally be considered to be a
"compatible" type is not enough.  For example, if the server passed
an argument as an "int" and the callout attempted to retrieve it as a
"long", an exception would be thrown even though any value that can
be stored in an "int" will fit into a "long".  This restriction also
applies the "const" attribute but only as applied to data pointed to by
pointers, e.g. if an argument is defined as a "char*", an exception will
be thrown if an attempt is made to retrieve it into a variable of type
"const char*".  (However, if an argument is set as a "const int", it can
be retrieved into an "int".)  The documentation of each hook point will
detail the data type of each argument.
- Although all arguments can be modified, some altered values may not
be read by the server. (These would be ones that the server considers
"read-only".) Consult the documentation of each hook to see whether an
argument can be used to transfer data back to the server.
- If a pointer to an object is passed to a callout (either a "raw"
pointer, or a boost smart pointer (as in the example above), and the
364
underlying object is altered through that pointer, the change will be
365
366
367
368
369
370
371
372
373
374
reflected in the server even if no call is made to setArgument.

In all cases, consult the documentation for the particular hook to see whether
parameters can be modified.  As a general rule:

- Do not alter arguments unless you mean the change to be reflected in
the server.
- If you alter an argument, call CalloutHandle::setArgument to update the
value in the CalloutHandle object.

375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
@subsubsection hooksdgSkipFlag The "Skip" Flag

When a to callouts attached to a hook returns, the server will usually continue
its processing.  However, a callout might have done something that means that
the server should follow another path.  Possible actions a server could take
include:

- Skip the next stage of processing because the callout has already
done it.  For example, a hook is located just before the DHCP server
allocates an address to the client.  A callout may decide to allocate
special addresses for certain clients, in which case it needs to tell
the server not to allocate an address in this case.
- Drop the packet and continue with the next request. A possible scenario
is a DNS server where a callout inspects the source address of an incoming
packet and compares it against a black list; if the address is on it,
the callout notifies the server to drop the packet.

To handle these common cases, the CalloutHandle has a "skip" flag.
This is set by a callout when it wishes the server to skip normal
processing.  It is set false by the hooks framework before callouts on a
hook are called.  If the flag is set on return, the server will take the
"skip" action relevant for the hook.

The methods to get and set the "skip" flag are getSkip and setSkip. Their
usage is intuitive:

@code
    // Get the current setting of the skip flag.
    bool skip = handle.getSkip();

    // Do some processing...
        :
    if (lease_allocated) {
        // Flag the server to skip the next step of the processing as we
        // already have an address.
        handle.setSkip(true);
    }
    return;
    
@endcode

Like arguments, the "skip" flag is passed to all callouts on a hook.  Callouts
later in the list are able to examine (and modify) the settings of earlier ones.

@subsubsection hooksdgCalloutContext Per-Request Context
420
421

Although many of the BIND 10 modules can be characterised as handling
422
singles packet - e.g. the DHCPv4 server receives a DISCOVER packet,
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
processes it and responds with an OFFER, this is not true in all cases.
The principal exception is the recursive DNS resolver: this receives a
packet from a client but that packet may itself generate multiple packets
being sent to upstream servers. To avoid possible confusion the rest of
this section uses the term "request" to indicate a request by a client
for some information or action.

As well as argument information, the CalloutHandle object can be used by
callouts to attach information to a request being handled by the server.
This information (known as "context") is not used by the server: its purpose
is to allow callouts to pass information between one another on a
per-request basis.

Context only exists only for the duration of the request: when a request
is completed, the context is destroyed.  A new request starts with no
context information.  Context is particularly useful in servers that may
439
440
be processing multiple requests simultaneously: callouts can effectively
attach data to a request that follows the request around the system.
441
442
443
444
445
446
447

Context information is held as name/value pairs in the same way
as arguments, being accessed by the pair of methods setContext and
getContext.  They have the same restrictions as the setArgument and
getArgument methods - the type of data retrieved from context must
<B>exactly</B> match the type of the data set.

448
The example in the next section illustrates their use.
449

450
@subsection hooksdgExampleCallouts Example Callouts
451
452
453
454
455
456
457
458
459
460
461
462
463

Continuing with the tutorial, the requirements need us to retrieve the
hardware address of the incoming packet, classify it, and write it,
together with the assigned IP address, to a log file.  Although we could
do this in one callout, for this example we'll use two:

- pkt_rcvd - a callout on this hook is invoked when a packet has been
received and has been parsed.  It is passed a single argument, "query"
which is an isc::dhcp::Pkt4 object (representing a DHCP v4 packet).
We will do the classification here.

- v4_lease_write_post - called when the lease (an assignment of an IPv4
address to a client for a fixed period of time) has been written to the
464
database. It is passed two arguments, the query ("query")
465
466
467
468
469
470
and the response (called "reply").  This is the point at which the
example code will write the hardware and IP addresses to the log file.

The standard for naming callouts is to give them the same name as
the hook.  If this is done, the callouts will be automatically found
by the Hooks system (this is discussed further in section @ref
471
hooksdgCalloutRegistration).  For our example, we will assume this is the
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
case, so the code for the first callout (used to classify the client's
hardware address) is:

@code
// pkt_rcvd.cc

#include <hooks/hooks.h>
#include <dhcp/pkt4.h>
#include "library_common.h"

#include <string>

using namespace isc::dhcp;
using namespace std;

extern "C" {

// This callout is called at the "pkt_rcvd" hook.
int pkt_rcvd(CalloutHandle& handle) {

    // A pointer to the packet is passed to the callout via a "boost" smart
    // pointer. The include file "pkt4.h" typedefs a pointer to the Pkt4
    // object as Pkt4Ptr.  Retrieve a pointer to the object.
    Pkt4Ptr query_ptr;
    handle.getArgument("query", query_ptr);

    // Point to the hardware address.
    HwAddrPtr hwaddr_ptr = query_ptr->getHWAddr();

    // The hardware address is held in a public member variable. We'll classify
    // it as interesting if the sum of all the bytes in it is divisible by 4.
    //  (This is a contrived example after all!)
    long sum = 0;
    for (int i = 0; i < hwaddr_ptr->hwaddr_.size(); ++i) {
        sum += hwaddr_ptr->hwadr_[i];
    }

    // Classify it.
    if (sum % 4 == 0) {
        // Store the text form of the hardware address in the context to pass
        // to the next callout.
        handle.setContext("hwaddr", hwaddr_ptr->hwaddr_.toText());
    }

    return (0);
};
@endcode

The pct_rcvd callout placed the hardware address of an interesting client in
the "hwaddr" context for the packet.  Turning now to the callout that will
write this information to the log file:

@code
// v4_lease_write.cc

#include <hooks/hooks.h>
#include <dhcp/pkt4.h>
#include "library_common.h"

#include <string>

using namespace isc::dhcp;
using namespace std;

extern "C" {

// This callout is called at the "v4_lease_write_post" hook.
int v4_lease_write_post(CalloutHandle& handle) {

    // Obtain the hardware address of the "interesting" client.  We have to
    // use a try...catch block here because if the client was not interesting,
    // no information would be set and getArgument would thrown an exception.
    string hwaddr;
    try (handle.getArgument("hwaddr", hwaddr) {

        // getArgument didn't throw so the client is interesting.  Get a pointer
548
549
550
551
        // to the reply.  Note that the argument list for this hook also
        // contains a pointer to the query: we don't need to access that in this
        // example.
        Pkt4Ptr reply;
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
        handle.getArgument("reply", reply);

        // Get the string form of the IP address.
        string ipaddr = reply->getYiaddr().toText();

        // Write the information to the log file.
        interesting << hwaddr << " " << ipaddr << "\n";

        // ... and to guard against a crash, we'll flush the output stream.
        flush(interesting);

    } catch (const NoSuchCalloutContext&) {

        // No such element in the per-request context with the name
        // "hwaddr".  We will do nothing, so just dismiss the exception.

    }

    return (0);
}

};
@endcode

576
@subsection hooksdgBuild Building the Library
577
578
579

Building the code requires building a shareable library.  This requires
the the code be compiled as positition-independent code (using the
580
581
582
compiler's "-fpic" switch) and linked as a shared library (with the
linker's "-shared" switch).  The build command also needs to point to
the BIND 10 include directory and link in the appropriate libraries.
583
584
585
586
587
588
589
590
591
592
593
594

Assuming that BIND 10 has been installed in the default location, the
command line needed to create the library using the Gnu C++ compiler on a
Linux system is:

@code
g++ -I /usr/include/bind10 -L /usr/lib/bind10 -fpic -shared -o example.so \
    load_unload.cc pkt_rcvd.cc v4_lease_write.cc version.cc \
    -lb10-dhcp++ -lb10-util -lb10-exceptions
@endcode

Notes:
595
- The compilation command and switches required may vary depending on
596
597
your operating system and compiler - consult the relevant documentation
for details.
598
599
600
- The values for the "-I" and "-L" switches depend on where you have
installed BIND 10.
- The list of libraries that need to be included in the command line
601
602
603
depends on the functionality used by the hook code and the module to
which they are attached (e.g. hook code for DNS will need to link against
the libb10-dns++ library).  Depending on operating system, you may also need
604
to explicitly list libraries on which the BIND 10 libraries depend.
605

606
@subsection hooksdgConfiguration Configuring the Hook Library
607
608
609
610
611
612
613
614
615

The final step is to make the library known to BIND 10.  All BIND 10 modules to
which hooks can be added contain the "hook_library" element, and user
libraries are added to this. (The BIND 10 hooks system can handle multiple libraries - this is discussed below.).

To add the example library (assumed to be in /usr/local/lib) to the DHCPv4
module, the following bindctl commands must be executed:

@code
616
617
> config add Dhcp4/hook_libraries
> config set Dhcp4/hook_libraries[0] "/usr/local/lib/example.so"
618
619
620
621
622
623
> config commit
@endcode

The DHCPv4 server will load the library and execute the callouts each time a
request is received.

624
@section hooksdgAdvancedTopics Advanced Topics
625

626
@subsection hooksdgContextCreateDestroy Context Creation and Destruction
627
628
629
630
631
632
633
634
635

As well as the hooks defined by the server, the hooks framework defines
two hooks of its own, "context_create" and "context_destroy".  The first
is called when a request is created in the server, before any of the
server-specific hooks gets called.  It's purpose it to allow a library
to initialize per-request context. The second is called after all
server-defined hooks have been processed, and is to allow a library to
tidy up.

636
637
638
639
640
641
642
643
As an example, the v4_lease_write example above required that the code
check for an exception being thrown when accessing the "hwaddr" context
item in case it was not set.  An alternative strategy would have been to
provide a callout for the "context_create" hook and set the context item
"hwaddr" to an empty string. Instead of needing to handle an exception,
v4_lease_write would be guaranteed to get something when looking for
the hwaddr item and so could write or not write the output depending on
the value.
644
645
646
647
648
649
650

In most cases, "context_destroy" is not needed as the Hooks system
automatically deletes context. An example where it could be required
is where memory has been allocated by a callout during the processing
of a request and a raw pointer to it stored in the context object. On
destruction of the context, that memory will not be automatically
released. Freeing in the memory in the "context_destroy callout will solve
651
652
653
that problem.

Actually, when the context is destroyed, the destructor
654
655
656
657
associated with any objects stored in it are run. Rather than point to
allocated memory with a raw pointer, a better idea would be to point to
it with a boost "smart" pointer and store that pointer in the context.
When the context is destroyed, the smart pointer's destructor is run,
658
which will automatically delete the pointed-to object.
659

660
661
662
663
664
665
666
These approaches are illustrated in the following examples.
Here it is assumed that the hooks library is performing some form of
security checking on the packet and needs to maintain information in
a user-specified "SecurityInformation" object. (The details of this
fictitious object are of no concern here.) The object is created in
the context_create callout and used in both the pkt4_rcvd and the
v4_lease_write_post callouts.
667

668
669
@code
// Storing information in a "raw" pointer.  Assume that the
670

671
672
673
674
#include <hooks/hooks.h>
   :

extern "C" {
675

676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
// context_create callout - called when the request is created.
int context_create(CalloutHandle& handle) {
    // Create the security information and store it in the context
    // for this packet.
    SecurityInformation* si = new SecurityInformation();
    handle.setContext("security_information", si);
}

// Callouts that use the context
int pktv_rcvd(CalloutHandle& handle) {
    // Retrieve the pointer to the SecurityInformation object
    SecurityInformation si;
    handle.getContext("security_information", si);
        :
        :
    // Set the security information
    si->setSomething(...);

    // The pointed-to information has been updated but the pointer has not been
    // altered, so there is no need to call setContext() again.
}

int v4_lease_write_post(CalloutHandle& handle) {
    // Retrieve the pointer to the SecurityInformation object
    SecurityInformation si;
    handle.getContext("security_information", si);
        :
        :
    // Retrieve security information
    bool active = si->getSomething(...);
        :
}

// Context destruction.  We need to delete the pointed-to SecurityInformation
// object because we will lose the pointer to it when the CalloutHandle is
// destroyed.
int context_destroy(CalloutHandle& handle) {
    // Retrieve the pointer to the SecurityInformation object
    SecurityInformation si;
    handle.getContext("security_information", si);

    // Delete the pointed-to memory.
    delete si;
}
@endcode

The requirement for the context_destroy callout can be eliminated if
a Boost shared ptr is used to point to the allocated memory:

@code
// Storing information in a "raw" pointer.  Assume that the

#include <hooks/hooks.h>
#include <boost/shared_ptr.hpp>
   :

extern "C" {

// context_create callout - called when the request is created.

int context_create(CalloutHandle& handle) {
    // Create the security information and store it in the context for this
    // packet.
    boost::shared_ptr<SecurityInformation> si(new SecurityInformation());
    handle.setContext("security_information", si);
}

// Other than the data type, a shared pointer has similar semantics to a "raw"
// pointer.  Only the code from pkt_rcvd is shown here.

int pktv_rcvd(CalloutHandle& handle) {
    // Retrieve the pointer to the SecurityInformation object
    boost::shared_ptr<SecurityInformation> si;
    handle.setContext("security_information", si);
        :
        :
    // Modify the security information
    si->setSomething(...);

    // The pointed-to information has been updated but the pointer has not
    // altered, so theree is no need to reset the context.
}

// No context_destroy callout is needed to delete the allocated
// SecurityInformation object.  When the CalloutHandle is destroyed, the shared
// pointer object will be destroyed.  If that is the last shared pointer to the
// allocated memory, then it too will be deleted.
@endcode

(Note that a Boost shared pointer - rather than any other Boost smart pointer -
should be used, as the pointer objects are copied within the hooks framework and
only shared pointers have the correct behavior for the copy operation.)


@subsection hooksdgCalloutRegistration Registering Callouts

As briefly mentioned in @ref hooksdgExampleCallouts, the standard is for
callouts in the user library to have the same name as the name of the
hook to which they are being attached.  This convention was followed
in the tutorial, e.g.  the callout that needed to be attached to the
"pkt_rcvd" hook was named pkt_rcvd.

The reason for this convention is that when the library is loaded, the
hook framework automatically searches the library for functions with
the same names as the server hooks.  When it finds one, it attaches it
to the appropriate hook point.  This simplifies the loading process and
bookkeeping required to create a library of callouts.
783
784
785
786

However, the hooks system is flexible in this area: callouts can have
non-standard names, and multiple callouts can be registered on a hook.

787
@subsubsection hooksdgLibraryHandle The LibraryHandle Object
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803

The way into the part of the hooks framework that allows callout
registration is through the LibraryHandle object.  This was briefly
introduced in the discussion of the framework functions, in that
an object of this type is pass to the "load" function.  A LibraryHandle
can also be obtained from within a callout by calling the CalloutHandle's
getLibraryHandle() method.

The LibraryHandle provides three methods to manipulate callouts:

- registerCallout - register a callout on a hook.
- deregisterCallout - deregister a callout from a hook.
- deregisterAllCallouts - deregister all callouts on a hook.

The following sections cover some of the ways in which these can be used.

804
@subsubsection hooksdgNonstandardCalloutNames Non-Standard Callout Names
805
806
807

The example in the tutorial used standard names for the callouts.  As noted
above, it is possible to use non-standard names.  Suppose, instead of the
808
callout names "pkt_rcvd" and "v4_lease_write", we had named our callouts
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
"classify" and "write_data".  The hooks framework would not have registered
these callouts, so we would have needed to do it ourself.  The place to
do this is the "load" framework function, and its code would have had to
been modified to:

@code
int load(LibraryHandle& libhandle) {
    // Register the callouts on the hooks. We assume that a header file
    // declares the "classify" and "write_data" functions.
    libhandle.registerCallout("pkt_rcvd", classify);
    libhandle.registerCallout("v4_lease_write", write_data);

    // Open the log file
    interesting.open("/data/clients/interesting.log",
                     std::fstream::out | std::fstream::app);
    return (interesting ? 0 : 1);
}
@endcode

It is possible for a library to contain callouts with both standard and
non-standard names: ones with standard names will be registered automatically,
ones with non-standard names need to be registered manually.

832
@subsubsection hooksdgMultipleCallouts Multiple Callouts on a Hook
833
834
835
836
837
838
839
840
841
842
843
844
845

The BIND 10 hooks framework allows multiple callouts to be attached to 
a hook point.  Although it is likely to be rare for user code to need to
do this, there may be instances where it make sense.

To register multiple callouts on a hook, just call
LibraryHandle::registerCallout multiple times on the same hook, e.g.

@code
    libhandle.registerCallout("pkt_rcvd", classify);
    libhandle.registerCallout("pkt_rcvd", write_data);
@endcode

846
847
848
849
The hooks framework will call the callouts in the order they are
registered.  The same CalloutHandle is passed between them, so any
change made to the CalloutHandle's arguments, "skip" flag, or per-request
context by the first is visible to the second.
850

851
@subsubsection hooksdgDynamicRegistration Dynamic Registration and Reregistration of Callouts
852
853
854

The previous sections have dealt with callouts being registered during
the call to "load".  The hooks framework is more flexible than that
855
in that callouts can be registered and deregistered within a callout.
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
In fact, a callout is able to register or deregister itself, and a callout
is able to be registered on a hook multiple times.

Using our contrived example again, the DHCPv4 server processes one request
to completion before it starts processing the next.  With this knowledge,
we could alter the logic of the code so that the callout attached to the
"pkt_rcvd" hook registers the callout doing the logging when it detects
an interesting packet, and the callout doing the logging deregisters
itself in its execution.  The relevant modifications to the code in
the tutorial are shown below:

@code
// pkt_rcvd.cc
//      :

int pkt_rcvd(CalloutHandle& handle) {

            :
            :

    // Classify it.
    if (sum % 4 == 0) {
878
879
880
881
882
        // Store the text form of the hardware address in the context to pass
        // to the next callout.
        handle.setContext("hwaddr", hwaddr_ptr->hwaddr_.toText());

        // Register the callback to log the data.
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
        handle.getLibraryHandle().registerCallout("v4_lease_write", write_data);
    }

    return (0);
};
@endcode

@code
// v4_lease_write.cc
        :

int write_data(CalloutHandle& handle) {

    // Obtain the hardware address of the "interesting" client. As the
    // callback is only registered when interesting data is present, we
    // know that the context contains the hardware address so an exception
    // will not be thrown when we call getArgument().
    string hwaddr;
    handle.getArgument("hwaddr", hwaddr);

    // The pointer to the reply.
    ConstPkt4Ptr reply;
    handle.getArgument("reply", reply);

    // Get the string form of the IP address.
    string ipaddr = reply->getYiaddr().toText():

    // Write the information to the log file and flush.
    interesting << hwaddr << " " << ipaddr << "\n";
    flush(interesting);

    // We've logged the data, so deregister ourself.  This callout will not
    // be called again until it is registered by pkt_rcvd.

    handle.getLibraryHandle().deregisterCallout("v4_lease_write", write_data);

    return (0);
}
@endcode

Note that the above example used a non-standard name for the callout
924
that wrote the data.  Had the name been a standard one, it would have been
925
926
927
928
929
930
931
932
933
registered when the library was loaded and called for the first request,
regardless of whether that was defined as "interesting".  (Although as
callouts with standard names are always registered before "load" gets called,
we could have got round that problem by deregistering that particular
callout in the "load" function.)


@note Deregistration of a callout on the hook that is currently
being called only takes effect when the server next calls the hook.
934
935
936
937
To illustrate this, suppose the callouts attached to a hook are A, B and C
(in that order), and during execution, A deregisters B and C and adds D.
When callout A returns, B and C will still run.  The next time the server
calls the hook's callouts, A and D will run (in that order).
938

939
@subsection hooksdgMultipleLibraries Multiple User Libraries
940

941
As alluded to in the section @ref hooksdgConfiguration, BIND 10 can load
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
multiple libraries.  The libraries are loaded in the order specified in
the configuration, and the callouts attached to the hooks in the order
presented by the libraries.

The following picture illustrates this, and also illustrates the scope of
data passed around the system.

@image html DataScopeArgument.png "Scope of Arguments"

In this illustration, a server has three hook points, alpha, beta
and gamma.  Two libraries are configured, library 1 and library 2.
Library 1 registers the callout "authorize" for hook alpha, "check" for
hook beta and "add_option" for hook gamma.  Library 2 registers "logpkt",
"validate" and "putopt"

The horizontal red lines represent arguments to callouts.  When the server
calls hook alpha, it creates an argument list and calls the
first callout for the hook, "authorize".  When that callout returns, the
same (but possibly modified) argument list is passed to the next callout
in the chain, "logpkt".  Another, separate argument list is created for
hook beta and passed to the callouts "check" and "validate" in
that order.  A similar sequence occurs for hook gamma.

The next picture shows the scope of the context associated with a
request.

@image html DataScopeContext.png "Illustration of per-library context"

The vertical blue lines represent callout context. Context is
per-packet but also per-library.  When the server calls "authorize",
the CalloutHandle's getContext and setContext methods access a context
created purely for library 1. The next callout on the hook will access
context created for library 2. These contexts are passed to the callouts
associated with the next hook.  So when "check" is called, it gets the
context data that was set by "authorize", when "validate" is called,
it gets the context data set by "logpkt".

It is stressed that the context for callouts associated with different
libraries is entirely separate.  For example, suppose "authorize" sets
the CalloutHandle's context item "foo" to 2 and "logpkt" sets an item of
the same name to the string "bar".  When "check" accesses the context
983
item "foo", it gets a value of 2; when "validate" accesses an item of
984
985
986
987
988
989
990
991
992
993
994
995
the same name, it gets the value "bar".

It is also stressed that all this context exists only for the life of the
request being processed.  When that request is complete, all the
context associated with that request - for all libraries - is destroyed,
and new context created for the next request.

This structure means that library authors can use per-request context
without worrying about the presence of other libraries.  Other libraries
may be present, but will not affect the context values set by a library's
callouts.

996
@subsection hooksdgInterLibraryData Passing Data Between Libraries
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014

In rare cases, it is possible that one library may want to pass
data to another.  This can be done in a limited way by means of the
CalloutHandle's setArgument and getArgument calls.  For example, in the
above diagram, the callout "add_option" can pass a value to "putopt"
by setting a name.value pair in the hook's argument list.  "putopt"
would be able to read this, but would not be able to return information
back to "add_option".

All argument names used by BIND 10 will be a combination of letters
(both upper- and lower-case), digits, hyphens and underscores: no
other characters will be used.  As argument names are simple strings,
it is suggested that if such a mechanism be used, the names of the data
values passed between the libraries include a special character such as
the dollar symbol or percent sign.  In this way there is no danger that
a name will conflict with any existing or future BIND 10 argument names.


1015
@subsection hooksdgRegisterMultipleLibraries Dynamic Callout Registration and Multiple Libraries
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031

On a particular hook, callouts are called in the order the libraries appear
in the configuration and, within a library, in the order the callouts
are registered.

This order applies to dynamically-registered callouts as well.  As an
example, consider the diagram above where for hook "beta", callout "check"
is followed by callout "validate".  Suppose that when "authorize" is run,
it registers a new callout ("double_check") on hook "beta".  That
callout will be inserted at the end of the callouts registered by
library 1 and before any registered by library 2.  It would therefore
appear between "check" and "validate".  On the other hand, if it were
"logpkt" that registered the new callout, "double_check" would appear
after "validate".

*/