1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
//
// 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/.
/**
@page dhcp6 DHCPv6 Server Component
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
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
describing a single subnet. It derives from the @c
isc::dhcp::SubnetConfigParser, which implements the common base for both
DHCPv4 and DHCPv6 subnets. The @ref isc::dhcp::Subnet6ConfigParser
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.
@section dhcpv6ConfigInherit DHCPv6 Configuration Inheritance
One notable useful feature of DHCP configuration is its parameter inheritance.
For example, the "renew-timer" value may be specified at a global scope and it then
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
the global parameters. If a value is not present, the value from the global
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 {
// look for local value
value = uint32_values_->getParam(name);
} catch (const DhcpConfigError &) {
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)");
}
}
return (Triplet<uint32_t>(value));
}
@endcode
Note that if the value is neither present in the local storage nor in the global
context, an error is signalled.
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.
Debugging a configuration parser may be confusing. Therefore there is a special
class called DebugParser. It does not configure anything, but just
accepts any parameter of any type. If requested to commit a configuration, it will
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.
@section dhcpv6DDNSIntegration DHCPv6 Server Support for the Dynamic DNS Updates
The DHCPv6 server supports processing of the DHCPv6 Client FQDN Option described in
the RFC4704. This Option is sent by the DHCPv6 client to instruct the server to
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.
As described in RFC4704, client may choose that the server delegates the forward
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.
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
for generating so called @ref isc::dhcp_ddns::NameChangeRequest and sending it to the
kea-dhcp-ddns module. The @ref isc::dhcp_ddns::NameChangeRequest object represents changes to the
DNS bindings, related to acquisition, renewal or release of the lease. The kea-dhcp6
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
send them to the kea-dhcp-ddns module when server is idle waiting for DHCP messages.
In the simplest case, when client gets one address from the server, a DHCPv6 server
may generate 0, 1 or 2 NameChangeRequests during single message processing.
The server generates no NameChangeRequests if it is not configured to update DNS
or it rejects the DNS update for any other reason.
The server may generate one NameChangeRequest in a situation when a client acquires a
new lease or it releases an existing lease. In the former case, the NameChangeRequest
type is CHG_ADD, which indicates that the kea-dhcp-ddns module should add a new DNS
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
indicate to the kea-dhcp-ddns module that the existing DNS binding should be removed
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
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.
The server may generate two NameChangeRequests in case the client is renewing a lease and
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
a message, and it will check in the lease database if the DNS Updates had
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.
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.
The DHCPv6 Client FQDN Option comprises "NOS" flags which communicate to the
server what updates (if any) client expects the server to perform. Server
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.
@todo Note, that current implementation doesn't allow configuration of the server's behavior
with respect to DNS Updates. This is planned for the future. The default behavior is
constituted by the set of constants defined in the (upper part of) dhcp6_srv.cc file.
Once the configuration is implemented, these constants will be removed.
@todo Add section about setting up options and their definitions with bindctl.
@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
@ref isc::dhcp::CfgMgr. The libdhcp++ provides functions for recursive parsing
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
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
options whose definitions are provided in the libdhcp++. In order to overcome
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).
@section dhcpv6Classifier DHCPv6 Client Classification
The Kea DHCPv6 currently supports two classification modes: simplified client
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
"simplified", because the incoming packets are classified based on the content
of the vendor class (16) option. More flexible classification was added in 1.0
and is described in @ref dhcpv6ClassifierFull.
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.
Nevertheless, there is such a possibility and it will be used in a near future. To
check whether a packet belongs to given class, isc::dhcp::Pkt6::inClass method should
be used.
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
"built-in" (see @ref dhcpv6ClassifierSimple) classification is called (see @ref
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
Currently there is no class behavior coded in DHCPv6, hence no v6 equivalent of
@ref isc::dhcp::Dhcpv6Srv::vendorClassSpecificProcessing. Should any need for such a code arise,
it will be conducted in an external hooks library.
It is possible to define class restrictions in subnet, so a given subnet is only
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().
Currently this capability is usable, but the number of scenarios it supports is
limited.
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
Earlier Kea vesions had a concept of backends, which were implementations of
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.
@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.
The signal handler for SIGHUP (also for SIGTERM and SIGINT) are installed in the
kea_controller.cc using the @c isc::util::SignalSet class. The
@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.
The signal handler reconfigures the server using the configuration file
specified at server startup. The location of this file is held in the
@c Daemon class.
@section dhcpv6Other Other DHCPv6 topics
For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
*/
|