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
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
364
365
366
367
368
369
370
371
372
373
374
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
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
|
// Copyright (C) 2011-2022 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/.
#ifndef LIBDHCP_H
#define LIBDHCP_H
#include <dhcp/option_definition.h>
#include <dhcp/option_space_container.h>
#include <dhcp/option_space.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <util/buffer.h>
#include <util/staged_value.h>
#include <iostream>
#include <stdint.h>
#include <string>
namespace isc {
namespace dhcp {
/// @brief A pointer to a ScopedPktOptionsCopy object instantiated using Pkt4.
typedef ScopedPktOptionsCopy<Pkt4> ScopedPkt4OptionsCopy;
/// @brief A pointer to a ScopedPktOptionsCopy object instantiated using Pkt6.
typedef ScopedPktOptionsCopy<Pkt6> ScopedPkt6OptionsCopy;
/// @brief A pointer to a ScopedSubOptionsCopy object.
typedef std::shared_ptr<ScopedSubOptionsCopy> ScopedOptionsCopyPtr;
/// @brief A container of ScopedOptionsCopyPtr objects.
typedef std::vector<ScopedOptionsCopyPtr> ScopedOptionsCopyContainer;
struct ManagedScopedOptionsCopyContainer {
/// @brief Constructor.
ManagedScopedOptionsCopyContainer() {
}
/// @brief Destructor.
~ManagedScopedOptionsCopyContainer() {
// Destroy the scoped options in same order so that parent options
// (stored last) are kept alive longer.
for (auto& scoped : scoped_options_) {
scoped.reset();
}
}
/// @brief The container.
ScopedOptionsCopyContainer scoped_options_;
};
class LibDHCP {
public:
/// Map of factory functions.
typedef std::map<unsigned short, Option::Factory*> FactoryMap;
/// @brief Returns collection of option definitions.
///
/// This method returns a collection of option definitions for a specified
/// option space.
///
/// @param space Option space.
///
/// @return Pointer to a collection of option definitions.
static const OptionDefContainerPtr getOptionDefs(const std::string& space);
/// @brief Return the first option definition matching a
/// particular option code.
///
/// @param space option space.
/// @param code option code.
///
/// @return reference to an option definition being requested
/// or NULL pointer if option definition has not been found.
static OptionDefinitionPtr getOptionDef(const std::string& space,
const uint16_t code);
/// @brief Return the definition of option having a specified name.
///
/// @param space option space.
/// @param name Option name.
///
/// @return Pointer to the option definition or NULL pointer if option
/// definition has not been found.
static OptionDefinitionPtr getOptionDef(const std::string& space,
const std::string& name);
/// @brief Returns vendor option definition for a given vendor-id and code
///
/// @param u universe (V4 or V6)
/// @param vendor_id enterprise-id for a given vendor
/// @param code option code
/// @return reference to an option definition being requested
/// or NULL pointer if option definition has not been found.
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u,
const uint32_t vendor_id,
const uint16_t code);
/// @brief Returns vendor option definition for a given vendor-id and
/// option name.
///
/// @param u Universe (V4 or V6)
/// @param vendor_id Enterprise-id for a given vendor
/// @param name Option name.
///
/// @return A pointer to an option definition or NULL pointer if
/// no option definition found.
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u,
const uint32_t vendor_id,
const std::string& name);
/// @brief Returns runtime (non-standard) option definition by space and
/// option code.
///
/// @param space Option space name.
/// @param code Option code.
///
/// @return Pointer to option definition or NULL if it doesn't exist.
static OptionDefinitionPtr getRuntimeOptionDef(const std::string& space,
const uint16_t code);
/// @brief Returns runtime (non-standard) option definition by space and
/// option name.
///
/// @param space Option space name.
/// @param name Option name.
///
/// @return Pointer to option definition or NULL if it doesn't exist.
static OptionDefinitionPtr getRuntimeOptionDef(const std::string& space,
const std::string& name);
/// @brief Returns runtime (non-standard) option definitions for specified
/// option space name.
///
/// @param space Option space name.
///
/// @return Pointer to the container holding option definitions or NULL.
static OptionDefContainerPtr getRuntimeOptionDefs(const std::string& space);
/// @brief Returns last resort option definition by space and option code.
///
/// @param space Option space name.
/// @param code Option code.
///
/// @return Pointer to option definition or NULL if it doesn't exist.
static OptionDefinitionPtr getLastResortOptionDef(const std::string& space,
const uint16_t code);
/// @brief Returns last resort option definition by space and option name.
///
/// @param space Option space name.
/// @param name Option name.
///
/// @return Pointer to option definition or NULL if it doesn't exist.
static OptionDefinitionPtr getLastResortOptionDef(const std::string& space,
const std::string& name);
/// @brief Returns last resort option definitions for specified option
/// space name.
///
/// @param space Option space name.
///
/// @return Pointer to the container holding option definitions or NULL.
static OptionDefContainerPtr getLastResortOptionDefs(const std::string& space);
/// @brief Checks if an option unpacking has to be deferred.
///
/// DHCPv4 option 43 and 224-254 unpacking is done after classification.
///
/// @param space Option space name.
/// @param code Option code.
///
/// @return True if option processing should be deferred.
static bool shouldDeferOptionUnpack(const std::string& space,
const uint16_t code);
/// @brief Factory function to create instance of option.
///
/// Factory method creates instance of specified option. The option
/// to be created has to have corresponding factory function
/// registered with \ref LibDHCP::OptionFactoryRegister.
///
/// @param u universe of the option (V4 or V6)
/// @param type option-type
/// @param buf option-buffer
///
/// @return instance of option.
///
/// @throw isc::InvalidOperation if there is no factory function registered
/// for the specified option type.
static isc::dhcp::OptionPtr optionFactory(isc::dhcp::Option::Universe u,
uint16_t type,
const OptionBuffer& buf);
/// @brief Stores DHCPv4 options in a buffer.
///
/// Stores all options defined in options containers in a on-wire
/// format in output buffer specified by buf.
///
/// May throw different exceptions if option assembly fails. There
/// may be different reasons (option too large, option malformed,
/// too many options etc.)
///
/// This is v4 specific version, which stores DHCP message type first,
/// and the Relay Agent Information option and END options last. This
/// function is initially called to pack the options for a packet in
/// @ref Pkt4::pack(). That call leads to it being called recursively in
/// @ref Option::packOptions(). Thus the logic used to output the
/// message type should only be executed by the top-most. This is governed
/// by the parameter top, below.
///
/// @param buf output buffer (assembled options will be stored here)
/// @param options collection of options to store to
/// @param top indicates if this is the first call to pack the options.
/// When true logic to emit the message type first is executed. It
/// defaults to false.
/// @param check indicates if the code should be more flexible with
/// PAD and END options. If true, PAD and END options will not be parsed.
/// This is useful for partial parsing and slightly broken packets.
static void packOptions4(isc::util::OutputBuffer& buf,
const isc::dhcp::OptionCollection& options,
bool top = false, bool check = true);
/// @brief Split long options in multiple options with the same option code
/// (RFC3396).
///
/// @param options The option container which needs to be updated with split
/// options.
/// @param scopedOptions temporary storage for options that are going to be
/// split. See @ref ScopedPktOptionsCopy for explanation.
/// @param used The size of the buffer that has already been used by the
/// parent option effectively shrinking the maximum supported length for
/// each options in the container.
/// @return True if any option has been split, false otherwise.
static bool splitOptions4(isc::dhcp::OptionCollection& options,
ScopedOptionsCopyContainer& scopedOptions,
uint32_t used = 0);
/// @brief Stores DHCPv6 options in a buffer.
///
/// Stores all options defined in options containers in a on-wire
/// format in output buffer specified by buf.
///
/// May throw different exceptions if option assembly fails. There
/// may be different reasons (option too large, option malformed,
/// too many options etc.)
///
/// Currently there's no special logic in it. Options are stored in
/// the order of their option codes.
///
/// @param buf output buffer (assembled options will be stored here)
/// @param options collection of options to store to
static void packOptions6(isc::util::OutputBuffer& buf,
const isc::dhcp::OptionCollection& options);
/// @brief Parses provided buffer as DHCPv6 options and creates
/// Option objects.
///
/// Parses provided buffer and stores created Option objects in
/// options container. The last two parameters are optional and
/// are used in relay parsing. If they are specified, relay-msg
/// option is not created, but rather those two parameters are
/// specified to point out where the relay-msg option resides and
/// what is its length. This is a performance optimization that
/// avoids unnecessary copying of potentially large relay-msg
/// option. It is not used for anything, except in the next
/// iteration its content will be treated as buffer to be parsed.
///
/// @param buf Buffer to be parsed.
/// @param option_space A name of the option space which holds definitions
/// to be used to parse options in the packets.
/// @param options Reference to option container. Options will be
/// put here.
/// @param relay_msg_offset reference to a size_t structure. If specified,
/// offset to beginning of relay_msg option will be stored in it.
/// @param relay_msg_len reference to a size_t structure. If specified,
/// length of the relay_msg option will be stored in it.
/// @return offset to the first byte after the last successfully
/// parsed option
///
/// @note This function throws when an option type is defined more
/// than once, and it calls option building routines which can throw.
/// Partial parsing does not throw: it is the responsibility of the
/// caller to handle this condition.
static size_t unpackOptions6(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options,
size_t* relay_msg_offset = 0,
size_t* relay_msg_len = 0);
/// @brief Fuse multiple options with the same option code in long options
/// (RFC3396).
///
/// @param options The option container which needs to be updated with fused
/// options.
/// @return True if any option has been fused, false otherwise.
static bool fuseOptions4(isc::dhcp::OptionCollection& options);
/// @brief Parses provided buffer as DHCPv4 options and creates
/// Option objects.
///
/// Parses provided buffer and stores created Option objects
/// in options container.
///
/// @param buf Buffer to be parsed.
/// @param option_space A name of the option space which holds definitions
/// to be used to parse options in the packets.
/// @param options Reference to option container. Options will be
/// put here.
/// @param deferred Reference to an option code list. Options which
/// processing is deferred will be put here.
/// @param flexible_pad_end Parse options 0 and 255 as PAD and END
/// when they are not defined in the option space.
/// @return offset to the first byte after the last successfully
/// parsed option or the offset of the DHO_END option type.
///
/// The unpackOptions6 note applies too.
static size_t unpackOptions4(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options,
std::list<uint16_t>& deferred,
bool flexible_pad_end = false);
/// Registers factory method that produces options of specific option types.
///
/// @throw isc::BadValue if provided the type is already registered, has
/// too large a value or an invalid universe is specified.
///
/// @param u universe of the option (V4 or V6)
/// @param type option-type
/// @param factory function pointer
static void OptionFactoryRegister(Option::Universe u,
uint16_t type,
Option::Factory * factory);
/// @brief Returns option definitions for given universe and vendor
///
/// @param u option universe
/// @param vendor_id enterprise-id of a given vendor
///
/// @return a container for a given vendor (or NULL if no option
/// definitions are defined)
static const OptionDefContainerPtr getVendorOptionDefs(Option::Universe u,
const uint32_t vendor_id);
/// @brief Parses provided buffer as DHCPv6 vendor options and creates
/// Option objects.
///
/// Parses provided buffer and stores created Option objects
/// in options container.
///
/// @param vendor_id enterprise-id of the vendor
/// @param buf Buffer to be parsed.
/// @param options Reference to option container. Suboptions will be
/// put here.
/// @return offset to the first byte after the last successfully
/// parsed suboption
///
/// @note unpackVendorOptions6 throws when it fails to parse a suboption
/// so the return value is currently always the buffer length.
static size_t unpackVendorOptions6(const uint32_t vendor_id,
const OptionBuffer& buf,
isc::dhcp::OptionCollection& options);
/// @brief Parses provided buffer as DHCPv4 vendor options and creates
/// Option objects.
///
/// Parses provided buffer and stores created Option objects
/// in options container.
///
/// @param vendor_id enterprise-id of the vendor
/// @param buf Buffer to be parsed.
/// @param options Reference to option container. Suboptions will be
/// put here.
/// @return offset to the first byte after the last successfully
/// parsed suboption
///
/// The unpackVendorOptions6 note applies
static size_t unpackVendorOptions4(const uint32_t vendor_id,
const OptionBuffer& buf,
isc::dhcp::OptionCollection& options);
/// @brief Copies option definitions created at runtime.
///
/// Copied option definitions will be used as "runtime" option definitions.
/// A typical use case is to set option definitions specified by the user
/// in the server configuration. These option definitions should be removed
/// or replaced with new option definitions upon reconfiguration.
///
/// @param defs Const reference to a container holding option definitions
/// grouped by option spaces.
static void setRuntimeOptionDefs(const OptionDefSpaceContainer& defs);
/// @brief Removes runtime option definitions.
static void clearRuntimeOptionDefs();
/// @brief Reverts uncommitted changes to runtime option definitions.
static void revertRuntimeOptionDefs();
/// @brief Commits runtime option definitions.
static void commitRuntimeOptionDefs();
/// @brief Converts option space name to vendor id.
///
/// If the option space name is specified in the following format:
/// "vendor-X" where X is an uint32_t number, it is assumed to be
/// a vendor space and the uint32_t number is returned by this function.
/// If the option space name is invalid this method will return 0, which
/// is not a valid vendor-id, to signal an error.
///
/// @todo remove this function once when the conversion is dealt by the
/// appropriate functions returning options by option space names.
///
/// @param option_space Option space name.
/// @return vendor id.
static uint32_t optionSpaceToVendorId(const std::string& option_space);
private:
/// Initialize DHCP option definitions.
///
/// The method creates option definitions for all DHCP options.
///
/// @throw std::bad alloc if system went out of memory.
/// @throw MalformedOptionDefinition if any of the definitions
/// are incorrect. This is programming error.
static bool initOptionDefs();
/// flag which indicates initialization state
static bool initialized_;
/// pointers to factories that produce DHCPv6 options
static FactoryMap v4factories_;
/// pointers to factories that produce DHCPv6 options
static FactoryMap v6factories_;
/// Container that holds option definitions for various option spaces.
static OptionDefContainers option_defs_;
/// Container for additional option definitions created in runtime.
static util::StagedValue<OptionDefSpaceContainer> runtime_option_defs_;
};
}
}
#endif // LIBDHCP_H
|