dhcp6.dox 30.5 KB
Newer Older
1
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
2
//
3
4
5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7
/**
8
 @page dhcp6 DHCPv6 Server Component
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Kea includes the "kea-dhcp6" component, which is the DHCPv6 server
implementation. This component is built around the
@ref isc::dhcp::Dhcpv6Srv class which controls all major operations
performed by the server such as: DHCP messages processing, callouts
execution for many hook points, FQDN processing and interactions with the
"kea-dhcp-ddns" component, lease allocation, system signals handling etc.

The "kea-dhcp6" component requires linking with many different libraries
to obtain access to common functions like: interfaces and sockets
management, configuration parsing, leases management and allocation,
hooks infrastructure, statistics management etc.

The following sections walk through some of the details of the "kea-dhcp6"
component implementation.

@section dhcpv6ConfigParser Configuration Parsers in DHCPv6

Tomek Mrugalski's avatar
Tomek Mrugalski committed
27
28
29
30
31
32
33
34
35
36
Three minutes overview. If you are here only to learn absolute minimum about
the new parser, here's how you use it:

@code
 // The following code:
 json = isc::data::Element::fromJSONFile(file_name, true);

 // can be replaced with this:
 Parser6Context parser;
 json = parser.parseFile(file_name, Parser6Context::PARSER_DHCP6);
37
@endcode
38

39
40
Note: parsers are currently being migrated to @ref isc::data::SimpleParser. See
@ref ccSimpleParser page for details.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
41

42
43
44
45
46
The common configuration parsers for the DHCP servers are located in the
src/lib/dhcpsrv/parsers/ directory. Parsers specific to the DHCPv6 component
are located in the src/bin/dhcp6/json_config_parser.cc. These parsers derive
from the common configuration parsers and customize their behavior. For
example: the @c Subnet6ConfigParser is used to parse parameters
47
describing a single subnet. It derives from the @c
48
isc::dhcp::SubnetConfigParser, which implements the common base for both
49
DHCPv4 and DHCPv6 subnets. The @ref Subnet6ConfigParser
50
51
52
53
54
55
56
57
58
implements the @c initSubnet abstract method, which creates an instance of
the DHCPv6 subnet. This method is invoked by the parent class.

Some parsers for the DHCPv6 server derive from the isc::dhcp::DhcpConfigParser
class directly. This is an abstract class, defining a basic interface for
all configuration parsers. All DHCPv6 parsers deriving from this class
directly have their entire implementation in the
src/bin/dhcp6/json_config_parser.cc.

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@section dhcpv6ConfigParserBison Configuration Parser for DHCPv6 (bison)

During 1.2 milestone it has been decided to significantly refactor the
parsers as their old implementation became unsustainable. For the brief overview
of the problems, see ticket 5014 (http://kea.isc.org/ticket/5014). In
general, the following issues of the existing code were noted:

-# parsers are overwhelmingly complex. Even though each parser is relatively
   simple class, the complexity comes from too large number of interacting parsers.
-# the code is disorganized, i.e. spread out between multiple directories
   (src/bin/dhcp6 and src/lib/dhcpsrv/parsers).
-# The split into build/commit never worked well. In particular, it is not
   trivial to revert configuration change. This split originates from BIND10
   days and was inherited from DNS auth that did receive only changes in
   the configuration, rather than the full configuration. As a result,
   the split was abused and many of the parsers have commit() being a
   no-op operation.
-# There is no way to generate a list of all directives. We do have .spec files,
   but they're not actually used by the code. The code has the directives
Francis Dupont's avatar
Francis Dupont committed
78
   spread out in multiple places in multiple files in multiple directories.
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
   Answering a simple question ("can I do X in the scope Y?") requires
   a short session of reverse engineering. What's worse, we have the .spec
   files that are kinda kept up to date. This is actually more damaging that
   way, because there's no strict correlation between the code and .spec file.
   So there may be parameters that are supported, but are not in .spec files.
   The opposite is also true - .spec files can be buggy and have different
   parameters. This is particularly true for default values.
-# It's exceedingly complex to add comments that don't start at the first
   column or span several lines. Both Tomek and Marcin tried to implement
   it, but failed miserably. The same is true for including files (have
   include statement in the config that includes other files)
-# The current parsers don't handle the default values, i.e. if there's no
   directive, the parser is not created at all. We have kludgy workarounds
   for that, but the code for it is in different place than the parser,
   which leads to the logic being spread out in different places.
-# syntax checking is poor. The existing JSON parser allowed things like
   empty option-data entries:
   @code
      "option-data": [ {} ]
   @endcode
   having trailing commas:
   @code
      "option-data": [
102
103
104
105
        {
            "code": 12,
            "data": "2001:db8:1:0:ff00::1"
        },
106
107
108
109
110
111
112
113
114
      ]
   @endcode
   or having incorrect types, e.g. specifying timer values as strings.

To solve those issues a two phase approach was proposed:

PHASE 1: replace isc::data::fromJSON with bison-based parser. This will allow
   to have a single file that defines the actual syntax, much better syntax
   checking, and provide more flexibility, like various comment types and
Tomek Mrugalski's avatar
Tomek Mrugalski committed
115
116
117
   file inclusions. As a result, the parser still returns JSON structures that
   are guaranteed to be correct from the grammar perspective. Sticking with
   the JSON structures also allows us to continue using existing parsers.
118
119
   Furthermore, it is possible to implement default values at this level
   as simply inserting extra JSON structures in places that are necessary.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
120
   This part is covered by ticket 5036.
121
122
123

PHASE 2: simplify existing parsers by getting rid of the build/commit split.
   Get rid of the inheritance contexts. Essentially the parser should
Tomek Mrugalski's avatar
Tomek Mrugalski committed
124
   take JSON structure as a parameter and return the configuration structure.
125
126
127
128
   For example, for options this should essentially look like this:
   @code
   CfgOptionPtr parse(ConstElementPtr options)
   @endcode
Tomek Mrugalski's avatar
Tomek Mrugalski committed
129
130
131
132
   The whole complexity behind inheriting contexts should be removed
   from the existing parsers  and implemented in the bison parser.
   It should return extra JSON elements. The details are TBD, but there is
   one example for setting up an renew-timer value on the subnet level that
Francis Dupont's avatar
Francis Dupont committed
133
   is inherited from the global ("Dhcp6") level. This phase is covered by
Tomek Mrugalski's avatar
Tomek Mrugalski committed
134
135
   ticket 5039.

Francis Dupont's avatar
Francis Dupont committed
136
The code change for 5036 introduces flex/bison based parser. It is
Tomek Mrugalski's avatar
Tomek Mrugalski committed
137
138
essentially defined in two files: dhcp6_lexer.ll, which defines
regular expressions that are used on the input (be it a file or a
Francis Dupont's avatar
Francis Dupont committed
139
string in memory). In essence, this code is being called repeatedly
Tomek Mrugalski's avatar
Tomek Mrugalski committed
140
141
142
143
and each time it returns a token. This repeats until either the
parsing is complete or syntax error is encountered. For example, for
the following text:

144
145
146
147
148
149
150
151
152
@code
{
   "Dhcp6":
   {
       "renew-timer": 100
   }
}
@endcode
this code would return the following sentence of tokens: LCURLY_BRACKET,
Francis Dupont's avatar
Francis Dupont committed
153
154
DHCP6, COLON, LCURLY_BRACKET, RENEW_TIMER, COLON, INTEGER
(a token with a value of 100), RCURLY_BRACKET, RCURLY_BRACKET, END
155
156
157
158
159
160
161
162
163

This stream of tokens is being consumed by the parser that is defined
in dhcp6_parser.yy. This file defines a grammar. Here's very simplified
version of the Dhcp6 grammar:

@code
dhcp6_object: DHCP6 COLON LCURLY_BRACKET global_params RCURLY_BRACKET;

global_params: global_param
Francis Dupont's avatar
Francis Dupont committed
164
165
             | global_params COMMA global_param
             ;
166
167
168

// These are the parameters that are allowed in the top-level for
// Dhcp6.
Francis Dupont's avatar
Francis Dupont committed
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
global_param: preferred_lifetime
            | valid_lifetime
            | renew_timer
            | rebind_timer
            | subnet6_list
            | interfaces_config
            | lease_database
            | hosts_database
            | mac_sources
            | relay_supplied_options
            | host_reservation_identifiers
            | client_classes
            | option_data_list
            | hooks_libraries
            | expired_leases_processing
            | server_id
            | dhcp4o6_port
            ;
187
188
189
190
191
192
193
194
195

renew_timer: RENEW_TIMER COLON INTEGER;
@endcode

This may be slightly difficult to read at the beginning, but after getting used
to the notation, it's very powerful and easy to extend. The first line defines
that dhcp6_object consists of certain tokens (DHCP6, COLON and LCURLY_BRACKET)
followed by 'global_params' expression, followed by RCURLY_BRACKET.

Francis Dupont's avatar
Francis Dupont committed
196
197
198
199
The 'global_params' is defined recursively. It can either be a single
'global_param' expression, or (a shorter) global_params followed by a
comma and global_param.  Bison will apply this and will be able to
parse comma separated lists of arbitrary lengths.
200

Francis Dupont's avatar
Francis Dupont committed
201
202
203
204
205
A single parameter is defined by 'global_param' expression. This
represents any parameter that may appear in the global scope of Dhcp6
object. The complete definition for all of them is complex, but the
example above includes renew_timer definition. It is defined as a
series of RENEW_TIMER, COLON, INTEGER tokens.
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
240
241
242

The above is a simplified version of the actual grammar. If used in the version
above, it would parse the whole file, but would do nothing with that information.
To build actual structures, bison allows to inject C++ code at any phase of
the parsing. For example, when the parser detects Dhcp6 object, it wants to
create a new MapElement. When the whole object is parsed, we can perform
some sanity checks, inject defaults for parameters that were not defined,
log and do other stuff.

@code
dhcp6_object: DHCP6 COLON LCURLY_BRACKET {
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("Dhcp6", m);
    ctx.stack_.push_back(m);
} global_params RCURLY_BRACKET {
    // Whole Dhcp6 parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking, insert defaults if not specified),
    // this would be the best place for it.
    ctx.stack_.pop_back();
};
@endcode

The above will do the following in order: consume DHCP6 token, consume COLON token,
consume LCURLY_BRACKET, execute the code in first { ... }, parse global_params
and do whatever the code for it tells, parser RCURLY_BRACKET, execute the code
in the second { ... }.

There is a simple stack defined in ctx.stack_, which is isc::dhcp::Parser6Context
defined in src/bin/dhcp6/parser_context.h. When walking through the config file, each
new context (e.g. entering into Dhcp6, Subnet6, Pool), a new Element is added
to the end of the stack. Once the parsing of a given context is complete, it
is removed from the stack. At the end of parsing, there should be a single
element on the stack as the top-level parsing (syntax_map) only inserts the
MapElement object, but does not remove it.

Francis Dupont's avatar
Francis Dupont committed
243
@section dhcpv6ConfigSubParser Parsing Partial Configuration in DHCPv6
Tomek Mrugalski's avatar
Tomek Mrugalski committed
244

245
One another important capability required is the ability to parse not only the
Francis Dupont's avatar
Francis Dupont committed
246
whole configuration, but a subset of it. This is done by introducing artificial
Tomek Mrugalski's avatar
Tomek Mrugalski committed
247
248
249
250
tokens (e.g. TOPLEVEL_JSON and TOPLEVEL_DHCP6). For complete list of available
starting contexts, see @ref isc::dhcp::Parser6Context::ParserType. The
Parse6Context::parse() method takes one parameter that specifies, whether the
data to be parsed is expected to have a generic JSON or the whole configuration
Tomek Mrugalski's avatar
Tomek Mrugalski committed
251
252
253
(DHCP6). This feature is currently mostly used by unit-tests (which often skip
the '{ "Dhcp6": {' preamble), but it is expected to be soon used for parsing
subnets only, host reservations only, options or basically any other elements.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
254
For example, to add the ability to parse only pools, the following could be added:
255
256

@code
Tomek Mrugalski's avatar
Tomek Mrugalski committed
257
start: TOPLEVEL_GENERIC_JSON sub_json
Francis Dupont's avatar
Francis Dupont committed
258
259
260
     | TOPLEVEL_DHCP6 sub_dhcp6
     | TOPLEVEL_POOL6 sub_pool6
     ;
261
262
@endcode

Tomek Mrugalski's avatar
Tomek Mrugalski committed
263
264
265
266
267
268
The parser code contains the code definition and the Kea-dhcp6 updated
to use that new parser. That parser is able to to load all examples
from doc/example/kea6. It is also able to parser # comments (bash
style, starting at the beginning or middle of the line), // comments
(C++ style, can start anywhere) or / * * / comments (C style, can span
multiple lines).
269

Tomek Mrugalski's avatar
Tomek Mrugalski committed
270
271
This parser is currently used in production code. See configure()
method in kea_controller.cc.
272

Tomek Mrugalski's avatar
Tomek Mrugalski committed
273
274
275
276
277
278
279
There are several new unit-tests written, but the code mostly reuses existing
one to verify that existing functionality was not compromised. There are
several new interesting ones, though. ParserTest.file loads all the
config file examples we have in doc/examples/kea6. This ensures that the
parser is able to load them and also checks that our examples are sane.

@section dhcp6ParserIncludes Config File Includes
280

Tomek Mrugalski's avatar
Tomek Mrugalski committed
281
282
283
284
The new parser provides an ability to include files. The syntax was chosen
to look similar to how Apache includes PHP scripts in HTML code. This
particular syntax was chosen to emphasize that the inclusion directive
is an additional feature and not really a part of JSON syntax.
285

Tomek Mrugalski's avatar
Tomek Mrugalski committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
To include one file from another, user the following syntax:

@code
{
    "Dhcp6": {
        "interfaces-config": {
            "interfaces": [ "*" ]},
        "preferred-lifetime": 3000,
        "rebind-timer": 2000,
        "renew-timer": 1000,
        <?include "subnets.json"?>
        "valid-lifetime": 4000
     }
}
@endcode
301

Francis Dupont's avatar
Francis Dupont committed
302
303
304
305
306
307
The inclusion is implemented as a stack of files. Typically, when a
single file is parsed, the files_ (a vector of strings) and sfiles_ (a
vector of FILE*) both contain a single entry. However, when lexer
detects &lt;?include "filename.json?&gt;, it calls
@ref isc::dhcp::Parser6Context::includeFile method. Up to ten nesting
levels are supported. This arbitrarily chosen limit is a protection
Tomek Mrugalski's avatar
Tomek Mrugalski committed
308
309
310
311
312
313
314
315
316
against recursive inclusions.

@section dhcp6ParserConflicts Avoiding syntactical conflicts in parsers

Syntactic parser has a powerful ability to not only parse the string and
check if it's a valid JSON syntax, but also check that the resulting structures
match expected syntax (if subnet6 are really an array, not a map, if
timers are expressed as integers, not as strings etc.).

Tomek Mrugalski's avatar
Tomek Mrugalski committed
317
318
319
320
321
322
323
324
325
326
However, there are times when we need to parse a string as arbitrary
JSON.  For example, if we're in Dhcp6 and the config contains entries
for DhcpDdns or Dhcp4. If we were to use naive approach, the lexer
would go through that content and most likely find some tokens that
are also used in Dhcp6.  for example 'renew-timer' would be detected
and the parser would complain that it was not expected. To avoid this
problem, syntactic context was introduced. When the syntactic parser
expects certain type of content, it calls @ref
isc::dhcp::Parser6Context::enter() method to indicate what type of
content is expected. For example, when Dhcp6 parser discovers
Tomek Mrugalski's avatar
Tomek Mrugalski committed
327
328
329
uninteresting content like Dhcp4, it enters NO_KEYWORD mode. In this
mode, everything is parsed as generic maps, lists, integers, booleans
or strings. This results in generic JSON structures without any syntax
Tomek Mrugalski's avatar
Tomek Mrugalski committed
330
331
332
333
334
335
336
337
338
339
checking. A corresponding/balanced @ref
isc::dhcp::Parser6Context::leave() call is required before leaving the
context to come back to the previous context.

Entering a new syntactic context is useful in several ways.  First, it allows
the parser to avoid conflicts. Second, it allows the lexer to return different
tokens depending on context (e.g. if "renew-timer" string is detected, the lexer
will return STRING token if in JSON mode or RENEW_TIMER if in DHCP6
mode). Finally, the syntactic context allows the error message to be more
descriptive if the input string does not parse properly.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
340
341
342

Details of the refactor of the classes derived from DhcpConfigParser are
documented in http://kea.isc.org/wiki/SimpleParser.
343

344
345
346
@section dhcpv6ConfigInherit DHCPv6 Configuration Inheritance

One notable useful feature of DHCP configuration is its parameter inheritance.
347
For example, the "renew-timer" value may be specified at a global scope and it then
348
349
350
351
352
353
354
355
356
357
358
applies to all subnets. However, some subnets may have it overwritten with subnet
specific values that takes precedence over global values that are considered
defaults. The parameters inheritance is implemented by means of the "global
context". The global context is represented by the @ref isc::dhcp::ParserContext
class and it holds pointers to storages of different kind, e.g. text parameters,
numeric parameters etc. When the server is parsing the top level configuration
parameters it passes pointers to the storages of the appropriate kind, to the
parsers being invoked to parse the global values. Parsers will store the
parsed values into these storages. Once the global parameters are stored in the
global context, the parsers for the nested configuration parameters are invoked.
These parsers check the presence of the parameters overriding the values of
359
the global parameters. If a value is not present, the value from the global
360
361
362
363
364
365
366
367
368
369
370
371
372
context is used.

A good example of inheritance is the implementation of the @ref
isc::dhcp::SubnetConfigParser. The @c getParam method is used throughout the
class to obtain values of the parameters defining a subnet. It first checks
if the specific value is present in the local values storage. If it is not
present, it uses the value from the global context.

@code
isc::dhcp::Triplet<uint32_t>
SubnetConfigParser::getParam(const std::string& name) {
    uint32_t value = 0;
    try {
373
374
        // look for local value
         value = uint32_values_->getParam(name);
375
    } catch (const DhcpConfigError &) {
376
377
378
379
380
381
382
        try {
            // no local, use global value
            value = global_context_->uint32_values_->getParam(name);
        } catch (const DhcpConfigError &) {
            isc_throw(DhcpConfigError, "Mandatory parameter " << name
                      << " missing (no global default and no subnet-"
                      << "specific value)");
383
        }
384
385
386
387
388
389
390
    }

    return (Triplet<uint32_t>(value));
}
@endcode

Note that if the value is neither present in the local storage nor in the global
391
context, an error is signaled.
392
393
394
395
396
397
398
399

Parameter inheritance is done once, during the reconfiguration phase.
Reconfigurations are rare, so extra logic here is not a problem. On the other
hand, values of those parameters may be used thousands times per second, so
access to these parameters must be as efficient as possible. In fact,
currently the code has to only call @c Subnet6::getT1(), regardless if the
"renew-timer" has been specified as a global or subnet specific value.

400
Debugging a configuration parser may be confusing. Therefore there is a special
401
class called DebugParser. It does not configure anything, but just
402
accepts any parameter of any type. If requested to commit a configuration, it will
403
404
405
print out received parameter name and its value. This class is not currently used,
but it is convenient to have it every time a new parameter is added to DHCP
configuration. For that purpose it should be left in the code.
406

407
408
409
@section dhcpv6DDNSIntegration DHCPv6 Server Support for the Dynamic DNS Updates

The DHCPv6 server supports processing of the DHCPv6 Client FQDN Option described in
410
the RFC4704. This Option is sent by the DHCPv6 client to instruct the server to
411
412
413
414
415
update the DNS mappings for the acquired lease. A client may send its fully
qualified domain name, a partial name or it may choose that server will generate
the name. In the last case, the client sends an empty domain-name field in the
DHCPv6 Client FQDN Option.

416
As described in RFC4704, client may choose that the server delegates the forward
417
418
419
420
DNS update to the client and that the server performs the reverse update only. Current
version of the DHCPv6 server does not support delegation of the forward update
to the client. The implementation of this feature is planned for the future releases.

421
422
The kea-dhcp-ddns process is responsible for the actual communication with the DNS
server, i.e. to send DNS Update messages. The kea-dhcp6 module is responsible
423
for generating so called @ref isc::dhcp_ddns::NameChangeRequest and sending it to the
424
kea-dhcp-ddns module. The @ref isc::dhcp_ddns::NameChangeRequest object represents changes to the
425
DNS bindings, related to acquisition, renewal or release of the lease. The kea-dhcp6
426
427
428
429
430
431
432
module implements the simple FIFO queue of the NameChangeRequest objects. The module
logic, which processes the incoming DHCPv6 Client FQDN Options puts these requests
into the FIFO queue.

@todo Currently the FIFO queue is not processed after the NameChangeRequests are
generated and added to it. In the future implementation steps it is planned to create
a code which will check if there are any outstanding requests in the queue and
433
send them to the kea-dhcp-ddns module when server is idle waiting for DHCP messages.
434

435
In the simplest case, when client gets one address from the server, a DHCPv6 server
436
may generate 0, 1 or 2 NameChangeRequests during single message processing.
437
The server generates no NameChangeRequests if it is not configured to update DNS
438
 or it rejects the DNS update for any other reason.
439

440
The server may generate one NameChangeRequest in a situation when a client acquires a
441
new lease or it releases an existing lease. In the former case, the NameChangeRequest
442
type is CHG_ADD, which indicates that the kea-dhcp-ddns module should add a new DNS
443
444
binding for the client, and it is assumed that there is no DNS binding for this
client already. In the latter case, the NameChangeRequest type is CHG_REMOVE to
445
indicate to the kea-dhcp-ddns module that the existing DNS binding should be removed
446
447
from the DNS. The binding consists of the forward and reverse mapping.
A server may only remove the mapping which it had added. Therefore, the lease database
448
449
450
451
holds an information which updates (no update, reverse only update, forward only update,
both reverse and forward update) have been performed when the lease was acquired.
Server checks this information to make a decision which mapping it is supposed to
remove when a lease is released.
452

453
The server may generate two NameChangeRequests in case the client is renewing a lease and
454
455
456
it already has a DNS binding for that lease. Note, that renewal may be triggered
as a result of sending a RENEW message as well as the REQUEST message. In both cases
DHCPv6 server will check if there is an existing lease for the client which has sent
457
a message, and it will check in the lease database if the DNS Updates had
458
459
460
461
462
463
464
been performed for this client. If the notion of client's FQDN changes comparing to
the information stored in the lease database, the DHCPv6 has to remove an existing
binding from the DNS and then add a new binding according to the new FQDN information
received from the client. If the FQDN sent in the message which triggered a renewal
doesn't change (comparing to the information in the lease database) the NameChangeRequest
is not generated.

465
466
467
468
In the more complex scenarios, when server sends multiple IA_NA options, each holding
multiple IAADDR options, server will generate more NameChangeRequests for a single
message being processed. That is 0, 1, 2 for the individual IA_NA. Generation of
the distinct NameChangeRequests for each IADDR is not supported yet.
469

470
The DHCPv6 Client FQDN Option comprises "NOS" flags which communicate to the
471
server what updates (if any) client expects the server to perform. Server
472
473
474
475
476
may be configured to obey client's preference or do FQDN processing in a
different way. If the server overrides client's preference it will communicate it
by sending the DHCPv6 Client FQDN Option in its responses to a client, with
appropriate flags set.

477
478
479
480
481
482
483
@section dhcpv6OptionsParse Custom functions to parse message options

The DHCPv6 server implementation provides a generic support to define option
formats and set option values. A number of options formats have been defined
for standard options in libdhcp++. However, the formats for vendor specific
options are dynamically configured by the server's administrator and thus can't
be stored in libdhcp++. Such option formats are stored in the
484
@ref isc::dhcp::CfgMgr. The libdhcp++ provides functions for recursive parsing
485
486
of options which may be encapsulated by other options up to the any level of
encapsulation but these functions are unaware of the option formats defined
487
488
489
in the @ref isc::dhcp::CfgMgr because they belong to a different library.
Therefore, the generic functions @ref isc::dhcp::LibDHCP::unpackOptions4 and
@ref isc::dhcp::LibDHCP::unpackOptions6 are only useful to parse standard
490
options whose definitions are provided in the libdhcp++. In order to overcome
491
492
493
494
495
496
497
498
499
this problem a callback mechanism has been implemented in @c Option and @c Pkt6
classes. By installing a callback function on the instance of the @c Pkt6 the
server may provide a custom implementation of the options parsing algorithm.
This callback function will take precedence over the @c LibDHCP::unpackOptions6
and @c LibDHCP::unpackOptions4 functions. With this approach, the callback is
implemented within the context of the server and it has access to all objects
which define its configuration (including dynamically created option
definitions).

500
501
@section dhcpv6Classifier DHCPv6 Client Classification

502
The Kea DHCPv6 currently supports two classification modes: simplified client
503
504
505
506
507
508
classification (that was an early implementation that used values of vendor class option)
and full client classification.

@subsection dhcpv6ClassifierSimple Simple Client Classification in DHCPv6

The Kea DHCPv6 server supports simplified client classification. It is called
509
"simplified", because the incoming packets are classified based on the content
510
511
of the vendor class (16) option. More flexible classification was added in 1.0
and is described in @ref dhcpv6ClassifierFull.
512
513
514
515
516
517
518
519
520
521
522
523
524

For each incoming packet, @ref isc::dhcp::Dhcpv6Srv::classifyPacket() method is
called.  It attempts to extract content of the vendor class option and interprets
as a name of the class. Although the RFC3315 says that the vendor class may
contain more than one chunk of data, the existing code handles only one data
block, because that is what actual devices use. For now, the code has been
tested with two classes used in cable modem networks: eRouter1.0 and docsis3.0,
but any other content of the vendor class option will be interpreted as a class
name.

In principle any given packet can belong to zero or more classes. As the current
classifier is very modest, there's only one way to assign a class (based on vendor class
option), the ability to assign more than one class to a packet is not yet exercised.
Francis Dupont's avatar
Francis Dupont committed
525
Nevertheless, there is such a possibility and it will be used in a near future. To
526
527
528
check whether a packet belongs to given class, isc::dhcp::Pkt6::inClass method should
be used.

529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
The code sometimes refers to this classification as "simple" or 'built-in", because
it does not require any configuration and thus is built into the server logic.

@subsection dhcpv6ClassifierFull Full Client Classification in DHCPv6

Kea 1.0 introduced full client classification. Each client class consists of a name
and an expression that can be evaluated on an incoming packet. If it evaluates to
true, this packet is considered a member of said class. Class definitions are stored
in isc::dhcp::ClientClassDef objects that are kept in isc::dhcp::ClientClassDictionary
and can be retrieved from CfgMgr using isc::dhcp::SrvConfig::getClientClassDictionary().
This is convenient as there are often multiple classes associated with a given scope.
As of Kea 1.0, the only supported scope is global, but there are plans to support
class definitions that are subnet specific.

Client classification is done in isc::dhcp::Dhcpv6Srv::classifyPacket. First, the old
544
"built-in" (see @ref dhcpv6ClassifierSimple) classification is called (see @ref
545
546
547
548
549
550
551
552
553
554
555
isc::dhcp::Dhcpv6Srv::classifyByVendor). Then the code iterates over all class definitions
and for each class definition it calls isc::dhcp::evaluate, which is implemented in libeval
(see @ref dhcpEval). If the evaluation is successful, the class name is added to the packet
(by calling isc::dhcp::pkt::addClass).

If packet belongs to at least one class, this fact is logged. If there are any
exceptions raised during class evaluation, an error is logged and the code attempts
to evaluate the next class.

@subsection dhcpv6ClassifierUsage How client classification information is used in DHCPv6

556
It is possible to define class restrictions in subnet, so a given subnet is only
Tomek Mrugalski's avatar
Tomek Mrugalski committed
557
558
accessible to clients that belong to a given class. That is implemented as isc::dhcp::Pkt6::classes_
being passed in isc::dhcp::Dhcpv6Srv::selectSubnet() to isc::dhcp::CfgMgr::getSubnet6().
559
560
561
Currently this capability is usable, but the number of scenarios it supports is
limited.

562
563
564
565
566
567
Finally, it is possible to define client class-specific options, so clients belonging
to a class foo, will get options associated with class foo. This is implemented in
isc::dhcp::Dhcpv6Srv::buildCfgOptionList.

@section dhcpv6ConfigBackend Configuration backend for DHCPv6

568
Earlier Kea versions had a concept of backends, which were implementations of
569
570
571
different ways how configuration could be delivered to Kea. It seems that the
concept of backends didn't get much enthusiasm from users and having multiple
backends was cumbersome to maintain, so it was removed in 1.0.
572

573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
@section dhcpv6SignalBasedReconfiguration Reconfiguring DHCPv6 server with SIGHUP signal

Online reconfiguration (reconfiguration without a need to restart the server) is an
important feature which is supported by all modern DHCP servers. When using the JSON
configuration backend, a configuration file name is specified with a command line
option of the DHCP server binary. The configuration file is used to configure the
server at startup. If the initial configuration fails, the server will fail to start.
If the server starts and configures successfully it will use the initial configuration
until it is reconfigured.

The reconfiguration request can be triggered externally (from other process) by editing
a configuration file and sending a SIGHUP signal to DHCP server process. After receiving
the SIGHUP signal, the server will re-read the configuration file specified at startup.
If the reconfiguration fails, the server will continue to run and use the last good
configuration.

589
The signal handler for SIGHUP (also for SIGTERM and SIGINT) are installed in the
590
kea_controller.cc using the @c isc::util::SignalSet class. The
591
592
593
594
595
@c isc::dhcp::Dhcp6Srv calls @c isc::dhcp::Daemon::handleSignal on each pass
through the main loop. This method fetches the last received signal and calls
a handler function defined in the kea_controller.cc. The handler function
calls a static function @c configure defined in the kea_controller.cc.

596
The signal handler reconfigures the server using the configuration file
597
specified at server startup. The location of this file is held in the
598
@c Daemon class.
599

600
@section dhcpv6Other Other DHCPv6 topics
601

602
For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
603

604
For a description of how DHCPv4-over-DHCPv6 is implemented, see @subpage dhcpv4o6Dhcp6
605

606
 */