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>
18#include <dhcp/option_space.h>
22#include <util/buffer.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
32using namespace std;
33using namespace isc::dhcp;
34using namespace isc::util;
35
36// static array with factories for options
37std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
38
39// static array with factories for options
40std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
41
42// Static container with DHCPv4 option definitions.
43OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer());
44
45// Static container with DHCPv6 option definitions.
46OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer());
47
48// Static container with option definitions grouped by option space.
49OptionDefContainers LibDHCP::option_defs_;
50
51// Static container with vendor option definitions for DHCPv4.
52VendorOptionDefContainers LibDHCP::vendor4_defs_;
53
54// Static container with vendor option definitions for DHCPv6.
55VendorOptionDefContainers LibDHCP::vendor6_defs_;
56
57// Static container with last resort option definitions for DHCPv4.
58OptionDefContainerPtr LibDHCP::lastresort_defs_(new OptionDefContainer());
59
60// Static container with option definitions created in runtime.
61StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
62
63// Null container.
65
66// Those two vendor classes are used for cable modems:
67
69const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
70
72const 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
77 const OptionDefParams* params,
78 size_t params_size);
79
81LibDHCP::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 }
104}
105
107LibDHCP::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
118 }
119 return (def->second);
120}
121
123LibDHCP::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
139 }
140 return (def->second);
141}
142
144LibDHCP::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
155LibDHCP::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
166LibDHCP::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
184LibDHCP::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
205LibDHCP::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
217LibDHCP::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
229LibDHCP::getRuntimeOptionDefs(const std::string& space) {
230 return (runtime_option_defs_.getValue().getItems(space));
231}
232
233void
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
249void
251 runtime_option_defs_.reset();
252}
253
254void
256 runtime_option_defs_.revert();
257}
258
259void
261 runtime_option_defs_.commit();
262}
263
265LibDHCP::getLastResortOptionDef(const std::string& space, const uint16_t code) {
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
277LibDHCP::getLastResortOptionDef(const std::string& space, const std::string& name) {
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
289LibDHCP::getLastResortOptionDefs(const std::string& space) {
290 if (space == DHCP4_OPTION_SPACE) {
291 return (lastresort_defs_);
292 }
294}
295
296bool
297LibDHCP::shouldDeferOptionUnpack(const std::string& space, const uint16_t code) {
298 return ((space == DHCP4_OPTION_SPACE) &&
300 ((code >= 224) && (code <= 254))));
301}
302
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
329 const std::string& option_space,
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.
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
469 const std::string& option_space,
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.
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
589size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
590 const OptionBuffer& buf,
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) {
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) {
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
684size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
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.
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).
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) {
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
788void
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) {
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
821void
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
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
869void
870LibDHCP::initStdOptionDefs4() {
871 initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS,
872 STANDARD_V4_OPTION_DEFINITIONS_SIZE);
873}
874
875void
876LibDHCP::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
891void
892LibDHCP::initLastResortOptionDefs() {
893 initOptionSpace(lastresort_defs_, LAST_RESORT_V4_OPTION_DEFINITIONS,
894 LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE);
895}
896
897void
898LibDHCP::initVendorOptsDocsis4() {
901}
902
903void
904LibDHCP::initVendorOptsDocsis6() {
907}
908
909void
910LibDHCP::initVendorOptsIsc6() {
911 initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_OPTION_DEFINITIONS,
912 ISC_V6_OPTION_DEFINITIONS_SIZE);
913}
914
915uint32_t
916LibDHCP::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
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}
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
A generic exception that is thrown when an unexpected error condition occurs.
static void OptionFactoryRegister(Option::Universe u, uint16_t type, Option::Factory *factory)
Registers factory method that produces options of specific option types.
Definition: libdhcp++.cc:830
static const OptionDefContainerPtr & getOptionDefs(const std::string &space)
Returns collection of option definitions.
Definition: libdhcp++.cc:81
static size_t unpackVendorOptions6(const uint32_t vendor_id, const OptionBuffer &buf, isc::dhcp::OptionCollection &options)
Parses provided buffer as DHCPv6 vendor options and creates Option objects.
Definition: libdhcp++.cc:589
static bool shouldDeferOptionUnpack(const std::string &space, const uint16_t code)
Checks if an option unpacking has to be deferred.
Definition: libdhcp++.cc:297
static isc::dhcp::OptionPtr optionFactory(isc::dhcp::Option::Universe u, uint16_t type, const OptionBuffer &buf)
Factory function to create instance of option.
Definition: libdhcp++.cc:304
static void setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition: libdhcp++.cc:234
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:144
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code)
Returns vendor option definition for a given vendor-id and code.
Definition: libdhcp++.cc:184
static OptionDefContainerPtr getLastResortOptionDefs(const std::string &space)
Returns last resort option definitions for specified option space name.
Definition: libdhcp++.cc:289
static void packOptions4(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv4 options in a buffer.
Definition: libdhcp++.cc:789
static OptionDefContainerPtr getRuntimeOptionDefs(const std::string &space)
Returns runtime (non-standard) option definitions for specified option space name.
Definition: libdhcp++.cc:229
static void commitRuntimeOptionDefs()
Commits runtime option definitions.
Definition: libdhcp++.cc:260
static const OptionDefContainerPtr & getVendorOption4Defs(const uint32_t vendor_id)
Returns v4 option definitions for a given vendor.
Definition: libdhcp++.cc:107
static void clearRuntimeOptionDefs()
Removes runtime option definitions.
Definition: libdhcp++.cc:250
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition: libdhcp++.cc:255
static const OptionDefContainerPtr & getVendorOption6Defs(const uint32_t vendor_id)
Returns v6 option definitions for a given vendor.
Definition: libdhcp++.cc:123
static size_t unpackOptions4(const OptionBuffer &buf, const std::string &option_space, isc::dhcp::OptionCollection &options, std::list< uint16_t > &deferred)
Parses provided buffer as DHCPv4 options and creates Option objects.
Definition: libdhcp++.cc:468
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
Definition: libdhcp++.cc:916
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition: libdhcp++.cc:205
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)
Parses provided buffer as DHCPv6 options and creates Option objects.
Definition: libdhcp++.cc:328
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
Definition: libdhcp++.cc:822
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition: libdhcp++.cc:265
static size_t unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer &buf, isc::dhcp::OptionCollection &options)
Parses provided buffer as DHCPv4 vendor options and creates Option objects.
Definition: libdhcp++.cc:684
Base class representing a DHCP option definition.
void addItem(const ItemType &item, const Selector &option_space)
Adds a new item to the option_space.
std::list< Selector > getOptionSpaceNames() const
Get a list of existing option spaces.
ItemsContainerPtr getItems(const Selector &option_space) const
Get all items for the particular option space.
This class represents vendor-specific information option.
Definition: option_vendor.h:30
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer &buf)
a factory function prototype
Definition: option.h:80
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
This class implements set/commit mechanism for a single object.
Definition: staged_value.h:32
@ D6O_RELAY_MSG
Definition: dhcp6.h:29
@ D6O_VENDOR_OPTS
Definition: dhcp6.h:37
#define VENDOR_ID_CABLE_LABS
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void initOptionSpace(OptionDefContainerPtr &defs, const OptionDefParams *params, size_t params_size)
Definition: libdhcp++.cc:941
const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer())
@ DHO_END
Definition: dhcp4.h:222
@ DHO_PAD
Definition: dhcp4.h:69
@ DHO_DHCP_AGENT_OPTIONS
Definition: dhcp4.h:151
@ DHO_VENDOR_ENCAPSULATED_OPTIONS
Definition: dhcp4.h:112
const int DOCSIS3_V6_DEFS_SIZE
Number of option definitions defined.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:41
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition: libdhcp++.cc:72
std::map< uint32_t, OptionDefContainerPtr > VendorOptionDefContainers
Container that holds various vendor option containers.
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
const OptionDefParams DOCSIS3_V6_DEFS[]
Definitions of standard DHCPv6 options.
std::pair< OptionDefContainerNameIndex::const_iterator, OptionDefContainerNameIndex::const_iterator > OptionDefContainerNameRange
Pair of iterators to represent the range of options definitions having the same option name.
const char * DOCSIS3_CLASS_MODEM
DOCSIS3.0 compatible cable modem.
Definition: libdhcp++.cc:69
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.
const int DOCSIS3_V4_DEFS_SIZE
Number of option definitions defined.
std::map< std::string, OptionDefContainerPtr > OptionDefContainers
Container that holds option definitions for various option spaces.
OptionDefContainer::nth_index< 2 >::type OptionDefContainerNameIndex
Type of the index #2 - option name.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
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.
const OptionDefParams DOCSIS3_V4_DEFS[]
Definitions of standard DHCPv4 options.
OptionDefContainer::nth_index< 1 >::type OptionDefContainerTypeIndex
Type of the index #1 - option type.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:38
boost::shared_ptr< OptionDefContainer > OptionDefContainerPtr
Pointer to an option definition container.
Definition: edns.h:19
uint16_t readUint16(const void *buffer, size_t length)
Read Unsigned 16-Bit Integer from Buffer.
Definition: io_utilities.h:28
#define V4V6_BIND_OPTION_SPACE
Definition: option_space.h:22
#define DHCP4_OPTION_SPACE
Definition: option_space.h:16
#define V4V6_RULE_OPTION_SPACE
Definition: option_space.h:21
#define MAPE_V6_OPTION_SPACE
Definition: option_space.h:18
#define LW_V6_OPTION_SPACE
Definition: option_space.h:20
#define DHCP6_OPTION_SPACE
Definition: option_space.h:17
#define MAPT_V6_OPTION_SPACE
Definition: option_space.h:19
Parameters being used to make up an option definition.