Kea: libdhcp++.cc Source File

| Kea 1.5.0

libdhcp++.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8
9 #include <dhcp/dhcp4.h>
10 #include <dhcp/dhcp6.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcp/option.h>
13 #include <dhcp/option_vendor.h>
14 #include <dhcp/option6_ia.h>
15 #include <dhcp/option6_iaaddr.h>
16 #include <dhcp/option_definition.h>
17 #include <dhcp/option_int_array.h>
18 #include <dhcp/option_space.h>
19 #include <dhcp/std_option_defs.h>
20 #include <dhcp/docsis3_option_defs.h>
21 #include <exceptions/exceptions.h>
22 #include <util/buffer.h>
23 #include <dhcp/option_definition.h>
24
25 #include <boost/lexical_cast.hpp>
26 #include <boost/shared_array.hpp>
27 #include <boost/shared_ptr.hpp>
28
29 #include <limits>
30 #include <list>
31
32 using namespace std;
33 using namespace isc::dhcp;
34 using namespace isc::util;
35
36 // static array with factories for options
37 std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
38
39 // static array with factories for options
40 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
41
42 // Static container with DHCPv4 option definitions.
43 OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer());
44
45 // Static container with DHCPv6 option definitions.
46 OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer());
47
48 // Static container with option definitions grouped by option space.
49 OptionDefContainers LibDHCP::option_defs_;
50
51 // Static container with vendor option definitions for DHCPv4.
52 VendorOptionDefContainers LibDHCP::vendor4_defs_;
53
54 // Static container with vendor option definitions for DHCPv6.
55 VendorOptionDefContainers LibDHCP::vendor6_defs_;
56
57 // Static container with last resort option definitions for DHCPv4.
58 OptionDefContainerPtr LibDHCP::lastresort_defs_(new OptionDefContainer());
59
60 // Static container with option definitions created in runtime.
61 StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
62
63 // Null container.
64 const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer());
65
66 // Those two vendor classes are used for cable modems:
67
69 const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
70
72 const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
73
74 // Let's keep it in .cc file. Moving it to .h would require including optionDefParams
75 // definitions there
76 void initOptionSpace(OptionDefContainerPtr& defs,
77 const OptionDefParams* params,
78 size_t params_size);
79
80 const OptionDefContainerPtr&
81 LibDHCP::getOptionDefs(const std::string& space) {
82 // If any of the containers is not initialized, it means that we haven't
83 // initialized option definitions at all.
84 if (v4option_defs_->empty()) {
85 initStdOptionDefs4();
86 initVendorOptsDocsis4();
87 initStdOptionDefs6();
88 initVendorOptsDocsis6();
89 initLastResortOptionDefs();
90 }
91
92 if (space == DHCP4_OPTION_SPACE) {
93 return (v4option_defs_);
94
95 } else if (space == DHCP6_OPTION_SPACE) {
96 return (v6option_defs_);
97 }
98
99 OptionDefContainers::const_iterator container = option_defs_.find(space);
100 if (container != option_defs_.end()) {
101 return (container->second);
102 }
103 return (null_option_def_container_);
104 }
105
106 const OptionDefContainerPtr&
107 LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
108
109 if (vendor_id == VENDOR_ID_CABLE_LABS &&
110 vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
111 initVendorOptsDocsis4();
112 }
113
114 VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
115 if (def == vendor4_defs_.end()) {
116 // No such vendor-id space
117 return (null_option_def_container_);
118 }
119 return (def->second);
120 }
121
122 const OptionDefContainerPtr&
123 LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
124
125 if (vendor_id == VENDOR_ID_CABLE_LABS &&
126 vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) {
127 initVendorOptsDocsis6();
128 }
129
130 if (vendor_id == ENTERPRISE_ID_ISC &&
131 vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) {
132 initVendorOptsIsc6();
133 }
134
135 VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
136 if (def == vendor6_defs_.end()) {
137 // No such vendor-id space
138 return (null_option_def_container_);
139 }
140 return (def->second);
141 }
142
143 OptionDefinitionPtr
144 LibDHCP::getOptionDef(const std::string& space, const uint16_t code) {
145 const OptionDefContainerPtr& defs = getOptionDefs(space);
146 const OptionDefContainerTypeIndex& idx = defs->get<1>();
147 const OptionDefContainerTypeRange& range = idx.equal_range(code);
148 if (range.first != range.second) {
149 return (*range.first);
150 }
151 return (OptionDefinitionPtr());
152 }
153
154 OptionDefinitionPtr
155 LibDHCP::getOptionDef(const std::string& space, const std::string& name) {
156 const OptionDefContainerPtr defs = getOptionDefs(space);
157 const OptionDefContainerNameIndex& idx = defs->get<2>();
158 const OptionDefContainerNameRange& range = idx.equal_range(name);
159 if (range.first != range.second) {
160 return (*range.first);
161 }
162 return (OptionDefinitionPtr());
163 }
164
165 OptionDefinitionPtr
166 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
167 const std::string& name) {
168 OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
169 getVendorOption6Defs(vendor_id));
170
171 if (!defs) {
172 return (OptionDefinitionPtr());
173 }
174
175 const OptionDefContainerNameIndex& idx = defs->get<2>();
176 const OptionDefContainerNameRange& range = idx.equal_range(name);
177 if (range.first != range.second) {
178 return (*range.first);
179 }
180 return (OptionDefinitionPtr());
181 }
182
183 OptionDefinitionPtr
184 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
185 const uint16_t code) {
186 OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
187 getVendorOption6Defs(vendor_id));
188
189 if (!defs) {
190 // Weird universe or unknown vendor_id. We don't care. No definitions
191 // one way or another
192 // What is it anyway?
193 return (OptionDefinitionPtr());
194 }
195
196 const OptionDefContainerTypeIndex& idx = defs->get<1>();
197 const OptionDefContainerTypeRange& range = idx.equal_range(code);
198 if (range.first != range.second) {
199 return (*range.first);
200 }
201 return (OptionDefinitionPtr());
202 }
203
204 OptionDefinitionPtr
205 LibDHCP::getRuntimeOptionDef(const std::string& space, const uint16_t code) {
206 OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
207 const OptionDefContainerTypeIndex& index = container->get<1>();
208 const OptionDefContainerTypeRange& range = index.equal_range(code);
209 if (range.first != range.second) {
210 return (*range.first);
211 }
212
213 return (OptionDefinitionPtr());
214 }
215
216 OptionDefinitionPtr
217 LibDHCP::getRuntimeOptionDef(const std::string& space, const std::string& name) {
218 OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
219 const OptionDefContainerNameIndex& index = container->get<2>();
220 const OptionDefContainerNameRange& range = index.equal_range(name);
221 if (range.first != range.second) {
222 return (*range.first);
223 }
224
225 return (OptionDefinitionPtr());
226 }
227
228 OptionDefContainerPtr
229 LibDHCP::getRuntimeOptionDefs(const std::string& space) {
230 return (runtime_option_defs_.getValue().getItems(space));
231 }
232
233 void
234 LibDHCP::setRuntimeOptionDefs(const OptionDefSpaceContainer& defs) {
235 OptionDefSpaceContainer defs_copy;
236 std::list<std::string> option_space_names = defs.getOptionSpaceNames();
237 for (std::list<std::string>::const_iterator name = option_space_names.begin();
238 name != option_space_names.end(); ++name) {
239 OptionDefContainerPtr container = defs.getItems(*name);
240 for (OptionDefContainer::const_iterator def = container->begin();
241 def != container->end(); ++def) {
242 OptionDefinitionPtr def_copy(new OptionDefinition(**def));
243 defs_copy.addItem(def_copy, *name);
244 }
245 }
246 runtime_option_defs_ = defs_copy;
247 }
248
249 void
250 LibDHCP::clearRuntimeOptionDefs() {
251 runtime_option_defs_.reset();
252 }
253
254 void
255 LibDHCP::revertRuntimeOptionDefs() {
256 runtime_option_defs_.revert();
257 }
258
259 void
260 LibDHCP::commitRuntimeOptionDefs() {
261 runtime_option_defs_.commit();
262 }
263
264 OptionDefinitionPtr
265 LibDHCP::getLastResortOptionDef(const std::string& space, const uint16_t code) {
266 OptionDefContainerPtr container = getLastResortOptionDefs(space);
267 const OptionDefContainerTypeIndex& index = container->get<1>();
268 const OptionDefContainerTypeRange& range = index.equal_range(code);
269 if (range.first != range.second) {
270 return (*range.first);
271 }
272
273 return (OptionDefinitionPtr());
274 }
275
276 OptionDefinitionPtr
277 LibDHCP::getLastResortOptionDef(const std::string& space, const std::string& name) {
278 OptionDefContainerPtr container = getLastResortOptionDefs(space);
279 const OptionDefContainerNameIndex& index = container->get<2>();
280 const OptionDefContainerNameRange& range = index.equal_range(name);
281 if (range.first != range.second) {
282 return (*range.first);
283 }
284
285 return (OptionDefinitionPtr());
286 }
287
288 OptionDefContainerPtr
289 LibDHCP::getLastResortOptionDefs(const std::string& space) {
290 if (space == DHCP4_OPTION_SPACE) {
291 return (lastresort_defs_);
292 }
293 return (null_option_def_container_);
294 }
295
296 bool
297 LibDHCP::shouldDeferOptionUnpack(const std::string& space, const uint16_t code) {
298 return ((space == DHCP4_OPTION_SPACE) &&
299 ((code == DHO_VENDOR_ENCAPSULATED_OPTIONS) ||
300 ((code >= 224) && (code <= 254))));
301 }
302
303 OptionPtr
304 LibDHCP::optionFactory(Option::Universe u,
305 uint16_t type,
306 const OptionBuffer& buf) {
307 FactoryMap::iterator it;
308 if (u == Option::V4) {
309 it = v4factories_.find(type);
310 if (it == v4factories_.end()) {
311 isc_throw(BadValue, "factory function not registered "
312 "for DHCP v4 option type " << type);
313 }
314 } else if (u == Option::V6) {
315 it = v6factories_.find(type);
316 if (it == v6factories_.end()) {
317 isc_throw(BadValue, "factory function not registered "
318 "for DHCPv6 option type " << type);
319 }
320 } else {
321 isc_throw(BadValue, "invalid universe specified (expected "
322 "Option::V4 or Option::V6");
323 }
324 return (it->second(u, type, buf));
325 }
326
327
328 size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
329 const std::string& option_space,
330 isc::dhcp::OptionCollection& options,
331 size_t* relay_msg_offset /* = 0 */,
332 size_t* relay_msg_len /* = 0 */) {
333 size_t offset = 0;
334 size_t length = buf.size();
335 size_t last_offset = 0;
336
337 // Get the list of standard option definitions.
338 const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
339 // Runtime option definitions for non standard option space and if
340 // the definition doesn't exist within the standard option definitions.
341 const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
342
343 // @todo Once we implement other option spaces we should add else clause
344 // here and gather option definitions for them. For now leaving option_defs
345 // empty will imply creation of generic Option.
346
347 // Get the search indexes #1. It allows to search for option definitions
348 // using option code.
349 const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
350 const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
351
352 // The buffer being read comprises a set of options, each starting with
353 // a two-byte type code and a two-byte length field.
354 while (offset < length) {
355 // Save the current offset for backtracking
356 last_offset = offset;
357
358 // Check if there is room for another option
359 if (offset + 4 > length) {
360 // Still something but smaller than an option
361 return (last_offset);
362 }
363
364 // Parse the option header
365 uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
366 offset += 2;
367
368 uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
369 offset += 2;
370
371 if (offset + opt_len > length) {
372 // We peeked at the option header of the next option, but
373 // discovered that it would end up beyond buffer end, so
374 // the option is truncated. Hence we can't parse
375 // it. Therefore we revert back by those bytes (as if
376 // we never parsed them).
377 //
378 // @note it is the responsibility of the caller to throw
379 // an exception on partial parsing
380 return (last_offset);
381 }
382
383 if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
384 // remember offset of the beginning of the relay-msg option
385 *relay_msg_offset = offset;
386 *relay_msg_len = opt_len;
387
388 // do not create that relay-msg option
389 offset += opt_len;
390 continue;
391 }
392
393 if (opt_type == D6O_VENDOR_OPTS) {
394 if (offset + 4 > length) {
395 // Truncated vendor-option. We expect at least
396 // 4 bytes for the enterprise-id field. Let's roll back
397 // option code + option length (4 bytes) and return.
398 return (last_offset);
399 }
400
401 // Parse this as vendor option
402 OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
403 buf.begin() + offset + opt_len));
404 options.insert(std::make_pair(opt_type, vendor_opt));
405
406 offset += opt_len;
407 continue;
408 }
409
410 // Get all definitions with the particular option code. Note
411 // that option code is non-unique within this container
412 // however at this point we expect to get one option
413 // definition with the particular code. If more are returned
414 // we report an error.
415 OptionDefContainerTypeRange range;
416 // Number of option definitions returned.
417 size_t num_defs = 0;
418
419 // We previously did the lookup only for dhcp6 option space, but with the
420 // addition of S46 options, we now do it for every space.
421 range = idx.equal_range(opt_type);
422 num_defs = std::distance(range.first, range.second);
423
424 // Standard option definitions do not include the definition for
425 // our option or we're searching for non-standard option. Try to
426 // find the definition among runtime option definitions.
427 if (num_defs == 0) {
428 range = runtime_idx.equal_range(opt_type);
429 num_defs = std::distance(range.first, range.second);
430 }
431
432 OptionPtr opt;
433 if (num_defs > 1) {
434 // Multiple options of the same code are not supported right now!
435 isc_throw(isc::Unexpected, "Internal error: multiple option"
436 " definitions for option type " << opt_type <<
437 " returned. Currently it is not supported to initialize"
438 " multiple option definitions for the same option code."
439 " This will be supported once support for option spaces"
440 " is implemented");
441 } else if (num_defs == 0) {
442 // @todo Don't crash if definition does not exist because
443 // only a few option definitions are initialized right
444 // now. In the future we will initialize definitions for
445 // all options and we will remove this elseif. For now,
446 // return generic option.
447 opt = OptionPtr(new Option(Option::V6, opt_type,
448 buf.begin() + offset,
449 buf.begin() + offset + opt_len));
450 } else {
451 // The option definition has been found. Use it to create
452 // the option instance from the provided buffer chunk.
453 const OptionDefinitionPtr& def = *(range.first);
454 assert(def);
455 opt = def->optionFactory(Option::V6, opt_type,
456 buf.begin() + offset,
457 buf.begin() + offset + opt_len);
458 }
459 // add option to options
460 options.insert(std::make_pair(opt_type, opt));
461 offset += opt_len;
462 }
463
464 last_offset = offset;
465 return (last_offset);
466 }
467
468 size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
469 const std::string& option_space,
470 isc::dhcp::OptionCollection& options,
471 std::list<uint16_t>& deferred) {
472 size_t offset = 0;
473 size_t last_offset = 0;
474
475 // Get the list of standard option definitions.
476 const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
477 // Runtime option definitions for non standard option space and if
478 // the definition doesn't exist within the standard option definitions.
479 const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
480
481 // Get the search indexes #1. It allows to search for option definitions
482 // using option code.
483 const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
484 const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
485
486 // The buffer being read comprises a set of options, each starting with
487 // a one-byte type code and a one-byte length field.
488 while (offset < buf.size()) {
489 // Save the current offset for backtracking
490 last_offset = offset;
491
492 // Get the option type
493 uint8_t opt_type = buf[offset++];
494
495 // DHO_END is a special, one octet long option
496 if (opt_type == DHO_END) {
497 // just return. Don't need to add DHO_END option
498 // Don't return offset because it makes this condition
499 // and partial parsing impossible to recognize.
500 return (last_offset);
501 }
502
503 // DHO_PAD is just a padding after DHO_END. Let's continue parsing
504 // in case we receive a message without DHO_END.
505 if (opt_type == DHO_PAD)
506 continue;
507
508 if (offset + 1 > buf.size()) {
509 // We peeked at the option header of the next option, but
510 // discovered that it would end up beyond buffer end, so
511 // the option is truncated. Hence we can't parse
512 // it. Therefore we revert back (as if we never parsed it).
513 //
514 // @note it is the responsibility of the caller to throw
515 // an exception on partial parsing
516 return (last_offset);
517 }
518
519 uint8_t opt_len = buf[offset++];
520 if (offset + opt_len > buf.size()) {
521 // We peeked at the option header of the next option, but
522 // discovered that it would end up beyond buffer end, so
523 // the option is truncated. Hence we can't parse
524 // it. Therefore we revert back (as if we never parsed it).
525 return (last_offset);
526 }
527
528 // Get all definitions with the particular option code. Note
529 // that option code is non-unique within this container
530 // however at this point we expect to get one option
531 // definition with the particular code. If more are returned
532 // we report an error.
533 OptionDefContainerTypeRange range;
534 // Number of option definitions returned.
535 size_t num_defs = 0;
536
537 // Previously we did the lookup only for "dhcp4" option space, but there
538 // may be standard options in other spaces (e.g. radius). So we now do
539 // the lookup for every space.
540 range = idx.equal_range(opt_type);
541 num_defs = std::distance(range.first, range.second);
542
543 // Standard option definitions do not include the definition for
544 // our option or we're searching for non-standard option. Try to
545 // find the definition among runtime option definitions.
546 if (num_defs == 0) {
547 range = runtime_idx.equal_range(opt_type);
548 num_defs = std::distance(range.first, range.second);
549 }
550
551 // Check if option unpacking must be deferred
552 if (shouldDeferOptionUnpack(option_space, opt_type)) {
553 num_defs = 0;
554 deferred.push_back(opt_type);
555 }
556
557 OptionPtr opt;
558 if (num_defs > 1) {
559 // Multiple options of the same code are not supported right now!
560 isc_throw(isc::Unexpected, "Internal error: multiple option"
561 " definitions for option type " <<
562 static_cast<int>(opt_type) <<
563 " returned. Currently it is not supported to initialize"
564 " multiple option definitions for the same option code."
565 " This will be supported once support for option spaces"
566 " is implemented");
567 } else if (num_defs == 0) {
568 opt = OptionPtr(new Option(Option::V4, opt_type,
569 buf.begin() + offset,
570 buf.begin() + offset + opt_len));
571 opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
572 } else {
573 // The option definition has been found. Use it to create
574 // the option instance from the provided buffer chunk.
575 const OptionDefinitionPtr& def = *(range.first);
576 assert(def);
577 opt = def->optionFactory(Option::V4, opt_type,
578 buf.begin() + offset,
579 buf.begin() + offset + opt_len);
580 }
581
582 options.insert(std::make_pair(opt_type, opt));
583 offset += opt_len;
584 }
585 last_offset = offset;
586 return (last_offset);
587 }
588
589 size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
590 const OptionBuffer& buf,
591 isc::dhcp::OptionCollection& options) {
592 size_t offset = 0;
593 size_t length = buf.size();
594
595 // Get the list of option definitions for this particular vendor-id
596 const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption6Defs(vendor_id);
597
598 // Get the search index #1. It allows to search for option definitions
599 // using option code. If there's no such vendor-id space, we're out of luck
600 // anyway.
601 const OptionDefContainerTypeIndex* idx = NULL;
602 if (option_defs) {
603 idx = &(option_defs->get<1>());
604 }
605
606 // The buffer being read comprises a set of options, each starting with
607 // a two-byte type code and a two-byte length field.
608 while (offset < length) {
609 if (offset + 4 > length) {
610 isc_throw(SkipRemainingOptionsError,
611 "Vendor option parse failed: truncated header");
612 }
613
614 uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
615 offset += 2;
616
617 uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
618 offset += 2;
619
620 if (offset + opt_len > length) {
621 isc_throw(SkipRemainingOptionsError,
622 "Vendor option parse failed. Tried to parse "
623 << offset + opt_len << " bytes from " << length
624 << "-byte long buffer.");
625 }
626
627 OptionPtr opt;
628 opt.reset();
629
630 // If there is a definition for such a vendor option...
631 if (idx) {
632 // Get all definitions with the particular option
633 // code. Note that option code is non-unique within this
634 // container however at this point we expect to get one
635 // option definition with the particular code. If more are
636 // returned we report an error.
637 const OptionDefContainerTypeRange& range =
638 idx->equal_range(opt_type);
639 // Get the number of returned option definitions for the
640 // option code.
641 size_t num_defs = std::distance(range.first, range.second);
642
643 if (num_defs > 1) {
644 // Multiple options of the same code are not supported
645 // right now!
646 isc_throw(isc::Unexpected, "Internal error: multiple option"
647 " definitions for option type " << opt_type <<
648 " returned. Currently it is not supported to"
649 " initialize multiple option definitions for the"
650 " same option code. This will be supported once"
651 " support for option spaces is implemented");
652 } else if (num_defs == 1) {
653 // The option definition has been found. Use it to create
654 // the option instance from the provided buffer chunk.
655 const OptionDefinitionPtr& def = *(range.first);
656 assert(def);
657 opt = def->optionFactory(Option::V6, opt_type,
658 buf.begin() + offset,
659 buf.begin() + offset + opt_len);
660 }
661 }
662
663 // This can happen in one of 2 cases:
664 // 1. we do not have definitions for that vendor-space
665 // 2. we do have definitions, but that particular option was
666 // not defined
667
668 if (!opt) {
669 opt = OptionPtr(new Option(Option::V6, opt_type,
670 buf.begin() + offset,
671 buf.begin() + offset + opt_len));
672 }
673
674 // add option to options
675 if (opt) {
676 options.insert(std::make_pair(opt_type, opt));
677 }
678 offset += opt_len;
679 }
680
681 return (offset);
682 }
683
684 size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
685 isc::dhcp::OptionCollection& options) {
686 size_t offset = 0;
687
688 // Get the list of standard option definitions.
689 const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
690 // Get the search index #1. It allows to search for option definitions
691 // using option code.
692 const OptionDefContainerTypeIndex* idx = NULL;
693 if (option_defs) {
694 idx = &(option_defs->get<1>());
695 }
696
697 // The buffer being read comprises a set of options, each starting with
698 // a one-byte type code and a one-byte length field.
699 while (offset < buf.size()) {
700 // Note that Vendor-Specific info option (RFC3925) has a
701 // different option format than Vendor-Spec info for
702 // DHCPv6. (there's additional layer of data-length)
703 uint8_t data_len = buf[offset++];
704
705 if (offset + data_len > buf.size()) {
706 // The option is truncated.
707 isc_throw(SkipRemainingOptionsError,
708 "Attempt to parse truncated vendor option");
709 }
710
711 uint8_t offset_end = offset + data_len;
712
713 // beginning of data-chunk parser
714 while (offset < offset_end) {
715 uint8_t opt_type = buf[offset++];
716
717 // No DHO_END or DHO_PAD in vendor options
718
719 if (offset + 1 > offset_end) {
720 // opt_type must be cast to integer so as it is not
721 // treated as unsigned char value (a number is
722 // presented in error message).
723 isc_throw(SkipRemainingOptionsError,
724 "Attempt to parse truncated vendor option "
725 << static_cast<int>(opt_type));
726 }
727
728 uint8_t opt_len = buf[offset++];
729 if (offset + opt_len > offset_end) {
730 isc_throw(SkipRemainingOptionsError,
731 "Option parse failed. Tried to parse "
732 << offset + opt_len << " bytes from " << buf.size()
733 << "-byte long buffer.");
734 }
735
736 OptionPtr opt;
737 opt.reset();
738
739 if (idx) {
740 // Get all definitions with the particular option
741 // code. Note that option code is non-unique within
742 // this container however at this point we expect to
743 // get one option definition with the particular
744 // code. If more are returned we report an error.
745 const OptionDefContainerTypeRange& range =
746 idx->equal_range(opt_type);
747 // Get the number of returned option definitions for
748 // the option code.
749 size_t num_defs = std::distance(range.first, range.second);
750
751 if (num_defs > 1) {
752 // Multiple options of the same code are not
753 // supported right now!
754 isc_throw(isc::Unexpected, "Internal error: multiple"
755 " option definitions for option type "
756 << opt_type << " returned. Currently it is"
757 " not supported to initialize multiple option"
758 " definitions for the same option code."
759 " This will be supported once support for"
760 " option spaces is implemented");
761 } else if (num_defs == 1) {
762 // The option definition has been found. Use it to create
763 // the option instance from the provided buffer chunk.
764 const OptionDefinitionPtr& def = *(range.first);
765 assert(def);
766 opt = def->optionFactory(Option::V4, opt_type,
767 buf.begin() + offset,
768 buf.begin() + offset + opt_len);
769 }
770 }
771
772 if (!opt) {
773 opt = OptionPtr(new Option(Option::V4, opt_type,
774 buf.begin() + offset,
775 buf.begin() + offset + opt_len));
776 }
777
778 options.insert(std::make_pair(opt_type, opt));
779 offset += opt_len;
780
781 } // end of data-chunk
782
783 break; // end of the vendor block.
784 }
785 return (offset);
786 }
787
788 void
789 LibDHCP::packOptions4(isc::util::OutputBuffer& buf,
790 const OptionCollection& options) {
791 OptionPtr agent;
792 OptionPtr end;
793 for (OptionCollection::const_iterator it = options.begin();
794 it != options.end(); ++it) {
795
796 // RAI and END options must be last.
797 switch (it->first) {
798 case DHO_DHCP_AGENT_OPTIONS:
799 agent = it->second;
800 break;
801 case DHO_END:
802 end = it->second;
803 break;
804 default:
805 it->second->pack(buf);
806 break;
807 }
808 }
809
810 // Add the RAI option if it exists.
811 if (agent) {
812 agent->pack(buf);
813 }
814
815 // And at the end the END option.
816 if (end) {
817 end->pack(buf);
818 }
819 }
820
821 void
822 LibDHCP::packOptions6(isc::util::OutputBuffer& buf,
823 const OptionCollection& options) {
824 for (OptionCollection::const_iterator it = options.begin();
825 it != options.end(); ++it) {
826 it->second->pack(buf);
827 }
828 }
829
830 void LibDHCP::OptionFactoryRegister(Option::Universe u,
831 uint16_t opt_type,
832 Option::Factory* factory) {
833 switch (u) {
834 case Option::V6: {
835 if (v6factories_.find(opt_type) != v6factories_.end()) {
836 isc_throw(BadValue, "There is already DHCPv6 factory registered "
837 << "for option type " << opt_type);
838 }
839 v6factories_[opt_type]=factory;
840 return;
841 }
842 case Option::V4:
843 {
844 // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
845 // instantiated as an Option object, but rather consumed during packet parsing.
846 if (opt_type == 0) {
847 isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
848 }
849 // Option 255 is never instantiated as an option object. It is special
850 // (a one-octet equal 255) option that is added at the end of all options
851 // during packet assembly. It is also silently consumed during packet parsing.
852 if (opt_type > 254) {
853 isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
854 }
855 if (v4factories_.find(opt_type)!=v4factories_.end()) {
856 isc_throw(BadValue, "There is already DHCPv4 factory registered "
857 << "for option type " << opt_type);
858 }
859 v4factories_[opt_type]=factory;
860 return;
861 }
862 default:
863 isc_throw(BadValue, "Invalid universe type specified.");
864 }
865
866 return;
867 }
868
869 void
870 LibDHCP::initStdOptionDefs4() {
871 initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS,
872 STANDARD_V4_OPTION_DEFINITIONS_SIZE);
873 }
874
875 void
876 LibDHCP::initStdOptionDefs6() {
877 initOptionSpace(v6option_defs_, STANDARD_V6_OPTION_DEFINITIONS,
878 STANDARD_V6_OPTION_DEFINITIONS_SIZE);
879 initOptionSpace(option_defs_[MAPE_V6_OPTION_SPACE], MAPE_V6_OPTION_DEFINITIONS,
880 MAPE_V6_OPTION_DEFINITIONS_SIZE);
881 initOptionSpace(option_defs_[MAPT_V6_OPTION_SPACE], MAPT_V6_OPTION_DEFINITIONS,
882 MAPT_V6_OPTION_DEFINITIONS_SIZE);
883 initOptionSpace(option_defs_[LW_V6_OPTION_SPACE], LW_V6_OPTION_DEFINITIONS,
884 LW_V6_OPTION_DEFINITIONS_SIZE);
885 initOptionSpace(option_defs_[V4V6_RULE_OPTION_SPACE], V4V6_RULE_OPTION_DEFINITIONS,
886 V4V6_RULE_OPTION_DEFINITIONS_SIZE);
887 initOptionSpace(option_defs_[V4V6_BIND_OPTION_SPACE], V4V6_BIND_OPTION_DEFINITIONS,
888 V4V6_BIND_OPTION_DEFINITIONS_SIZE);
889 }
890
891 void
892 LibDHCP::initLastResortOptionDefs() {
893 initOptionSpace(lastresort_defs_, LAST_RESORT_V4_OPTION_DEFINITIONS,
894 LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE);
895 }
896
897 void
898 LibDHCP::initVendorOptsDocsis4() {
899 initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS,
900 DOCSIS3_V4_DEFS_SIZE);
901 }
902
903 void
904 LibDHCP::initVendorOptsDocsis6() {
905 initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS,
906 DOCSIS3_V6_DEFS_SIZE);
907 }
908
909 void
910 LibDHCP::initVendorOptsIsc6() {
911 initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_OPTION_DEFINITIONS,
912 ISC_V6_OPTION_DEFINITIONS_SIZE);
913 }
914
915 uint32_t
916 LibDHCP::optionSpaceToVendorId(const std::string& option_space) {
917 // 8 is a minimal length of "vendor-X" format
918 if ((option_space.size() < 8) || (option_space.substr(0,7) != "vendor-")) {
919 return (0);
920 }
921
922 int64_t check;
923 try {
924 // text after "vendor-", supposedly numbers only
925 std::string x = option_space.substr(7);
926
927 check = boost::lexical_cast<int64_t>(x);
928
929 } catch (const boost::bad_lexical_cast &) {
930 return (0);
931 }
932
933 if ((check < 0) || (check > std::numeric_limits<uint32_t>::max())) {
934 return (0);
935 }
936
937 // value is small enough to fit
938 return (static_cast<uint32_t>(check));
939 }
940
941 void initOptionSpace(OptionDefContainerPtr& defs,
942 const OptionDefParams* params,
943 size_t params_size) {
944 // Container holding vendor options is typically not initialized, as it
945 // is held in map of null pointers. We need to initialize here in this
946 // case.
947 if (!defs) {
948 defs.reset(new OptionDefContainer());
949
950 } else {
951 defs->clear();
952 }
953
954 for (size_t i = 0; i < params_size; ++i) {
955 std::string encapsulates(params[i].encapsulates);
956 if (!encapsulates.empty() && params[i].array) {
957 isc_throw(isc::BadValue, "invalid standard option definition: "
958 << "option with code '" << params[i].code
959 << "' may not encapsulate option space '"
960 << encapsulates << "' because the definition"
961 << " indicates that this option comprises an array"
962 << " of values");
963 }
964
965 // Depending whether an option encapsulates an option space or not
966 // we pick different constructor to create an instance of the option
967 // definition.
968 OptionDefinitionPtr definition;
969 if (encapsulates.empty()) {
970 // Option does not encapsulate any option space.
971 definition.reset(new OptionDefinition(params[i].name,
972 params[i].code,
973 params[i].type,
974 params[i].array));
975 } else {
976 // Option does encapsulate an option space.
977 definition.reset(new OptionDefinition(params[i].name,
978 params[i].code,
979 params[i].type,
980 params[i].encapsulates));
981
982 }
983
984 for (size_t rec = 0; rec < params[i].records_size; ++rec) {
985 definition->addRecordField(params[i].records[rec]);
986 }
987
988 try {
989 definition->validate();
990 } catch (const isc::Exception&) {
991 // This is unlikely event that validation fails and may
992 // be only caused by programming error. To guarantee the
993 // data consistency we clear all option definitions that
994 // have been added so far and pass the exception forward.
995 defs->clear();
996 throw;
997 }
998 defs->push_back(definition);
999 }
1000 }
isc::dhcp::OptionBuffer
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
docsis3_option_defs.h
isc::Unexpected
A generic exception that is thrown when an unexpected error condition occurs.
Definition: exceptions/exceptions.h:153
option_space.h
VENDOR_ID_CABLE_LABS
#define VENDOR_ID_CABLE_LABS
Definition: docsis3_option_defs.h:16
isc::dhcp::DOCSIS3_V6_DEFS
const OptionDefParams DOCSIS3_V6_DEFS[]
Definitions of standard DHCPv6 options.
Definition: docsis3_option_defs.h:46
isc::dhcp::OptionSpaceContainer::addItem
void addItem(const ItemType &item, const Selector &option_space)
Adds a new item to the option_space.
Definition: option_space_container.h:45
isc::dhcp::OptionDefinitionPtr
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
Definition: option_definition.h:51
initOptionSpace
void initOptionSpace(OptionDefContainerPtr &defs, const OptionDefParams *params, size_t params_size)
Definition: libdhcp++.cc:941
libdhcp++.h
option6_ia.h
isc::dhcp::DOCSIS3_V6_DEFS_SIZE
const int DOCSIS3_V6_DEFS_SIZE
Number of option definitions defined.
Definition: docsis3_option_defs.h:61
isc::dhcp::OptionDefContainerTypeRange
std::pair< OptionDefContainerTypeIndex::const_iterator, OptionDefContainerTypeIndex::const_iterator > OptionDefContainerTypeRange
Pair of iterators to represent the range of options definitions having the same option type value.
Definition: option_definition.h:834
DHCP6_OPTION_SPACE
#define DHCP6_OPTION_SPACE
Definition: option_space.h:17
isc::dhcp::OptionSpaceContainer< OptionDefContainer, OptionDefinitionPtr, std::string >
isc::dhcp::DOCSIS3_CLASS_EROUTER
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition: libdhcp++.cc:72
V4V6_BIND_OPTION_SPACE
#define V4V6_BIND_OPTION_SPACE
Definition: option_space.h:22
D6O_VENDOR_OPTS
@ D6O_VENDOR_OPTS
Definition: dhcp6.h:37
isc::dhcp::DHO_DHCP_AGENT_OPTIONS
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
isc::dhcp::OptionCollection
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:41
isc::dhcp::OptionDefinition
Base class representing a DHCP option definition.
Definition: option_definition.h:137
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc::dhcp::OptionDefParams::records_size
size_t records_size
Definition: option_data_types.h:74
dhcp4.h
D6O_RELAY_MSG
@ D6O_RELAY_MSG
Definition: dhcp6.h:29
isc::util
Definition: edns.h:19
isc::dhcp::DOCSIS3_V4_DEFS
const OptionDefParams DOCSIS3_V4_DEFS[]
Definitions of standard DHCPv4 options.
Definition: docsis3_option_defs.h:22
isc::dhcp::OptionDefParams::array
bool array
Definition: option_data_types.h:72
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::dhcp::DOCSIS3_V4_DEFS_SIZE
const int DOCSIS3_V4_DEFS_SIZE
Number of option definitions defined.
Definition: docsis3_option_defs.h:28
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: exceptions/exceptions.h:132
isc::dhcp
Definition: ctrl_dhcp4_srv.cc:75
std_option_defs.h
isc::dhcp::DHO_VENDOR_ENCAPSULATED_OPTIONS
@ DHO_VENDOR_ENCAPSULATED_OPTIONS
Definition: dhcp4.h:112
DHCP4_OPTION_SPACE
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
isc::dhcp::OptionDefContainerNameIndex
OptionDefContainer::nth_index< 2 >::type OptionDefContainerNameIndex
Type of the index #2 - option name.
Definition: option_definition.h:837
isc::util::OutputBuffer
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
isc::util::readUint16
uint16_t readUint16(const void *buffer, size_t length)
Read Unsigned 16-Bit Integer from Buffer.
Definition: io_utilities.h:28
isc::dhcp::SkipRemainingOptionsError
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
isc::dhcp::DHO_END
@ DHO_END
Definition: dhcp4.h:222
option_definition.h
null_option_def_container_
const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer())
isc::dhcp::OptionDefContainer
boost::multi_index_container< OptionDefinitionPtr, boost::multi_index::indexed_by< boost::multi_index::sequenced<>, boost::multi_index::hashed_non_unique< boost::multi_index::const_mem_fun< OptionDefinition, uint16_t, &OptionDefinition::getCode > >, boost::multi_index::hashed_non_unique< boost::multi_index::const_mem_fun< OptionDefinition, std::string, &OptionDefinition::getName > > >> OptionDefContainer
Multi index container for DHCP option definitions.
Definition: option_definition.h:816
isc::dhcp::DHO_PAD
@ DHO_PAD
Definition: dhcp4.h:69
isc::dhcp::VendorOptionDefContainers
std::map< uint32_t, OptionDefContainerPtr > VendorOptionDefContainers
Container that holds various vendor option containers.
Definition: option_definition.h:825
dhcp6.h
buffer.h
option_vendor.h
isc::dhcp::OptionSpaceContainer::getOptionSpaceNames
std::list< Selector > getOptionSpaceNames() const
Get a list of existing option spaces.
Definition: option_space_container.h:76
isc::util::StagedValue
This class implements set/commit mechanism for a single object.
Definition: staged_value.h:32
MAPT_V6_OPTION_SPACE
#define MAPT_V6_OPTION_SPACE
Definition: option_space.h:19
isc::dhcp::OptionVendor
This class represents vendor-specific information option.
Definition: option_vendor.h:30
isc::dhcp::OptionDefContainers
std::map< std::string, OptionDefContainerPtr > OptionDefContainers
Container that holds option definitions for various option spaces.
Definition: option_definition.h:822
isc::dhcp::OptionSpaceContainer::getItems
ItemsContainerPtr getItems(const Selector &option_space) const
Get all items for the particular option space.
Definition: option_space_container.h:60
isc::dhcp::DOCSIS3_CLASS_MODEM
const char * DOCSIS3_CLASS_MODEM
DOCSIS3.0 compatible cable modem.
Definition: libdhcp++.cc:69
isc::dhcp::OptionPtr
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
exceptions.h
LW_V6_OPTION_SPACE
#define LW_V6_OPTION_SPACE
Definition: option_space.h:20
V4V6_RULE_OPTION_SPACE
#define V4V6_RULE_OPTION_SPACE
Definition: option_space.h:21
isc::dhcp::Option::Universe
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
isc::dhcp::OptionDefContainerNameRange
std::pair< OptionDefContainerNameIndex::const_iterator, OptionDefContainerNameIndex::const_iterator > OptionDefContainerNameRange
Pair of iterators to represent the range of options definitions having the same option name.
Definition: option_definition.h:843
option6_iaaddr.h
option.h
isc::dhcp::OptionDefContainerPtr
boost::shared_ptr< OptionDefContainer > OptionDefContainerPtr
Pointer to an option definition container.
Definition: option_definition.h:819
isc::dhcp::Option
Definition: option.h:58
isc::dhcp::OptionDefParams
Parameters being used to make up an option definition.
Definition: option_data_types.h:68
option_int_array.h
MAPE_V6_OPTION_SPACE
#define MAPE_V6_OPTION_SPACE
Definition: option_space.h:18
isc::dhcp::OptionDefContainerTypeIndex
OptionDefContainer::nth_index< 1 >::type OptionDefContainerTypeIndex
Type of the index #1 - option type.
Definition: option_definition.h:828