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