Kea 2.7.5
libdhcp++.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2024 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>
19#include <dhcp/option_custom.h>
24#include <util/buffer.h>
25#include <util/io.h>
26
27#include <boost/foreach.hpp>
28#include <boost/lexical_cast.hpp>
29#include <boost/shared_array.hpp>
30#include <boost/shared_ptr.hpp>
31
32#include <limits>
33#include <list>
34#include <unordered_map>
35#include <vector>
36
37using namespace std;
38using namespace isc::dhcp;
39using namespace isc::util;
40
41namespace isc {
42namespace dhcp {
43
44namespace {
45
49const OptionDefParamsEncapsulation OPTION_DEF_PARAMS[] = {
50 { STANDARD_V4_OPTION_DEFINITIONS, STANDARD_V4_OPTION_DEFINITIONS_SIZE, DHCP4_OPTION_SPACE },
51 { STANDARD_V6_OPTION_DEFINITIONS, STANDARD_V6_OPTION_DEFINITIONS_SIZE, DHCP6_OPTION_SPACE },
54 { ISC_V6_OPTION_DEFINITIONS, ISC_V6_OPTION_DEFINITIONS_SIZE, ISC_V6_OPTION_SPACE },
55 { MAPE_V6_OPTION_DEFINITIONS, MAPE_V6_OPTION_DEFINITIONS_SIZE, MAPE_V6_OPTION_SPACE },
56 { MAPT_V6_OPTION_DEFINITIONS, MAPT_V6_OPTION_DEFINITIONS_SIZE, MAPT_V6_OPTION_SPACE },
57 { LW_V6_OPTION_DEFINITIONS, LW_V6_OPTION_DEFINITIONS_SIZE, LW_V6_OPTION_SPACE },
58 { V4V6_RULE_OPTION_DEFINITIONS, V4V6_RULE_OPTION_DEFINITIONS_SIZE, V4V6_RULE_OPTION_SPACE },
59 { V4V6_BIND_OPTION_DEFINITIONS, V4V6_BIND_OPTION_DEFINITIONS_SIZE, V4V6_BIND_OPTION_SPACE },
60 { V6_NTP_SERVER_DEFINITIONS, V6_NTP_SERVER_DEFINITIONS_SIZE, V6_NTP_SERVER_SPACE },
61 { CABLELABS_CLIENT_CONF_DEFINITIONS, CABLELABS_CLIENT_CONF_DEFINITIONS_SIZE,
63 { LAST_RESORT_V4_OPTION_DEFINITIONS, LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE, LAST_RESORT_V4_OPTION_SPACE },
64 { DHCP_AGENT_OPTION_DEFINITIONS, DHCP_AGENT_OPTION_DEFINITIONS_SIZE, DHCP_AGENT_OPTION_SPACE },
65 { NULL, 0, "" }
66};
67
68} // namespace
69
70} // namespace dhcp
71} // namespace isc
72
73// static array with factories for options
74map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
75
76// static array with factories for options
77map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
78
79// Static container with option definitions grouped by option space.
80OptionDefContainers LibDHCP::option_defs_;
81
82// Static container with option definitions created in runtime.
83StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
84
85// Null container.
87
88// Those two vendor classes are used for cable modems:
89
91const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
92
94const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
95
96// Let's keep it in .cc file. Moving it to .h would require including optionDefParams
97// definitions there
99 const OptionDefParams* params,
100 size_t params_size);
101
102bool LibDHCP::initialized_ = LibDHCP::initOptionDefs();
103
105LibDHCP::getOptionDefs(const string& space) {
106 auto const& container = option_defs_.find(space);
107 if (container != option_defs_.end()) {
108 return (container->second);
109 }
110
112}
113
115LibDHCP::getVendorOptionDefs(const Option::Universe u, const uint32_t vendor_id) {
116 if (Option::V4 == u) {
117 if (VENDOR_ID_CABLE_LABS == vendor_id) {
119 }
120 } else if (Option::V6 == u) {
121 if (VENDOR_ID_CABLE_LABS == vendor_id) {
123 } else if (ENTERPRISE_ID_ISC == vendor_id) {
125 }
126 }
127
129}
130
132LibDHCP::getOptionDef(const string& space, const uint16_t code) {
133 const OptionDefContainerPtr& defs = getOptionDefs(space);
134 const OptionDefContainerTypeIndex& idx = defs->get<1>();
135 const OptionDefContainerTypeRange& range = idx.equal_range(code);
136 if (range.first != range.second) {
137 return (*range.first);
138 }
139
140 return (OptionDefinitionPtr());
141}
142
144LibDHCP::getOptionDef(const string& space, const string& name) {
145 const OptionDefContainerPtr& defs = getOptionDefs(space);
146 const OptionDefContainerNameIndex& idx = defs->get<2>();
147 const OptionDefContainerNameRange& range = idx.equal_range(name);
148 if (range.first != range.second) {
149 return (*range.first);
150 }
151
152 return (OptionDefinitionPtr());
153}
154
156LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
157 const string& name) {
158 const OptionDefContainerPtr& defs = getVendorOptionDefs(u, vendor_id);
159
160 if (!defs) {
161 return (OptionDefinitionPtr());
162 }
163
164 const OptionDefContainerNameIndex& idx = defs->get<2>();
165 const OptionDefContainerNameRange& range = idx.equal_range(name);
166 if (range.first != range.second) {
167 return (*range.first);
168 }
169
170 return (OptionDefinitionPtr());
171}
172
174LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
175 const uint16_t code) {
176 const OptionDefContainerPtr& defs = getVendorOptionDefs(u, vendor_id);
177
178 if (!defs) {
179 // Weird universe or unknown vendor_id. We don't care. No definitions
180 // one way or another
181 // What is it anyway?
182 return (OptionDefinitionPtr());
183 }
184
185 const OptionDefContainerTypeIndex& idx = defs->get<1>();
186 const OptionDefContainerTypeRange& range = idx.equal_range(code);
187 if (range.first != range.second) {
188 return (*range.first);
189 }
190
191 return (OptionDefinitionPtr());
192}
193
195LibDHCP::getRuntimeOptionDef(const string& space, const uint16_t code) {
196 OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
197 const OptionDefContainerTypeIndex& index = container->get<1>();
198 const OptionDefContainerTypeRange& range = index.equal_range(code);
199 if (range.first != range.second) {
200 return (*range.first);
201 }
202
203 return (OptionDefinitionPtr());
204}
205
207LibDHCP::getRuntimeOptionDef(const string& space, const string& name) {
208 OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
209 const OptionDefContainerNameIndex& index = container->get<2>();
210 const OptionDefContainerNameRange& range = index.equal_range(name);
211 if (range.first != range.second) {
212 return (*range.first);
213 }
214
215 return (OptionDefinitionPtr());
216}
217
219LibDHCP::getRuntimeOptionDefs(const string& space) {
220 return (runtime_option_defs_.getValue().getItems(space));
221}
222
223void
225 OptionDefSpaceContainer defs_copy;
226 list<string> option_space_names = defs.getOptionSpaceNames();
227 for (auto const& name : option_space_names) {
228 OptionDefContainerPtr container = defs.getItems(name);
229 for (auto const& def : *container) {
230 OptionDefinitionPtr def_copy(new OptionDefinition(*def));
231 defs_copy.addItem(def_copy);
232 }
233 }
234 runtime_option_defs_ = defs_copy;
235}
236
237void
239 runtime_option_defs_.reset();
240}
241
242void
244 runtime_option_defs_.revert();
245}
246
247void
249 runtime_option_defs_.commit();
250}
251
253LibDHCP::getLastResortOptionDef(const string& space, const uint16_t code) {
255 const OptionDefContainerTypeIndex& index = container->get<1>();
256 const OptionDefContainerTypeRange& range = index.equal_range(code);
257 if (range.first != range.second) {
258 return (*range.first);
259 }
260
261 return (OptionDefinitionPtr());
262}
263
265LibDHCP::getLastResortOptionDef(const string& space, const string& name) {
267 const OptionDefContainerNameIndex& index = container->get<2>();
268 const OptionDefContainerNameRange& range = index.equal_range(name);
269 if (range.first != range.second) {
270 return (*range.first);
271 }
272
273 return (OptionDefinitionPtr());
274}
275
278 if (space == DHCP4_OPTION_SPACE) {
280 }
281
283}
284
285bool
286LibDHCP::shouldDeferOptionUnpack(const string& space, const uint16_t code) {
287 return ((space == DHCP4_OPTION_SPACE) &&
289 ((code >= 224) && (code <= 254))));
290}
291
294 uint16_t type,
295 const OptionBuffer& buf) {
296 FactoryMap::iterator it;
297 if (u == Option::V4) {
298 it = v4factories_.find(type);
299 if (it == v4factories_.end()) {
300 isc_throw(BadValue, "factory function not registered "
301 "for DHCP v4 option type " << type);
302 }
303 } else if (u == Option::V6) {
304 it = v6factories_.find(type);
305 if (it == v6factories_.end()) {
306 isc_throw(BadValue, "factory function not registered "
307 "for DHCPv6 option type " << type);
308 }
309 } else {
310 isc_throw(BadValue, "invalid universe specified (expected "
311 "Option::V4 or Option::V6");
312 }
313 return (it->second(u, type, buf));
314}
315
316size_t
317LibDHCP::unpackOptions6(const OptionBuffer& buf, const string& option_space,
318 OptionCollection& options,
319 size_t* relay_msg_offset /* = 0 */,
320 size_t* relay_msg_len /* = 0 */) {
321 size_t offset = 0;
322 size_t length = buf.size();
323 size_t last_offset = 0;
324
325 // Get the list of standard option definitions.
326 const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
327 // Runtime option definitions for non standard option space and if
328 // the definition doesn't exist within the standard option definitions.
329 const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
330
331 // @todo Once we implement other option spaces we should add else clause
332 // here and gather option definitions for them. For now leaving option_defs
333 // empty will imply creation of generic Option.
334
335 // Get the search indexes #1. It allows to search for option definitions
336 // using option code.
337 const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
338 const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
339
340 // The buffer being read comprises a set of options, each starting with
341 // a two-byte type code and a two-byte length field.
342 while (offset < length) {
343 // Save the current offset for backtracking
344 last_offset = offset;
345
346 // Check if there is room for another option
347 if (offset + 4 > length) {
348 // Still something but smaller than an option
349 return (last_offset);
350 }
351
352 // Parse the option header
353 uint16_t opt_type = readUint16(&buf[offset], 2);
354 offset += 2;
355
356 uint16_t opt_len = readUint16(&buf[offset], 2);
357 offset += 2;
358
359 if (offset + opt_len > length) {
360 // We peeked at the option header of the next option, but
361 // discovered that it would end up beyond buffer end, so
362 // the option is truncated. Hence we can't parse
363 // it. Therefore we revert back by those bytes (as if
364 // we never parsed them).
365 //
366 // @note it is the responsibility of the caller to throw
367 // an exception on partial parsing
368 return (last_offset);
369 }
370
371 if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
372 // remember offset of the beginning of the relay-msg option
373 *relay_msg_offset = offset;
374 *relay_msg_len = opt_len;
375
376 // do not create that relay-msg option
377 offset += opt_len;
378 continue;
379 }
380
381 if (opt_type == D6O_VENDOR_OPTS) {
382 if (offset + 4 > length) {
383 // Truncated vendor-option. We expect at least
384 // 4 bytes for the enterprise-id field. Let's roll back
385 // option code + option length (4 bytes) and return.
386 return (last_offset);
387 }
388
389 // Parse this as vendor option
390 OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
391 buf.begin() + offset + opt_len));
392 options.insert(std::make_pair(opt_type, vendor_opt));
393
394 offset += opt_len;
395 continue;
396 }
397
398 // Get all definitions with the particular option code. Note
399 // that option code is non-unique within this container
400 // however at this point we expect to get one option
401 // definition with the particular code. If more are returned
402 // we report an error.
404 // Number of option definitions returned.
405 size_t num_defs = 0;
406
407 // We previously did the lookup only for dhcp6 option space, but with the
408 // addition of S46 options, we now do it for every space.
409 range = idx.equal_range(opt_type);
410 num_defs = std::distance(range.first, range.second);
411
412 // Standard option definitions do not include the definition for
413 // our option or we're searching for non-standard option. Try to
414 // find the definition among runtime option definitions.
415 if (num_defs == 0) {
416 range = runtime_idx.equal_range(opt_type);
417 num_defs = std::distance(range.first, range.second);
418 }
419
420 OptionPtr opt;
421 if (num_defs > 1) {
422 // Multiple options of the same code are not supported right now!
423 isc_throw(isc::Unexpected, "Internal error: multiple option"
424 " definitions for option type " << opt_type <<
425 " returned. Currently it is not supported to initialize"
426 " multiple option definitions for the same option code."
427 " This will be supported once support for option spaces"
428 " is implemented");
429 } else if (num_defs == 0) {
430 // @todo Don't crash if definition does not exist because
431 // only a few option definitions are initialized right
432 // now. In the future we will initialize definitions for
433 // all options and we will remove this elseif. For now,
434 // return generic option.
435 opt = OptionPtr(new Option(Option::V6, opt_type,
436 buf.begin() + offset,
437 buf.begin() + offset + opt_len));
438 } else {
439 try {
440 // The option definition has been found. Use it to create
441 // the option instance from the provided buffer chunk.
442 const OptionDefinitionPtr& def = *(range.first);
443 isc_throw_assert(def);
444 opt = def->optionFactory(Option::V6, opt_type,
445 buf.begin() + offset,
446 buf.begin() + offset + opt_len);
447 } catch (const SkipThisOptionError&) {
448 opt.reset();
449 } catch (const SkipRemainingOptionsError&) {
450 throw;
451 } catch (const std::exception& ex) {
452 isc_throw(OptionParseError, "opt_type: " << static_cast<uint16_t>(opt_type)
453 << ", opt_len " << static_cast<uint16_t>(opt_len)
454 << ", error: " << ex.what());
455 }
456 }
457
458 // add option to options
459 if (opt) {
460 options.insert(std::make_pair(opt_type, opt));
461 }
462
463 offset += opt_len;
464 }
465
466 last_offset = offset;
467 return (last_offset);
468}
469
470size_t
471LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
472 OptionCollection& options, list<uint16_t>& deferred,
473 bool check) {
474 size_t offset = 0;
475 size_t last_offset = 0;
476
477 // Special case when option_space is dhcp4.
478 bool space_is_dhcp4 = (option_space == DHCP4_OPTION_SPACE);
479
480 // Get the list of standard option definitions.
481 const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
482 // Runtime option definitions for non standard option space and if
483 // the definition doesn't exist within the standard option definitions.
484 const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
485
486 // Get the search indexes #1. It allows to search for option definitions
487 // using option code.
488 const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
489 const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
490
491 // Flexible PAD and END parsing.
492 bool flex_pad = (check && (runtime_idx.count(DHO_PAD) == 0));
493 bool flex_end = (check && (runtime_idx.count(DHO_END) == 0));
494
495 // The buffer being read comprises a set of options, each starting with
496 // a one-byte type code and a one-byte length field.
497
498 // Track seen options in a first pass.
499 vector<uint32_t> count(256, 0);
500 while (offset < buf.size()) {
501 // Get the option type
502 uint8_t opt_type = buf[offset++];
503
504 // DHO_END is a special, one octet long option
505 // Valid in dhcp4 space or when check is true and
506 // there is a sub-option configured for this code.
507 if ((opt_type == DHO_END) && (space_is_dhcp4 || flex_end)) {
508 // Done.
509 break;
510 }
511
512 // DHO_PAD is just a padding after DHO_END. Let's continue parsing
513 // in case we receive a message without DHO_END.
514 // Valid in dhcp4 space or when check is true and
515 // there is a sub-option configured for this code.
516 if ((opt_type == DHO_PAD) && (space_is_dhcp4 || flex_pad)) {
517 continue;
518 }
519
520 if (offset + 1 > buf.size()) {
521 // Error case.
522 break;
523 }
524
525 uint8_t opt_len = buf[offset++];
526 if (offset + opt_len > buf.size()) {
527 // Error case.
528 break;
529 }
530
531 // See below for this special case.
532 if (space_is_dhcp4 && opt_len == 0 && opt_type == DHO_HOST_NAME) {
533 continue;
534 }
535
536 offset += opt_len;
537
538 // Increment count.
539 count[opt_type] += 1;
540 }
541
542 // Fusing option buffers.
543 unordered_map<uint8_t, pair<OptionBuffer, uint32_t>> fused;
544
545 // Second pass.
546 offset = 0;
547 while (offset < buf.size()) {
548 // Save the current offset for backtracking
549 last_offset = offset;
550
551 // Get the option type
552 uint8_t opt_type = buf[offset++];
553
554 // DHO_END is a special, one octet long option
555 // Valid in dhcp4 space or when check is true and
556 // there is a sub-option configured for this code.
557 if ((opt_type == DHO_END) && (space_is_dhcp4 || flex_end)) {
558 // just return. Don't need to add DHO_END option
559 // Don't return offset because it makes this condition
560 // and partial parsing impossible to recognize.
561 return (last_offset);
562 }
563
564 // DHO_PAD is just a padding after DHO_END. Let's continue parsing
565 // in case we receive a message without DHO_END.
566 // Valid in dhcp4 space or when check is true and
567 // there is a sub-option configured for this code.
568 if ((opt_type == DHO_PAD) && (space_is_dhcp4 || flex_pad)) {
569 continue;
570 }
571
572 if (offset + 1 > buf.size()) {
573 // We peeked at the option header of the next option, but
574 // discovered that it would end up beyond buffer end, so
575 // the option is truncated. Hence we can't parse
576 // it. Therefore we revert back (as if we never parsed it).
577 //
578 // @note it is the responsibility of the caller to throw
579 // an exception on partial parsing
580 return (last_offset);
581 }
582
583 uint8_t opt_len = buf[offset++];
584 if (offset + opt_len > buf.size()) {
585 // We peeked at the option header of the next option, but
586 // discovered that it would end up beyond buffer end, so
587 // the option is truncated. Hence we can't parse
588 // it. Therefore we revert back (as if we never parsed it).
589 return (last_offset);
590 }
591
592 // While an empty Host Name option is non-RFC compliant, some clients
593 // do send it. In the spirit of being liberal, we'll just drop it,
594 // rather than the dropping the whole packet. We do not have a
595 // way to log this from here but meh... a PCAP will show it arriving,
596 // and we know we drop it.
597 if (space_is_dhcp4 && opt_len == 0 && opt_type == DHO_HOST_NAME) {
598 continue;
599 }
600
601 OptionBuffer obuf(buf.begin() + offset, buf.begin() + offset + opt_len);
602 offset += opt_len;
603
604 // Concatenate multiple instance of an option.
605 uint32_t opt_count = count[opt_type];
606 if (opt_count > 1) {
607 OptionBuffer& previous = fused[opt_type].first;
608 previous.insert(previous.end(), obuf.begin(), obuf.end());
609 uint32_t& already_seen = fused[opt_type].second;
610 ++already_seen;
611 if (already_seen != opt_count) {
612 continue;
613 } else {
614 // last occurrence: build the option.
615 obuf = previous;
616 }
617 }
618
619 // Get all definitions with the particular option code. Note
620 // that option code is non-unique within this container
621 // however at this point we expect to get one option
622 // definition with the particular code. If more are returned
623 // we report an error.
625 // Number of option definitions returned.
626 size_t num_defs = 0;
627
628 // Previously we did the lookup only for "dhcp4" option space, but there
629 // may be standard options in other spaces (e.g. radius). So we now do
630 // the lookup for every space.
631 range = idx.equal_range(opt_type);
632 num_defs = std::distance(range.first, range.second);
633
634 // Standard option definitions do not include the definition for
635 // our option or we're searching for non-standard option. Try to
636 // find the definition among runtime option definitions.
637 if (num_defs == 0) {
638 range = runtime_idx.equal_range(opt_type);
639 num_defs = std::distance(range.first, range.second);
640 }
641
642 // Check if option unpacking must be deferred
643 if (shouldDeferOptionUnpack(option_space, opt_type)) {
644 num_defs = 0;
645 // Store deferred option only once.
646 bool found = false;
647 for (auto const& existing : deferred) {
648 if (existing == opt_type) {
649 found = true;
650 break;
651 }
652 }
653 if (!found) {
654 deferred.push_back(opt_type);
655 }
656 }
657
658 if (space_is_dhcp4 &&
659 (opt_type == DHO_VIVSO_SUBOPTIONS ||
660 opt_type == DHO_VIVCO_SUBOPTIONS)) {
661 num_defs = 0;
662 }
663
664 OptionPtr opt;
665 if (num_defs > 1) {
666 // Multiple options of the same code are not supported right now!
667 isc_throw(isc::Unexpected, "Internal error: multiple option"
668 " definitions for option type " <<
669 static_cast<int>(opt_type) <<
670 " returned. Currently it is not supported to initialize"
671 " multiple option definitions for the same option code."
672 " This will be supported once support for option spaces"
673 " is implemented");
674 } else if (num_defs == 0) {
675 opt = OptionPtr(new Option(Option::V4, opt_type, obuf));
676 } else {
677 try {
678 // The option definition has been found. Use it to create
679 // the option instance from the provided buffer chunk.
680 const OptionDefinitionPtr& def = *(range.first);
681 isc_throw_assert(def);
682 opt = def->optionFactory(Option::V4, opt_type, obuf);
683 } catch (const SkipThisOptionError&) {
684 opt.reset();
685 } catch (const SkipRemainingOptionsError&) {
686 throw;
687 } catch (const std::exception& ex) {
688 isc_throw(OptionParseError, "opt_type: " << static_cast<uint16_t>(opt_type)
689 << ", opt_len: " << static_cast<uint16_t>(opt_len)
690 << ", error: " << ex.what());
691 }
692 }
693
694 // If we have the option, insert it
695 if (opt) {
696 options.insert(std::make_pair(opt_type, opt));
697 }
698 }
699 last_offset = offset;
700 return (last_offset);
701}
702
703namespace { // Anonymous namespace.
704
705// VIVCO part of extendVendorOptions4.
706
707void
708extendVivco(OptionCollection& options) {
709 typedef vector<OpaqueDataTuple> TuplesCollection;
710 map<uint32_t, TuplesCollection> vendors_tuples;
711 auto const& range = options.equal_range(DHO_VIVCO_SUBOPTIONS);
712 BOOST_FOREACH(auto const& it, range) {
713 uint32_t offset = 0;
714 auto const& data = it.second->getData();
715 size_t size;
716 while ((size = data.size() - offset) != 0) {
717 if (size < sizeof(uint32_t)) {
718 options.erase(DHO_VIVCO_SUBOPTIONS);
720 "Truncated vendor-class information option"
721 << ", length=" << size);
722 }
723 uint32_t vendor_id = readUint32(&data[offset], data.size());
724 offset += 4;
725 try {
726 // From OptionVendorClass::unpack.
728 data.begin() + offset, data.end());
729 vendors_tuples[vendor_id].push_back(tuple);
730 offset += tuple.getTotalLength();
731 } catch (const OpaqueDataTupleError&) {
732 // Ignore this kind of error and continue.
733 break;
734 } catch (const isc::Exception&) {
735 options.erase(DHO_VIVCO_SUBOPTIONS);
736 throw;
737 }
738 }
739 }
740 if (vendors_tuples.empty()) {
741 return;
742 }
743 // Delete the initial option.
744 options.erase(DHO_VIVCO_SUBOPTIONS);
745 // Create a new instance of OptionVendor for each enterprise ID.
746 for (auto const& vendor : vendors_tuples) {
747 if (vendor.second.empty()) {
748 continue;
749 }
751 vendor.first));
752 for (size_t i = 0; i < vendor.second.size(); ++i) {
753 if (i == 0) {
754 vendor_opt->setTuple(0, vendor.second[0]);
755 } else {
756 vendor_opt->addTuple(vendor.second[i]);
757 }
758 }
759 // Add the new instance of VendorOption with respective sub-options for
760 // this enterprise ID.
761 options.insert(std::make_pair(DHO_VIVCO_SUBOPTIONS, vendor_opt));
762 }
763}
764
765// VIVSO part of extendVendorOptions4.
766
767void
768extendVivso(OptionCollection& options) {
769 map<uint32_t, OptionCollection> vendors_data;
770 auto const& range = options.equal_range(DHO_VIVSO_SUBOPTIONS);
771 BOOST_FOREACH(auto const& it, range) {
772 uint32_t offset = 0;
773 auto const& data = it.second->getData();
774 size_t size;
775 while ((size = data.size() - offset) != 0) {
776 if (size < sizeof(uint32_t)) {
777 options.erase(DHO_VIVSO_SUBOPTIONS);
779 "Truncated vendor-specific information option"
780 << ", length=" << size);
781 }
782 uint32_t vendor_id = readUint32(&data[offset], data.size());
783 offset += 4;
784 const OptionBuffer vendor_buffer(data.begin() + offset, data.end());
785 try {
786 offset += LibDHCP::unpackVendorOptions4(vendor_id, vendor_buffer,
787 vendors_data[vendor_id]);
788 } catch (const SkipThisOptionError&) {
789 // Ignore this kind of error and continue.
790 break;
791 } catch (const isc::Exception&) {
792 options.erase(DHO_VIVSO_SUBOPTIONS);
793 throw;
794 }
795 }
796 }
797 if (vendors_data.empty()) {
798 return;
799 }
800 // Delete the initial option.
801 options.erase(DHO_VIVSO_SUBOPTIONS);
802 // Create a new instance of OptionVendor for each enterprise ID.
803 for (auto const& vendor : vendors_data) {
804 OptionVendorPtr vendor_opt(new OptionVendor(Option::V4, vendor.first));
805 for (auto const& option : vendor.second) {
806 vendor_opt->addOption(option.second);
807 }
808 // Add the new instance of VendorOption with respective sub-options for
809 // this enterprise ID.
810 options.insert(std::make_pair(DHO_VIVSO_SUBOPTIONS, vendor_opt));
811 }
812}
813
814} // end of anonymous namespace.
815
816void
818 extendVivco(options);
819 extendVivso(options);
820}
821
822size_t
823LibDHCP::unpackVendorOptions6(const uint32_t vendor_id, const OptionBuffer& buf,
824 OptionCollection& options) {
825 size_t offset = 0;
826 size_t length = buf.size();
827
828 // Get the list of option definitions for this particular vendor-id
829 const OptionDefContainerPtr& option_defs =
831
832 // Get the search index #1. It allows to search for option definitions
833 // using option code. If there's no such vendor-id space, we're out of luck
834 // anyway.
835 const OptionDefContainerTypeIndex* idx = NULL;
836 if (option_defs) {
837 idx = &(option_defs->get<1>());
838 }
839
840 // The buffer being read comprises a set of options, each starting with
841 // a two-byte type code and a two-byte length field.
842 while (offset < length) {
843 if (offset + 4 > length) {
845 "Vendor option parse failed: truncated header");
846 }
847
848 uint16_t opt_type = readUint16(&buf[offset], 2);
849 offset += 2;
850
851 uint16_t opt_len = readUint16(&buf[offset], 2);
852 offset += 2;
853
854 if (offset + opt_len > length) {
856 "Vendor option parse failed. Tried to parse "
857 << offset + opt_len << " bytes from " << length
858 << "-byte long buffer.");
859 }
860
861 OptionPtr opt;
862 opt.reset();
863
864 // If there is a definition for such a vendor option...
865 if (idx) {
866 // Get all definitions with the particular option
867 // code. Note that option code is non-unique within this
868 // container however at this point we expect to get one
869 // option definition with the particular code. If more are
870 // returned we report an error.
871 const OptionDefContainerTypeRange& range =
872 idx->equal_range(opt_type);
873 // Get the number of returned option definitions for the
874 // option code.
875 size_t num_defs = std::distance(range.first, range.second);
876
877 if (num_defs > 1) {
878 // Multiple options of the same code are not supported
879 // right now!
880 isc_throw(isc::Unexpected, "Internal error: multiple option"
881 " definitions for option type " << opt_type <<
882 " returned. Currently it is not supported to"
883 " initialize multiple option definitions for the"
884 " same option code. This will be supported once"
885 " support for option spaces is implemented");
886 } else if (num_defs == 1) {
887 // The option definition has been found. Use it to create
888 // the option instance from the provided buffer chunk.
889 const OptionDefinitionPtr& def = *(range.first);
890 isc_throw_assert(def);
891 opt = def->optionFactory(Option::V6, opt_type,
892 buf.begin() + offset,
893 buf.begin() + offset + opt_len);
894 }
895 }
896
897 // This can happen in one of 2 cases:
898 // 1. we do not have definitions for that vendor-space
899 // 2. we do have definitions, but that particular option was
900 // not defined
901
902 if (!opt) {
903 opt = OptionPtr(new Option(Option::V6, opt_type,
904 buf.begin() + offset,
905 buf.begin() + offset + opt_len));
906 }
907
908 // add option to options
909 if (opt) {
910 options.insert(std::make_pair(opt_type, opt));
911 }
912 offset += opt_len;
913 }
914
915 return (offset);
916}
917
918size_t
919LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
920 OptionCollection& options) {
921 size_t offset = 0;
922
923 // Get the list of standard option definitions.
924 const OptionDefContainerPtr& option_defs =
926 // Get the search index #1. It allows to search for option definitions
927 // using option code.
928 const OptionDefContainerTypeIndex* idx = NULL;
929 if (option_defs) {
930 idx = &(option_defs->get<1>());
931 }
932
933 // The buffer being read comprises a set of options, each starting with
934 // a one-byte type code and a one-byte length field.
935 while (offset < buf.size()) {
936 // Note that Vendor-Specific info option (RFC3925) has a
937 // different option format than Vendor-Spec info for
938 // DHCPv6. (there's additional layer of data-length)
939 uint8_t data_len = buf[offset++];
940
941 if (offset + data_len > buf.size()) {
942 // The option is truncated.
944 "Attempt to parse truncated vendor option");
945 }
946
947 uint8_t offset_end = offset + data_len;
948
949 // beginning of data-chunk parser
950 while (offset < offset_end) {
951 uint8_t opt_type = buf[offset++];
952
953 // No DHO_END or DHO_PAD in vendor options
954
955 if (offset + 1 > offset_end) {
956 // opt_type must be cast to integer so as it is not
957 // treated as unsigned char value (a number is
958 // presented in error message).
960 "Attempt to parse truncated vendor option "
961 << static_cast<int>(opt_type));
962 }
963
964 uint8_t opt_len = buf[offset++];
965 if (offset + opt_len > offset_end) {
967 "Option parse failed. Tried to parse "
968 << offset + opt_len << " bytes from " << buf.size()
969 << "-byte long buffer.");
970 }
971
972 OptionPtr opt;
973 opt.reset();
974
975 if (idx) {
976 // Get all definitions with the particular option
977 // code. Note that option code is non-unique within
978 // this container however at this point we expect to
979 // get one option definition with the particular
980 // code. If more are returned we report an error.
981 const OptionDefContainerTypeRange& range =
982 idx->equal_range(opt_type);
983 // Get the number of returned option definitions for
984 // the option code.
985 size_t num_defs = std::distance(range.first, range.second);
986
987 if (num_defs > 1) {
988 // Multiple options of the same code are not
989 // supported right now!
990 isc_throw(isc::Unexpected, "Internal error: multiple"
991 " option definitions for option type "
992 << opt_type << " returned. Currently it is"
993 " not supported to initialize multiple option"
994 " definitions for the same option code."
995 " This will be supported once support for"
996 " option spaces is implemented");
997 } else if (num_defs == 1) {
998 // The option definition has been found. Use it to create
999 // the option instance from the provided buffer chunk.
1000 const OptionDefinitionPtr& def = *(range.first);
1001 isc_throw_assert(def);
1002 opt = def->optionFactory(Option::V4, opt_type,
1003 buf.begin() + offset,
1004 buf.begin() + offset + opt_len);
1005 }
1006 }
1007
1008 if (!opt) {
1009 opt = OptionPtr(new Option(Option::V4, opt_type,
1010 buf.begin() + offset,
1011 buf.begin() + offset + opt_len));
1012 }
1013
1014 options.insert(std::make_pair(opt_type, opt));
1015 offset += opt_len;
1016
1017 } // end of data-chunk
1018
1019 break; // end of the vendor block.
1020 }
1021 return (offset);
1022}
1023
1024void
1026 bool top, bool check) {
1027 OptionCollection agent;
1028 OptionPtr end;
1029
1030 // We only look for type when we're the top level
1031 // call that starts packing for options for a packet.
1032 // This way we avoid doing type logic in all ensuing
1033 // recursive calls.
1034 if (top) {
1035 auto x = options.find(DHO_DHCP_MESSAGE_TYPE);
1036 if (x != options.end()) {
1037 x->second->pack(buf, check);
1038 }
1039 }
1040
1041 for (auto const& option : options) {
1042 // TYPE is already done, RAI and END options must be last.
1043 switch (option.first) {
1045 break;
1047 agent.insert(make_pair(DHO_DHCP_AGENT_OPTIONS, option.second));
1048 break;
1049 case DHO_END:
1050 end = option.second;
1051 break;
1052 default:
1053 option.second->pack(buf, check);
1054 break;
1055 }
1056 }
1057
1058 // Add the RAI option if it exists.
1059 for (auto const& option : agent) {
1060 option.second->pack(buf, check);
1061 }
1062
1063 // And at the end the END option.
1064 if (end) {
1065 end->pack(buf, check);
1066 }
1067}
1068
1069bool
1071 ScopedOptionsCopyContainer& scoped_options,
1072 uint32_t used) {
1073 bool result = false;
1074 // We need to loop until all options have been split.
1075 uint32_t tries = 0;
1076 for (;; tries++) {
1077 // Let's not do this forever if there is a bug hiding here somewhere...
1078 // 65535 times should be enough for any packet load...
1079 if (tries == std::numeric_limits<uint16_t>::max()) {
1080 isc_throw(Unexpected, "packet split failed after trying "
1081 << tries << " times.");
1082 }
1083 bool found = false;
1084 // Make a copy of the options so we can safely iterate over the
1085 // old container.
1086 OptionCollection copy = options;
1087 // Iterate over all options in the container.
1088 for (auto const& option : options) {
1089 OptionPtr candidate = option.second;
1090 OptionCollection& sub_options = candidate->getMutableOptions();
1091 // Split suboptions recursively, if any.
1092 OptionCollection distinct_options;
1093 bool updated = false;
1094 bool found_suboptions = false;
1095 // There are 3 cases when the total size is larger than (255 - used):
1096 // 1. option has no suboptions and has large data
1097 // 2. option has large suboptions and has no data
1098 // 3. option has both options and suboptions:
1099 // 3.1. suboptions are large and data is large
1100 // 3.2. suboptions are large and data is small
1101 // 3.3. suboptions are small and data is large
1102 // 3.4. suboptions are small and data is small but combined they are large
1103 // All other combinations reside in total size smaller than (255 - used):
1104 // 4. no split of any suboption or data:
1105 // 4.1 option has no suboptions and has small data
1106 // 4.2 option has small suboptions and has no data
1107 // 4.3 option has both small suboptions and small data
1108 // 4.4 option has no suboptions and has no data
1109 if (sub_options.size()) {
1110 // The 2. and 3. and 4.2 and 4.3 cases are handled here (the suboptions part).
1111 ScopedOptionsCopyPtr candidate_scoped_options(new ScopedSubOptionsCopy(candidate));
1112 found_suboptions = LibDHCP::splitOptions4(sub_options, scoped_options,
1113 used + candidate->getHeaderLen());
1114 // There are 3 cases here:
1115 // 2. option has large suboptions and has no data
1116 // 3. option has both options and suboptions:
1117 // 3.1. suboptions are large and data is large so there is suboption splitting
1118 // and found_suboptions is true
1119 // 3.2. suboptions are large and data is small so there is suboption splitting
1120 // and found_suboptions is true
1121 // 3.3. suboptions are small and data is large so there is no suboption splitting
1122 // and found_suboptions is false
1123 // 3.4. suboptions are small and data is small so there is no suboption splitting
1124 // and found_suboptions is false but combined they are large
1125 // 4. no split of any suboption or data
1126 // Also split if the overflow is caused by adding the suboptions
1127 // to the option data.
1128 if (found_suboptions || candidate->len() > (255 - used)) {
1129 // The 2. and 3. cases are handled here (the suboptions part).
1130 updated = true;
1131 scoped_options.push_back(candidate_scoped_options);
1132 // Erase the old options from the new container so that only
1133 // the new options are present.
1134 copy.erase(option.first);
1135 result = true;
1136 // If there are suboptions which have been split, one parent
1137 // option will be created for each of the chunk of the
1138 // suboptions. If the suboptions have not been split,
1139 // but they cause overflow when added to the option data,
1140 // one parent option will contain the option data and one
1141 // parent option will be created for each suboption.
1142 // This will guarantee that none of the options plus
1143 // suboptions will have more than 255 bytes.
1144 for (auto const& sub_option : candidate->getMutableOptions()) {
1145 OptionPtr data_sub_option(new Option(candidate->getUniverse(),
1146 candidate->getType(),
1147 OptionBuffer(0)));
1148 data_sub_option->addOption(sub_option.second);
1149 distinct_options.insert(make_pair(candidate->getType(), data_sub_option));
1150 }
1151 }
1152 }
1153 // The 1. and 3. and 4. cases are handled here (the data part).
1154 // Create a new option containing only data that needs to be split
1155 // and no suboptions (which are inserted in completely separate
1156 // options which are added at the end).
1157 OptionPtr data_option(new Option(candidate->getUniverse(),
1158 candidate->getType(),
1159 OptionBuffer(candidate->getData().begin(),
1160 candidate->getData().end())));
1161 OutputBuffer buf(0);
1162 data_option->pack(buf, false);
1163 uint32_t header_len = candidate->getHeaderLen();
1164 // At least 1 + header length bytes must be available.
1165 if (used >= 255 - header_len) {
1166 isc_throw(BadValue, "there is no space left to split option "
1167 << candidate->getType() << " after parent already used "
1168 << used);
1169 }
1170 // Maximum option buffer size is 255 - header size - buffer size
1171 // already used by parent options.
1172 uint8_t len = 255 - header_len - used;
1173 // Current option size after split is the sum of the data and the
1174 // header size. The suboptions are serialized in separate options.
1175 // The header is duplicated in all new options, but the rest of the
1176 // data must be split and serialized.
1177 uint32_t size = buf.getLength() - header_len;
1178 // Only split if data does not fit in the current option.
1179 // There are 3 cases here:
1180 // 1. option has no suboptions and has large data
1181 // 3. option has both options and suboptions:
1182 // 3.1. suboptions are large and data is large
1183 // 3.2. suboptions are large and data is small
1184 // 3.3. suboptions are small and data is large
1185 // 3.4. suboptions are small and data is small but combined they are large
1186 // 4. no split of any suboption or data
1187 if (size > len) {
1188 // The 1. and 3.1. and 3.3 cases are handled here (the data part).
1189 // Erase the old option from the new container so that only new
1190 // options are present.
1191 if (!updated) {
1192 updated = true;
1193 // Erase the old options from the new container so that only
1194 // the new options are present.
1195 copy.erase(option.first);
1196 result = true;
1197 }
1198 uint32_t offset = 0;
1199 // Drain the option buffer in multiple new options until all
1200 // data is serialized.
1201 for (; offset != size;) {
1202 // Adjust the data length of the new option if remaining
1203 // data is less than the 255 - header size (for the last
1204 // option).
1205 if (size - offset < len) {
1206 len = size - offset;
1207 }
1208 // Create new option with data starting from offset and
1209 // containing truncated length.
1210 const uint8_t* data = buf.getData();
1211 data += header_len;
1212 OptionPtr new_option(new Option(candidate->getUniverse(),
1213 candidate->getType(),
1214 OptionBuffer(data + offset,
1215 data + offset + len)));
1216 // Adjust the offset for remaining data to be written to the
1217 // next new option.
1218 offset += len;
1219 // Add the new option to the new container.
1220 copy.insert(make_pair(candidate->getType(), new_option));
1221 }
1222 } else if ((candidate->len() > (255 - used)) && size) {
1223 // The 3.2 and 3.4 cases are handled here (the data part).
1224 // Also split if the overflow is caused by adding the suboptions
1225 // to the option data (which should be of non zero size).
1226 // Add the new option to the new container.
1227 copy.insert(make_pair(candidate->getType(), data_option));
1228 }
1229 if (updated) {
1230 // Add the new options containing the split suboptions, if any,
1231 // to the new container.
1232 copy.insert(distinct_options.begin(), distinct_options.end());
1233 // After all new options have been split and added, update the
1234 // option container with the new container.
1235 options = copy;
1236 // Other options might need splitting, so we need to iterate
1237 // again until no option needs splitting.
1238 found = true;
1239 break;
1240 }
1241 }
1242 // No option needs splitting, so we can exit the loop.
1243 if (!found) {
1244 break;
1245 }
1246 }
1247 return (result);
1248}
1249
1250void
1252 for (auto const& option : options) {
1253 option.second->pack(buf);
1254 }
1255}
1256
1257void
1259 pair<OptionCollection::const_iterator, OptionCollection::const_iterator>
1260 range = options.equal_range(D6O_NTP_SERVER);
1261 if (range.first == range.second) {
1262 return;
1263 }
1264 auto const& ntp_servers = OptionCollection(range.first, range.second);
1265 static_cast<void>(options.erase(range.first, range.second));
1266 auto const& def = D6O_NTP_SERVER_DEF();
1267 for (auto const& opt : ntp_servers) {
1268 for (auto const& sub : opt.second->getOptions()) {
1269 auto new_option(new OptionCustom(def, Option::V6));
1270 new_option->addOption(sub.second);
1271 options.insert(make_pair(D6O_NTP_SERVER, new_option));
1272 }
1273 }
1274}
1275
1276void
1278 Option::Factory* factory) {
1279 switch (u) {
1280 case Option::V6:
1281 {
1282 if (v6factories_.find(opt_type) != v6factories_.end()) {
1283 isc_throw(BadValue, "There is already DHCPv6 factory registered "
1284 << "for option type " << opt_type);
1285 }
1286 v6factories_[opt_type] = factory;
1287 return;
1288 }
1289 case Option::V4:
1290 {
1291 // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
1292 // instantiated as an Option object, but rather consumed during packet parsing.
1293 if (opt_type == 0) {
1294 isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
1295 }
1296 // Option 255 is never instantiated as an option object. It is special
1297 // (a one-octet equal 255) option that is added at the end of all options
1298 // during packet assembly. It is also silently consumed during packet parsing.
1299 if (opt_type > 254) {
1300 isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
1301 }
1302 if (v4factories_.find(opt_type) != v4factories_.end()) {
1303 isc_throw(BadValue, "There is already DHCPv4 factory registered "
1304 << "for option type " << opt_type);
1305 }
1306 v4factories_[opt_type] = factory;
1307 return;
1308 }
1309 default:
1310 isc_throw(BadValue, "Invalid universe type specified.");
1311 }
1312
1313 return;
1314}
1315
1316bool
1317LibDHCP::initOptionDefs() {
1318 for (uint32_t i = 0; OPTION_DEF_PARAMS[i].optionDefParams; ++i) {
1319 string space = OPTION_DEF_PARAMS[i].space;
1320 option_defs_[space] = OptionDefContainerPtr(new OptionDefContainer());
1321 initOptionSpace(option_defs_[space],
1322 OPTION_DEF_PARAMS[i].optionDefParams,
1323 OPTION_DEF_PARAMS[i].size);
1324 }
1325
1326 static_cast<void>(LibDHCP::DHO_DHCP_REQUESTED_ADDRESS_DEF());
1327 static_cast<void>(LibDHCP::DHO_DHCP_SERVER_IDENTIFIER_DEF());
1328 static_cast<void>(LibDHCP::DHO_DHCP_AGENT_OPTIONS_DEF());
1329 static_cast<void>(LibDHCP::DHO_SUBNET_SELECTION_DEF());
1330 static_cast<void>(LibDHCP::DHO_DOMAIN_SEARCH_DEF());
1331 static_cast<void>(LibDHCP::DHO_STATUS_CODE_DEF());
1332 static_cast<void>(LibDHCP::D6O_CLIENT_FQDN_DEF());
1333 static_cast<void>(LibDHCP::D6O_LQ_QUERY_DEF());
1334 static_cast<void>(LibDHCP::D6O_CLIENT_DATA_DEF());
1335 static_cast<void>(LibDHCP::D6O_LQ_RELAY_DATA_DEF());
1336 static_cast<void>(LibDHCP::D6O_NTP_SERVER_DEF());
1337 static_cast<void>(LibDHCP::D6O_BOOTFILE_URL_DEF());
1338 static_cast<void>(LibDHCP::D6O_RSOO_DEF());
1339
1340 return (true);
1341}
1342
1343uint32_t
1344LibDHCP::optionSpaceToVendorId(const string& option_space) {
1345 // 8 is a minimal length of "vendor-X" format
1346 if ((option_space.size() < 8) || (option_space.substr(0,7) != "vendor-")) {
1347 return (0);
1348 }
1349
1350 int64_t check;
1351 try {
1352 // text after "vendor-", supposedly numbers only
1353 string x = option_space.substr(7);
1354
1355 check = boost::lexical_cast<int64_t>(x);
1356 } catch (const boost::bad_lexical_cast &) {
1357 return (0);
1358 }
1359
1360 if ((check < 0) || (check > std::numeric_limits<uint32_t>::max())) {
1361 return (0);
1362 }
1363
1364 // value is small enough to fit
1365 return (static_cast<uint32_t>(check));
1366}
1367
1368void
1370 size_t params_size) {
1371 // Container holding vendor options is typically not initialized, as it
1372 // is held in map of null pointers. We need to initialize here in this
1373 // case.
1374 if (!defs) {
1375 defs.reset(new OptionDefContainer());
1376 } else {
1377 defs->clear();
1378 }
1379
1380 for (size_t i = 0; i < params_size; ++i) {
1381 string encapsulates(params[i].encapsulates);
1382 if (!encapsulates.empty() && params[i].array) {
1383 isc_throw(isc::BadValue, "invalid standard option definition: "
1384 << "option with code '" << params[i].code
1385 << "' may not encapsulate option space '"
1386 << encapsulates << "' because the definition"
1387 << " indicates that this option comprises an array"
1388 << " of values");
1389 }
1390
1391 // Depending whether an option encapsulates an option space or not
1392 // we pick different constructor to create an instance of the option
1393 // definition.
1394 OptionDefinitionPtr definition;
1395 if (encapsulates.empty()) {
1396 // Option does not encapsulate any option space.
1397 definition.reset(new OptionDefinition(params[i].name,
1398 params[i].code,
1399 params[i].space,
1400 params[i].type,
1401 params[i].array));
1402 } else {
1403 // Option does encapsulate an option space.
1404 definition.reset(new OptionDefinition(params[i].name,
1405 params[i].code,
1406 params[i].space,
1407 params[i].type,
1408 params[i].encapsulates));
1409
1410 }
1411
1412 for (size_t rec = 0; rec < params[i].records_size; ++rec) {
1413 definition->addRecordField(params[i].records[rec]);
1414 }
1415
1416 try {
1417 definition->validate();
1418 } catch (const isc::Exception&) {
1419 // This is unlikely event that validation fails and may
1420 // be only caused by programming error. To guarantee the
1421 // data consistency we clear all option definitions that
1422 // have been added so far and pass the exception forward.
1423 defs->clear();
1424 throw;
1425 }
1426
1427 // option_defs is a multi-index container with no unique indexes
1428 // so push_back can't fail).
1429 static_cast<void>(defs->push_back(definition));
1430 }
1431}
1432
1433const OptionDefinition&
1435 static OptionDefinitionPtr def =
1437 static bool check_once(true);
1438 if (check_once) {
1439 isc_throw_assert(def);
1440 isc_throw_assert(def->getName() == "dhcp-requested-address");
1442 isc_throw_assert(def->getType() == OPT_IPV4_ADDRESS_TYPE);
1443 isc_throw_assert(!def->getArrayType());
1444 isc_throw_assert(def->getEncapsulatedSpace().empty());
1445 isc_throw_assert(def->getOptionSpaceName() == DHCP4_OPTION_SPACE);
1446 check_once = false;
1447 }
1448 return (*def);
1449}
1450
1451const OptionDefinition&
1453 static OptionDefinitionPtr def =
1455 static bool check_once(true);
1456 if (check_once) {
1457 isc_throw_assert(def);
1458 isc_throw_assert(def->getName() == "dhcp-server-identifier");
1460 isc_throw_assert(def->getType() == OPT_IPV4_ADDRESS_TYPE);
1461 isc_throw_assert(!def->getArrayType());
1462 isc_throw_assert(def->getEncapsulatedSpace().empty());
1463 isc_throw_assert(def->getOptionSpaceName() == DHCP4_OPTION_SPACE);
1464 check_once = false;
1465 }
1466 return (*def);
1467}
1468
1469const OptionDefinition&
1471 static OptionDefinitionPtr def =
1473 static bool check_once(true);
1474 if (check_once) {
1475 isc_throw_assert(def);
1476 isc_throw_assert(def->getName() == "dhcp-agent-options");
1477 isc_throw_assert(def->getCode() == DHO_DHCP_AGENT_OPTIONS);
1478 isc_throw_assert(def->getType() == OPT_EMPTY_TYPE);
1479 isc_throw_assert(!def->getArrayType());
1480 isc_throw_assert(def->getEncapsulatedSpace() == DHCP_AGENT_OPTION_SPACE);
1481 isc_throw_assert(def->getOptionSpaceName() == DHCP4_OPTION_SPACE);
1482 check_once = false;
1483 }
1484 return (*def);
1485}
1486
1487const OptionDefinition&
1489 static OptionDefinitionPtr def =
1491 static bool check_once(true);
1492 if (check_once) {
1493 isc_throw_assert(def);
1494 isc_throw_assert(def->getName() == "subnet-selection");
1495 isc_throw_assert(def->getCode() == DHO_SUBNET_SELECTION);
1496 isc_throw_assert(def->getType() == OPT_IPV4_ADDRESS_TYPE);
1497 isc_throw_assert(!def->getArrayType());
1498 isc_throw_assert(def->getEncapsulatedSpace().empty());
1499 isc_throw_assert(def->getOptionSpaceName() == DHCP4_OPTION_SPACE);
1500 check_once = false;
1501 }
1502 return (*def);
1503}
1504
1505const OptionDefinition&
1507 static OptionDefinitionPtr def =
1509 static bool check_once(true);
1510 if (check_once) {
1511 isc_throw_assert(def);
1512 isc_throw_assert(def->getName() == "domain-search");
1513 isc_throw_assert(def->getCode() == DHO_DOMAIN_SEARCH);
1514 isc_throw_assert(def->getType() == OPT_FQDN_TYPE);
1515 isc_throw_assert(def->getArrayType());
1516 isc_throw_assert(def->getEncapsulatedSpace().empty());
1517 isc_throw_assert(def->getOptionSpaceName() == DHCP4_OPTION_SPACE);
1518 check_once = false;
1519 }
1520 return (*def);
1521}
1522
1523const OptionDefinition&
1525 static OptionDefinitionPtr def =
1527 static bool check_once(true);
1528 if (check_once) {
1529 isc_throw_assert(def);
1530 isc_throw_assert(def->getName() == "status-code");
1531 isc_throw_assert(def->getCode() == DHO_STATUS_CODE);
1532 isc_throw_assert(def->getType() == OPT_RECORD_TYPE);
1533 isc_throw_assert(!def->getArrayType());
1534 isc_throw_assert(def->getEncapsulatedSpace().empty());
1535 isc_throw_assert(def->getOptionSpaceName() == DHCP4_OPTION_SPACE);
1536 check_once = false;
1537 }
1538 return (*def);
1539}
1540
1541const OptionDefinition&
1543 static OptionDefinitionPtr def =
1545 static bool check_once(true);
1546 if (check_once) {
1547 isc_throw_assert(def);
1548 isc_throw_assert(def->getName() == "client-fqdn");
1549 isc_throw_assert(def->getCode() == D6O_CLIENT_FQDN);
1550 isc_throw_assert(def->getType() == OPT_RECORD_TYPE);
1551 isc_throw_assert(!def->getArrayType());
1552 isc_throw_assert(def->getEncapsulatedSpace().empty());
1553 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1554 check_once = false;
1555 }
1556 return (*def);
1557}
1558
1559const OptionDefinition&
1561 static OptionDefinitionPtr def =
1563 static bool check_once(true);
1564 if (check_once) {
1565 isc_throw_assert(def);
1566 isc_throw_assert(def->getName() == "lq-query");
1567 isc_throw_assert(def->getCode() == D6O_LQ_QUERY);
1568 isc_throw_assert(def->getType() == OPT_RECORD_TYPE);
1569 isc_throw_assert(!def->getArrayType());
1570 isc_throw_assert(def->getEncapsulatedSpace() == DHCP6_OPTION_SPACE);
1571 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1572 check_once = false;
1573 }
1574 return (*def);
1575}
1576
1577const OptionDefinition&
1579 static OptionDefinitionPtr def =
1581 static bool check_once(true);
1582 if (check_once) {
1583 isc_throw_assert(def);
1584 isc_throw_assert(def->getName() == "client-data");
1585 isc_throw_assert(def->getCode() == D6O_CLIENT_DATA);
1586 isc_throw_assert(def->getType() == OPT_EMPTY_TYPE);
1587 isc_throw_assert(!def->getArrayType());
1588 isc_throw_assert(def->getEncapsulatedSpace() == DHCP6_OPTION_SPACE);
1589 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1590 check_once = false;
1591 }
1592 return (*def);
1593}
1594
1595const OptionDefinition&
1597 static OptionDefinitionPtr def =
1599 static bool check_once(true);
1600 if (check_once) {
1601 isc_throw_assert(def);
1602 isc_throw_assert(def->getName() == "lq-relay-data");
1603 isc_throw_assert(def->getCode() == D6O_LQ_RELAY_DATA);
1604 isc_throw_assert(def->getType() == OPT_RECORD_TYPE);
1605 isc_throw_assert(!def->getArrayType());
1606 isc_throw_assert(def->getEncapsulatedSpace().empty());
1607 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1608 check_once = false;
1609 }
1610 return (*def);
1611}
1612
1613const OptionDefinition&
1615 static OptionDefinitionPtr def =
1617 static bool check_once(true);
1618 if (check_once) {
1619 isc_throw_assert(def);
1620 isc_throw_assert(def->getName() == "ntp-server");
1621 isc_throw_assert(def->getCode() == D6O_NTP_SERVER);
1622 isc_throw_assert(def->getType() == OPT_EMPTY_TYPE);
1623 isc_throw_assert(!def->getArrayType());
1624 isc_throw_assert(def->getEncapsulatedSpace() == V6_NTP_SERVER_SPACE);
1625 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1626 check_once = false;
1627 }
1628 return (*def);
1629}
1630
1631const OptionDefinition&
1633 static OptionDefinitionPtr def =
1635 static bool check_once(true);
1636 if (check_once) {
1637 isc_throw_assert(def);
1638 isc_throw_assert(def->getName() == "bootfile-url");
1639 isc_throw_assert(def->getCode() == D6O_BOOTFILE_URL);
1640 isc_throw_assert(def->getType() == OPT_STRING_TYPE);
1641 isc_throw_assert(!def->getArrayType());
1642 isc_throw_assert(def->getEncapsulatedSpace().empty());
1643 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1644 check_once = false;
1645 }
1646 return (*def);
1647}
1648
1649const OptionDefinition&
1651 static OptionDefinitionPtr def =
1653 static bool check_once(true);
1654 if (check_once) {
1655 isc_throw_assert(def);
1656 isc_throw_assert(def->getName() == "rsoo");
1657 isc_throw_assert(def->getCode() == D6O_RSOO);
1658 isc_throw_assert(def->getType() == OPT_EMPTY_TYPE);
1659 isc_throw_assert(!def->getArrayType());
1660 isc_throw_assert(def->getEncapsulatedSpace() == "rsoo-opts");
1661 isc_throw_assert(def->getOptionSpaceName() == DHCP6_OPTION_SPACE);
1662 check_once = false;
1663 }
1664 return (*def);
1665}
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.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
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:471
static void OptionFactoryRegister(Option::Universe u, uint16_t type, Option::Factory *factory)
Registers factory method that produces options of specific option types.
static const OptionDefinition & D6O_LQ_RELAY_DATA_DEF()
Get definition of D6O_LQ_RELAY_DATA option.
static const OptionDefinition & D6O_NTP_SERVER_DEF()
Get definition of D6O_NTP_SERVER option.
static const OptionDefinition & D6O_RSOO_DEF()
Get definition of D6O_RSOO option.
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:823
static const OptionDefContainerPtr getOptionDefs(const std::string &space)
Returns collection of option definitions.
Definition libdhcp++.cc:105
static void splitNtpServerOptions6(isc::dhcp::OptionCollection &options)
Split NTP server option to one suboption per instance.
static bool shouldDeferOptionUnpack(const std::string &space, const uint16_t code)
Checks if an option unpacking has to be deferred.
Definition libdhcp++.cc:286
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:293
static void setRuntimeOptionDefs(const OptionDefSpaceContainer &defs)
Copies option definitions created at runtime.
Definition libdhcp++.cc:224
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition libdhcp++.cc:132
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:174
static OptionDefContainerPtr getLastResortOptionDefs(const std::string &space)
Returns last resort option definitions for specified option space name.
Definition libdhcp++.cc:277
static const OptionDefinition & D6O_LQ_QUERY_DEF()
Get definition of D6O_LQ_QUERY option.
static OptionDefContainerPtr getRuntimeOptionDefs(const std::string &space)
Returns runtime (non-standard) option definitions for specified option space name.
Definition libdhcp++.cc:219
static void commitRuntimeOptionDefs()
Commits runtime option definitions.
Definition libdhcp++.cc:248
static void clearRuntimeOptionDefs()
Removes runtime option definitions.
Definition libdhcp++.cc:238
static void extendVendorOptions4(isc::dhcp::OptionCollection &options)
Extend vendor options from fused options in multiple OptionVendor or OptionVendorClass options and ad...
Definition libdhcp++.cc:817
static const OptionDefinition & D6O_CLIENT_DATA_DEF()
Get definition of D6O_CLIENT_DATA option.
static void packOptions4(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options, bool top=false, bool check=true)
Stores DHCPv4 options in a buffer.
static bool splitOptions4(isc::dhcp::OptionCollection &options, ScopedOptionsCopyContainer &scopedOptions, uint32_t used=0)
Split long options in multiple options with the same option code (RFC3396).
static const OptionDefinition & DHO_DHCP_REQUESTED_ADDRESS_DEF()
Get definition of DHO_DHCP_REQUESTED_ADDRESS option.
static const OptionDefinition & D6O_BOOTFILE_URL_DEF()
Get definition of D6O_BOOTFILE_URL option.
static const OptionDefinition & DHO_DHCP_SERVER_IDENTIFIER_DEF()
Get definition of DHO_DHCP_SERVER_IDENTIFIER option.
static const OptionDefinition & DHO_SUBNET_SELECTION_DEF()
Get definition of DHO_SUBNET_SELECTION option.
static void revertRuntimeOptionDefs()
Reverts uncommitted changes to runtime option definitions.
Definition libdhcp++.cc:243
static const OptionDefinition & DHO_DOMAIN_SEARCH_DEF()
Get definition of DHO_DOMAIN_SEARCH option.
static const OptionDefContainerPtr getVendorOptionDefs(Option::Universe u, const uint32_t vendor_id)
Returns option definitions for given universe and vendor.
Definition libdhcp++.cc:115
static const OptionDefinition & D6O_CLIENT_FQDN_DEF()
Get definition of D6O_CLIENT_FQDN option.
static const OptionDefinition & DHO_DHCP_AGENT_OPTIONS_DEF()
Get definition of DHO_DHCP_AGENT_OPTIONS option.
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
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:195
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:317
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
static const OptionDefinition & DHO_STATUS_CODE_DEF()
Get definition of DHO_STATUS_CODE option.
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition libdhcp++.cc:253
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:919
Exception to be thrown when the operation on OpaqueDataTuple object results in an error.
Represents a single instance of the opaque data preceded by length.
Option with defined data fields represented as buffers that can be accessed using data field index.
Class of option definition space container.
Base class representing a DHCP option definition.
Wrapper exception thrown by unpackOptionsX functions to add option type and len to the underlying err...
Definition option.h:75
std::list< Selector > getOptionSpaceNames() const
Get a list of existing option spaces.
This class encapsulates DHCPv6 Vendor Class and DHCPv4 V-I Vendor Class options.
This class represents vendor-specific information option.
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:90
OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer &buf)
a factory function prototype
Definition option.h:103
RAII object enabling duplication of the stored options and restoring the original options on destruct...
Definition pkt.h:1012
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition option.h:52
Exception thrown during option unpacking This exception is thrown when an error has occurred unpackin...
Definition option.h:67
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
@ D6O_CLIENT_FQDN
Definition dhcp6.h:59
@ D6O_RSOO
Definition dhcp6.h:86
@ D6O_RELAY_MSG
Definition dhcp6.h:29
@ D6O_VENDOR_OPTS
Definition dhcp6.h:37
@ D6O_BOOTFILE_URL
Definition dhcp6.h:79
@ D6O_LQ_QUERY
Definition dhcp6.h:64
@ D6O_CLIENT_DATA
Definition dhcp6.h:65
@ D6O_LQ_RELAY_DATA
Definition dhcp6.h:67
@ D6O_NTP_SERVER
Definition dhcp6.h:76
#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)
const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer())
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
@ DHO_DHCP_MESSAGE_TYPE
Definition dhcp4.h:122
@ DHO_DHCP_SERVER_IDENTIFIER
Definition dhcp4.h:123
@ DHO_HOST_NAME
Definition dhcp4.h:81
@ DHO_VIVCO_SUBOPTIONS
Definition dhcp4.h:188
@ DHO_DHCP_REQUESTED_ADDRESS
Definition dhcp4.h:119
@ DHO_END
Definition dhcp4.h:229
@ DHO_PAD
Definition dhcp4.h:69
@ DHO_DHCP_AGENT_OPTIONS
Definition dhcp4.h:151
@ DHO_VENDOR_ENCAPSULATED_OPTIONS
Definition dhcp4.h:112
@ DHO_SUBNET_SELECTION
Definition dhcp4.h:182
@ DHO_DOMAIN_SEARCH
Definition dhcp4.h:183
@ DHO_VIVSO_SUBOPTIONS
Definition dhcp4.h:189
@ DHO_STATUS_CODE
Definition dhcp4.h:205
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:94
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:91
boost::shared_ptr< OptionVendorClass > OptionVendorClassPtr
Defines a pointer to the OptionVendorClass.
std::map< std::string, OptionDefContainerPtr > OptionDefContainers
Container that holds option definitions for various option spaces.
std::shared_ptr< ScopedSubOptionsCopy > ScopedOptionsCopyPtr
A pointer to a ScopedSubOptionsCopy object.
Definition libdhcp++.h:30
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:37
std::vector< ScopedOptionsCopyPtr > ScopedOptionsCopyContainer
A container of ScopedOptionsCopyPtr objects.
Definition libdhcp++.h:32
const OptionDefParams DOCSIS3_V6_OPTION_DEFINITIONS[]
Definitions of standard DHCPv6 options.
boost::shared_ptr< OptionDefContainer > OptionDefContainerPtr
Pointer to an option definition container.
uint16_t readUint16(void const *const buffer, size_t const length)
uint16_t wrapper over readUint.
Definition io.h:76
uint32_t readUint32(void const *const buffer, size_t const length)
uint32_t wrapper over readUint.
Definition io.h:82
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 V6_NTP_SERVER_SPACE
#define V4V6_RULE_OPTION_SPACE
#define MAPE_V6_OPTION_SPACE
#define DHCP_AGENT_OPTION_SPACE
encapsulated option spaces
#define LW_V6_OPTION_SPACE
#define DHCP6_OPTION_SPACE
#define MAPT_V6_OPTION_SPACE
#define CABLELABS_CLIENT_CONF_SPACE
Encapsulation of option definition parameters and the structure size.
Parameters being used to make up an option definition.