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