Kea  2.3.5-git
libdhcp++.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2023 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <dhcp/dhcp4.h>
10 #include <dhcp/dhcp6.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcp/option.h>
13 #include <dhcp/option_vendor.h>
14 #include <dhcp/option6_ia.h>
15 #include <dhcp/option6_iaaddr.h>
16 #include <dhcp/option_definition.h>
17 #include <dhcp/option_int_array.h>
18 #include <dhcp/std_option_defs.h>
20 #include <exceptions/exceptions.h>
21 #include <exceptions/isc_assert.h>
22 #include <util/buffer.h>
23 
24 #include <boost/lexical_cast.hpp>
25 #include <boost/shared_array.hpp>
26 #include <boost/shared_ptr.hpp>
27 
28 #include <limits>
29 #include <list>
30 
31 using namespace std;
32 using namespace isc::dhcp;
33 using namespace isc::util;
34 
35 namespace isc {
36 namespace dhcp {
37 
38 namespace {
39 
43 const OptionDefParamsEncapsulation OPTION_DEF_PARAMS[] = {
44  { STANDARD_V4_OPTION_DEFINITIONS, STANDARD_V4_OPTION_DEFINITIONS_SIZE, DHCP4_OPTION_SPACE },
45  { STANDARD_V6_OPTION_DEFINITIONS, STANDARD_V6_OPTION_DEFINITIONS_SIZE, DHCP6_OPTION_SPACE },
48  { ISC_V6_OPTION_DEFINITIONS, ISC_V6_OPTION_DEFINITIONS_SIZE, ISC_V6_OPTION_SPACE },
49  { MAPE_V6_OPTION_DEFINITIONS, MAPE_V6_OPTION_DEFINITIONS_SIZE, MAPE_V6_OPTION_SPACE },
50  { MAPT_V6_OPTION_DEFINITIONS, MAPT_V6_OPTION_DEFINITIONS_SIZE, MAPT_V6_OPTION_SPACE },
51  { LW_V6_OPTION_DEFINITIONS, LW_V6_OPTION_DEFINITIONS_SIZE, LW_V6_OPTION_SPACE },
52  { V4V6_RULE_OPTION_DEFINITIONS, V4V6_RULE_OPTION_DEFINITIONS_SIZE, V4V6_RULE_OPTION_SPACE },
53  { V4V6_BIND_OPTION_DEFINITIONS, V4V6_BIND_OPTION_DEFINITIONS_SIZE, V4V6_BIND_OPTION_SPACE },
54  { DHCP_AGENT_OPTION_DEFINITIONS, DHCP_AGENT_OPTION_DEFINITIONS_SIZE, DHCP_AGENT_OPTION_SPACE },
55  { LAST_RESORT_V4_OPTION_DEFINITIONS, LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE, LAST_RESORT_V4_OPTION_SPACE },
56  { NULL, 0, "" }
57 };
58 
59 } // namespace
60 
61 } // namespace dhcp
62 } // namespace isc
63 
64 // static array with factories for options
65 map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
66 
67 // static array with factories for options
68 map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
69 
70 // Static container with option definitions grouped by option space.
71 OptionDefContainers LibDHCP::option_defs_;
72 
73 // Static container with option definitions created in runtime.
74 StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
75 
76 // Null container.
78 
79 // Those two vendor classes are used for cable modems:
80 
82 const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
83 
85 const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
86 
87 // Let's keep it in .cc file. Moving it to .h would require including optionDefParams
88 // definitions there
90  const OptionDefParams* params,
91  size_t params_size);
92 
93 bool LibDHCP::initialized_ = LibDHCP::initOptionDefs();
94 
96 LibDHCP::getOptionDefs(const string& space) {
97  auto const& container = option_defs_.find(space);
98  if (container != option_defs_.end()) {
99  return (container->second);
100  }
101 
103 }
104 
106 LibDHCP::getVendorOptionDefs(const Option::Universe u, const uint32_t vendor_id) {
107  if (Option::V4 == u) {
108  if (VENDOR_ID_CABLE_LABS == vendor_id) {
109  return getOptionDefs(DOCSIS3_V4_OPTION_SPACE);
110  }
111  } else if (Option::V6 == u) {
112  if (VENDOR_ID_CABLE_LABS == vendor_id) {
113  return getOptionDefs(DOCSIS3_V6_OPTION_SPACE);
114  } else if (ENTERPRISE_ID_ISC == vendor_id) {
115  return getOptionDefs(ISC_V6_OPTION_SPACE);
116  }
117  }
118 
120 }
121 
123 LibDHCP::getOptionDef(const string& space, const uint16_t code) {
124  const OptionDefContainerPtr& defs = getOptionDefs(space);
125  const OptionDefContainerTypeIndex& idx = defs->get<1>();
126  const OptionDefContainerTypeRange& range = idx.equal_range(code);
127  if (range.first != range.second) {
128  return (*range.first);
129  }
130 
131  return (OptionDefinitionPtr());
132 }
133 
135 LibDHCP::getOptionDef(const string& space, const string& name) {
136  const OptionDefContainerPtr& defs = getOptionDefs(space);
137  const OptionDefContainerNameIndex& idx = defs->get<2>();
138  const OptionDefContainerNameRange& range = idx.equal_range(name);
139  if (range.first != range.second) {
140  return (*range.first);
141  }
142 
143  return (OptionDefinitionPtr());
144 }
145 
147 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
148  const string& name) {
149  const OptionDefContainerPtr& defs = getVendorOptionDefs(u, vendor_id);
150 
151  if (!defs) {
152  return (OptionDefinitionPtr());
153  }
154 
155  const OptionDefContainerNameIndex& idx = defs->get<2>();
156  const OptionDefContainerNameRange& range = idx.equal_range(name);
157  if (range.first != range.second) {
158  return (*range.first);
159  }
160 
161  return (OptionDefinitionPtr());
162 }
163 
165 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
166  const uint16_t code) {
167  const OptionDefContainerPtr& defs = getVendorOptionDefs(u, vendor_id);
168 
169  if (!defs) {
170  // Weird universe or unknown vendor_id. We don't care. No definitions
171  // one way or another
172  // What is it anyway?
173  return (OptionDefinitionPtr());
174  }
175 
176  const OptionDefContainerTypeIndex& idx = defs->get<1>();
177  const OptionDefContainerTypeRange& range = idx.equal_range(code);
178  if (range.first != range.second) {
179  return (*range.first);
180  }
181 
182  return (OptionDefinitionPtr());
183 }
184 
186 LibDHCP::getRuntimeOptionDef(const string& space, const uint16_t code) {
187  OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
188  const OptionDefContainerTypeIndex& index = container->get<1>();
189  const OptionDefContainerTypeRange& range = index.equal_range(code);
190  if (range.first != range.second) {
191  return (*range.first);
192  }
193 
194  return (OptionDefinitionPtr());
195 }
196 
198 LibDHCP::getRuntimeOptionDef(const string& space, const string& name) {
199  OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
200  const OptionDefContainerNameIndex& index = container->get<2>();
201  const OptionDefContainerNameRange& range = index.equal_range(name);
202  if (range.first != range.second) {
203  return (*range.first);
204  }
205 
206  return (OptionDefinitionPtr());
207 }
208 
210 LibDHCP::getRuntimeOptionDefs(const string& space) {
211  return (runtime_option_defs_.getValue().getItems(space));
212 }
213 
214 void
215 LibDHCP::setRuntimeOptionDefs(const OptionDefSpaceContainer& defs) {
216  OptionDefSpaceContainer defs_copy;
217  list<string> option_space_names = defs.getOptionSpaceNames();
218  for (auto const& name : option_space_names) {
219  OptionDefContainerPtr container = defs.getItems(name);
220  for (auto const& def : *container) {
221  OptionDefinitionPtr def_copy(new OptionDefinition(*def));
222  defs_copy.addItem(def_copy);
223  }
224  }
225  runtime_option_defs_ = defs_copy;
226 }
227 
228 void
229 LibDHCP::clearRuntimeOptionDefs() {
230  runtime_option_defs_.reset();
231 }
232 
233 void
234 LibDHCP::revertRuntimeOptionDefs() {
235  runtime_option_defs_.revert();
236 }
237 
238 void
239 LibDHCP::commitRuntimeOptionDefs() {
240  runtime_option_defs_.commit();
241 }
242 
244 LibDHCP::getLastResortOptionDef(const string& space, const uint16_t code) {
245  OptionDefContainerPtr container = getLastResortOptionDefs(space);
246  const OptionDefContainerTypeIndex& index = container->get<1>();
247  const OptionDefContainerTypeRange& range = index.equal_range(code);
248  if (range.first != range.second) {
249  return (*range.first);
250  }
251 
252  return (OptionDefinitionPtr());
253 }
254 
256 LibDHCP::getLastResortOptionDef(const string& space, const string& name) {
257  OptionDefContainerPtr container = getLastResortOptionDefs(space);
258  const OptionDefContainerNameIndex& index = container->get<2>();
259  const OptionDefContainerNameRange& range = index.equal_range(name);
260  if (range.first != range.second) {
261  return (*range.first);
262  }
263 
264  return (OptionDefinitionPtr());
265 }
266 
268 LibDHCP::getLastResortOptionDefs(const string& space) {
269  if (space == DHCP4_OPTION_SPACE) {
270  return getOptionDefs(LAST_RESORT_V4_OPTION_SPACE);
271  }
272 
274 }
275 
276 bool
277 LibDHCP::shouldDeferOptionUnpack(const string& space, const uint16_t code) {
278  return ((space == DHCP4_OPTION_SPACE) &&
279  ((code == DHO_VENDOR_ENCAPSULATED_OPTIONS) ||
280  ((code >= 224) && (code <= 254))));
281 }
282 
283 OptionPtr
284 LibDHCP::optionFactory(Option::Universe u,
285  uint16_t type,
286  const OptionBuffer& buf) {
287  FactoryMap::iterator it;
288  if (u == Option::V4) {
289  it = v4factories_.find(type);
290  if (it == v4factories_.end()) {
291  isc_throw(BadValue, "factory function not registered "
292  "for DHCP v4 option type " << type);
293  }
294  } else if (u == Option::V6) {
295  it = v6factories_.find(type);
296  if (it == v6factories_.end()) {
297  isc_throw(BadValue, "factory function not registered "
298  "for DHCPv6 option type " << type);
299  }
300  } else {
301  isc_throw(BadValue, "invalid universe specified (expected "
302  "Option::V4 or Option::V6");
303  }
304  return (it->second(u, type, buf));
305 }
306 
307 size_t
308 LibDHCP::unpackOptions6(const OptionBuffer& buf, const string& option_space,
309  OptionCollection& options,
310  size_t* relay_msg_offset /* = 0 */,
311  size_t* relay_msg_len /* = 0 */) {
312  size_t offset = 0;
313  size_t length = buf.size();
314  size_t last_offset = 0;
315 
316  // Get the list of standard option definitions.
317  const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
318  // Runtime option definitions for non standard option space and if
319  // the definition doesn't exist within the standard option definitions.
320  const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
321 
322  // @todo Once we implement other option spaces we should add else clause
323  // here and gather option definitions for them. For now leaving option_defs
324  // empty will imply creation of generic Option.
325 
326  // Get the search indexes #1. It allows to search for option definitions
327  // using option code.
328  const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
329  const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
330 
331  // The buffer being read comprises a set of options, each starting with
332  // a two-byte type code and a two-byte length field.
333  while (offset < length) {
334  // Save the current offset for backtracking
335  last_offset = offset;
336 
337  // Check if there is room for another option
338  if (offset + 4 > length) {
339  // Still something but smaller than an option
340  return (last_offset);
341  }
342 
343  // Parse the option header
344  uint16_t opt_type = readUint16(&buf[offset], 2);
345  offset += 2;
346 
347  uint16_t opt_len = readUint16(&buf[offset], 2);
348  offset += 2;
349 
350  if (offset + opt_len > length) {
351  // We peeked at the option header of the next option, but
352  // discovered that it would end up beyond buffer end, so
353  // the option is truncated. Hence we can't parse
354  // it. Therefore we revert back by those bytes (as if
355  // we never parsed them).
356  //
357  // @note it is the responsibility of the caller to throw
358  // an exception on partial parsing
359  return (last_offset);
360  }
361 
362  if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
363  // remember offset of the beginning of the relay-msg option
364  *relay_msg_offset = offset;
365  *relay_msg_len = opt_len;
366 
367  // do not create that relay-msg option
368  offset += opt_len;
369  continue;
370  }
371 
372  if (opt_type == D6O_VENDOR_OPTS) {
373  if (offset + 4 > length) {
374  // Truncated vendor-option. We expect at least
375  // 4 bytes for the enterprise-id field. Let's roll back
376  // option code + option length (4 bytes) and return.
377  return (last_offset);
378  }
379 
380  // Parse this as vendor option
381  OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
382  buf.begin() + offset + opt_len));
383  options.insert(std::make_pair(opt_type, vendor_opt));
384 
385  offset += opt_len;
386  continue;
387  }
388 
389  // Get all definitions with the particular option code. Note
390  // that option code is non-unique within this container
391  // however at this point we expect to get one option
392  // definition with the particular code. If more are returned
393  // we report an error.
395  // Number of option definitions returned.
396  size_t num_defs = 0;
397 
398  // We previously did the lookup only for dhcp6 option space, but with the
399  // addition of S46 options, we now do it for every space.
400  range = idx.equal_range(opt_type);
401  num_defs = std::distance(range.first, range.second);
402 
403  // Standard option definitions do not include the definition for
404  // our option or we're searching for non-standard option. Try to
405  // find the definition among runtime option definitions.
406  if (num_defs == 0) {
407  range = runtime_idx.equal_range(opt_type);
408  num_defs = std::distance(range.first, range.second);
409  }
410 
411  OptionPtr opt;
412  if (num_defs > 1) {
413  // Multiple options of the same code are not supported right now!
414  isc_throw(isc::Unexpected, "Internal error: multiple option"
415  " definitions for option type " << opt_type <<
416  " returned. Currently it is not supported to initialize"
417  " multiple option definitions for the same option code."
418  " This will be supported once support for option spaces"
419  " is implemented");
420  } else if (num_defs == 0) {
421  // @todo Don't crash if definition does not exist because
422  // only a few option definitions are initialized right
423  // now. In the future we will initialize definitions for
424  // all options and we will remove this elseif. For now,
425  // return generic option.
426  opt = OptionPtr(new Option(Option::V6, opt_type,
427  buf.begin() + offset,
428  buf.begin() + offset + opt_len));
429  } else {
430  try {
431  // The option definition has been found. Use it to create
432  // the option instance from the provided buffer chunk.
433  const OptionDefinitionPtr& def = *(range.first);
434  isc_throw_assert(def);
435  opt = def->optionFactory(Option::V6, opt_type,
436  buf.begin() + offset,
437  buf.begin() + offset + opt_len);
438  } catch (const SkipThisOptionError&) {
439  opt.reset();
440  }
441  }
442 
443  // add option to options
444  if (opt) {
445  options.insert(std::make_pair(opt_type, opt));
446  }
447 
448  offset += opt_len;
449  }
450 
451  last_offset = offset;
452  return (last_offset);
453 }
454 
455 size_t
456 LibDHCP::unpackOptions4(const OptionBuffer& buf, const string& option_space,
457  OptionCollection& options, list<uint16_t>& deferred,
458  bool check) {
459  size_t offset = 0;
460  size_t last_offset = 0;
461 
462  // Special case when option_space is dhcp4.
463  bool space_is_dhcp4 = (option_space == DHCP4_OPTION_SPACE);
464 
465  // Get the list of standard option definitions.
466  const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
467  // Runtime option definitions for non standard option space and if
468  // the definition doesn't exist within the standard option definitions.
469  const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
470 
471  // Get the search indexes #1. It allows to search for option definitions
472  // using option code.
473  const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
474  const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
475 
476  // Flexible PAD and END parsing.
477  bool flex_pad = (check && (runtime_idx.count(DHO_PAD) == 0));
478  bool flex_end = (check && (runtime_idx.count(DHO_END) == 0));
479 
480  // The buffer being read comprises a set of options, each starting with
481  // a one-byte type code and a one-byte length field.
482  while (offset < buf.size()) {
483  // Save the current offset for backtracking
484  last_offset = offset;
485 
486  // Get the option type
487  uint8_t opt_type = buf[offset++];
488 
489  // DHO_END is a special, one octet long option
490  // Valid in dhcp4 space or when check is true and
491  // there is a sub-option configured for this code.
492  if ((opt_type == DHO_END) && (space_is_dhcp4 || flex_end)) {
493  // just return. Don't need to add DHO_END option
494  // Don't return offset because it makes this condition
495  // and partial parsing impossible to recognize.
496  return (last_offset);
497  }
498 
499  // DHO_PAD is just a padding after DHO_END. Let's continue parsing
500  // in case we receive a message without DHO_END.
501  // Valid in dhcp4 space or when check is true and
502  // there is a sub-option configured for this code.
503  if ((opt_type == DHO_PAD) && (space_is_dhcp4 || flex_pad)) {
504  continue;
505  }
506 
507  if (offset + 1 > buf.size()) {
508  // We peeked at the option header of the next option, but
509  // discovered that it would end up beyond buffer end, so
510  // the option is truncated. Hence we can't parse
511  // it. Therefore we revert back (as if we never parsed it).
512  //
513  // @note it is the responsibility of the caller to throw
514  // an exception on partial parsing
515  return (last_offset);
516  }
517 
518  uint8_t opt_len = buf[offset++];
519  if (offset + opt_len > buf.size()) {
520  // We peeked at the option header of the next option, but
521  // discovered that it would end up beyond buffer end, so
522  // the option is truncated. Hence we can't parse
523  // it. Therefore we revert back (as if we never parsed it).
524  return (last_offset);
525  }
526 
527  // While an empty Host Name option is non-RFC compliant, some clients
528  // do send it. In the spirit of being liberal, we'll just drop it,
529  // rather than the dropping the whole packet. We do not have a
530  // way to log this from here but meh... a PCAP will show it arriving,
531  // and we know we drop it.
532  if (space_is_dhcp4 && opt_len == 0 && opt_type == DHO_HOST_NAME) {
533  continue;
534  }
535 
536  // Get all definitions with the particular option code. Note
537  // that option code is non-unique within this container
538  // however at this point we expect to get one option
539  // definition with the particular code. If more are returned
540  // we report an error.
542  // Number of option definitions returned.
543  size_t num_defs = 0;
544 
545  // Previously we did the lookup only for "dhcp4" option space, but there
546  // may be standard options in other spaces (e.g. radius). So we now do
547  // the lookup for every space.
548  range = idx.equal_range(opt_type);
549  num_defs = std::distance(range.first, range.second);
550 
551  // Standard option definitions do not include the definition for
552  // our option or we're searching for non-standard option. Try to
553  // find the definition among runtime option definitions.
554  if (num_defs == 0) {
555  range = runtime_idx.equal_range(opt_type);
556  num_defs = std::distance(range.first, range.second);
557  }
558 
559  // Check if option unpacking must be deferred
560  if (shouldDeferOptionUnpack(option_space, opt_type)) {
561  num_defs = 0;
562  // Store deferred option only once.
563  bool found = false;
564  for (auto const& existing : deferred) {
565  if (existing == opt_type) {
566  found = true;
567  break;
568  }
569  }
570  if (!found) {
571  deferred.push_back(opt_type);
572  }
573  }
574 
575  if (space_is_dhcp4 && opt_type == DHO_VIVSO_SUBOPTIONS) {
576  num_defs = 0;
577  }
578 
579  OptionPtr opt;
580  if (num_defs > 1) {
581  // Multiple options of the same code are not supported right now!
582  isc_throw(isc::Unexpected, "Internal error: multiple option"
583  " definitions for option type " <<
584  static_cast<int>(opt_type) <<
585  " returned. Currently it is not supported to initialize"
586  " multiple option definitions for the same option code."
587  " This will be supported once support for option spaces"
588  " is implemented");
589  } else if (num_defs == 0) {
590  opt = OptionPtr(new Option(Option::V4, opt_type,
591  buf.begin() + offset,
592  buf.begin() + offset + opt_len));
593  opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
594  } else {
595  try {
596  // The option definition has been found. Use it to create
597  // the option instance from the provided buffer chunk.
598  const OptionDefinitionPtr& def = *(range.first);
599  isc_throw_assert(def);
600  opt = def->optionFactory(Option::V4, opt_type,
601  buf.begin() + offset,
602  buf.begin() + offset + opt_len);
603  } catch (const SkipThisOptionError&) {
604  opt.reset();
605  }
606  }
607 
608  // If we have the option, insert it
609  if (opt) {
610  options.insert(std::make_pair(opt_type, opt));
611  }
612 
613  offset += opt_len;
614  }
615  last_offset = offset;
616  return (last_offset);
617 }
618 
619 bool
620 LibDHCP::fuseOptions4(OptionCollection& options) {
621  bool result = false;
622  // We need to loop until all options have been fused.
623  for (;;) {
624  uint32_t found = 0;
625  bool found_suboptions = false;
626  // Iterate over all options in the container.
627  for (auto const& option : options) {
628  OptionPtr candidate = option.second;
629  OptionCollection& sub_options = candidate->getMutableOptions();
630  // Fuse suboptions recursively, if any.
631  if (sub_options.size()) {
632  // Fusing suboptions might result in new options with multiple
633  // options having the same code, so we need to iterate again
634  // until no option needs fusing.
635  found_suboptions = LibDHCP::fuseOptions4(sub_options);
636  if (found_suboptions) {
637  result = true;
638  }
639  }
640  OptionBuffer data;
641  OptionCollection suboptions;
642  // Make a copy of the options so we can safely iterate over the
643  // old container.
644  OptionCollection copy = options;
645  for (auto const& old_option : copy) {
646  if (old_option.first == option.first) {
647  // Copy the option data to the buffer.
648  data.insert(data.end(), old_option.second->getData().begin(),
649  old_option.second->getData().end());
650  suboptions.insert(old_option.second->getOptions().begin(),
651  old_option.second->getOptions().end());
652  // Other options might need fusing, so we need to iterate
653  // again until no options needs fusing.
654  found++;
655  }
656  }
657  if (found > 1) {
658  result = true;
659  // Erase the old options from the new container so that only the
660  // new option is present.
661  copy.erase(option.first);
662  // Create new option with entire data.
663  OptionPtr new_option(new Option(candidate->getUniverse(),
664  candidate->getType(), data));
665  // Recreate suboptions container.
666  new_option->getMutableOptions() = suboptions;
667  // Add the new option to the new container.
668  copy.insert(make_pair(candidate->getType(), new_option));
669  // After all options have been fused and new option added,
670  // update the option container with the new container.
671  options = copy;
672  break;
673  } else {
674  found = 0;
675  }
676  }
677  // No option needs fusing, so we can exit the loop.
678  if ((found <= 1) && !found_suboptions) {
679  break;
680  }
681  }
682  return (result);
683 }
684 
685 void
686 LibDHCP::extendVendorOptions4(OptionCollection& options) {
687  map<uint32_t, OptionCollection> vendors_data;
688  const auto& range = options.equal_range(DHO_VIVSO_SUBOPTIONS);
689  for (auto it = range.first; it != range.second; ++it) {
690  uint32_t offset = 0;
691  auto const& data = it->second->getData();
692  size_t size;
693  while ((size = data.size() - offset) != 0) {
694  if (size < sizeof(uint32_t)) {
695  options.erase(DHO_VIVSO_SUBOPTIONS);
697  "Truncated vendor-specific information option"
698  << ", length=" << size);
699  }
700  uint32_t vendor_id = readUint32(&data[offset], data.size());
701  offset += 4;
702  const OptionBuffer vendor_buffer(data.begin() + offset, data.end());
703  try {
704  offset += LibDHCP::unpackVendorOptions4(vendor_id, vendor_buffer,
705  vendors_data[vendor_id]);
706  } catch (const SkipThisOptionError&) {
707  // Ignore this kind of error and continue.
708  break;
709  } catch (const Exception&) {
710  options.erase(DHO_VIVSO_SUBOPTIONS);
711  throw;
712  }
713  }
714  }
715  if (vendors_data.empty()) {
716  return;
717  }
718  // Delete the initial option.
719  options.erase(DHO_VIVSO_SUBOPTIONS);
720  // Create a new instance of OptionVendor for each enterprise ID.
721  for (auto const& vendor : vendors_data) {
722  OptionVendorPtr vendor_opt(new OptionVendor(Option::V4, vendor.first));
723  for (auto const& option : vendor.second) {
724  vendor_opt->addOption(option.second);
725  }
726  // Add the new instance of VendorOption with respective sub-options for
727  // this enterprise ID.
728  options.insert(std::make_pair(DHO_VIVSO_SUBOPTIONS, vendor_opt));
729  }
730 }
731 
732 size_t
733 LibDHCP::unpackVendorOptions6(const uint32_t vendor_id, const OptionBuffer& buf,
734  OptionCollection& options) {
735  size_t offset = 0;
736  size_t length = buf.size();
737 
738  // Get the list of option definitions for this particular vendor-id
739  const OptionDefContainerPtr& option_defs =
740  LibDHCP::getVendorOptionDefs(Option::V6, vendor_id);
741 
742  // Get the search index #1. It allows to search for option definitions
743  // using option code. If there's no such vendor-id space, we're out of luck
744  // anyway.
745  const OptionDefContainerTypeIndex* idx = NULL;
746  if (option_defs) {
747  idx = &(option_defs->get<1>());
748  }
749 
750  // The buffer being read comprises a set of options, each starting with
751  // a two-byte type code and a two-byte length field.
752  while (offset < length) {
753  if (offset + 4 > length) {
755  "Vendor option parse failed: truncated header");
756  }
757 
758  uint16_t opt_type = readUint16(&buf[offset], 2);
759  offset += 2;
760 
761  uint16_t opt_len = readUint16(&buf[offset], 2);
762  offset += 2;
763 
764  if (offset + opt_len > length) {
766  "Vendor option parse failed. Tried to parse "
767  << offset + opt_len << " bytes from " << length
768  << "-byte long buffer.");
769  }
770 
771  OptionPtr opt;
772  opt.reset();
773 
774  // If there is a definition for such a vendor option...
775  if (idx) {
776  // Get all definitions with the particular option
777  // code. Note that option code is non-unique within this
778  // container however at this point we expect to get one
779  // option definition with the particular code. If more are
780  // returned we report an error.
781  const OptionDefContainerTypeRange& range =
782  idx->equal_range(opt_type);
783  // Get the number of returned option definitions for the
784  // option code.
785  size_t num_defs = std::distance(range.first, range.second);
786 
787  if (num_defs > 1) {
788  // Multiple options of the same code are not supported
789  // right now!
790  isc_throw(isc::Unexpected, "Internal error: multiple option"
791  " definitions for option type " << opt_type <<
792  " returned. Currently it is not supported to"
793  " initialize multiple option definitions for the"
794  " same option code. This will be supported once"
795  " support for option spaces is implemented");
796  } else if (num_defs == 1) {
797  // The option definition has been found. Use it to create
798  // the option instance from the provided buffer chunk.
799  const OptionDefinitionPtr& def = *(range.first);
800  isc_throw_assert(def);
801  opt = def->optionFactory(Option::V6, opt_type,
802  buf.begin() + offset,
803  buf.begin() + offset + opt_len);
804  }
805  }
806 
807  // This can happen in one of 2 cases:
808  // 1. we do not have definitions for that vendor-space
809  // 2. we do have definitions, but that particular option was
810  // not defined
811 
812  if (!opt) {
813  opt = OptionPtr(new Option(Option::V6, opt_type,
814  buf.begin() + offset,
815  buf.begin() + offset + opt_len));
816  }
817 
818  // add option to options
819  if (opt) {
820  options.insert(std::make_pair(opt_type, opt));
821  }
822  offset += opt_len;
823  }
824 
825  return (offset);
826 }
827 
828 size_t
829 LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
830  OptionCollection& options) {
831  size_t offset = 0;
832 
833  // Get the list of standard option definitions.
834  const OptionDefContainerPtr& option_defs =
835  LibDHCP::getVendorOptionDefs(Option::V4, vendor_id);
836  // Get the search index #1. It allows to search for option definitions
837  // using option code.
838  const OptionDefContainerTypeIndex* idx = NULL;
839  if (option_defs) {
840  idx = &(option_defs->get<1>());
841  }
842 
843  // The buffer being read comprises a set of options, each starting with
844  // a one-byte type code and a one-byte length field.
845  while (offset < buf.size()) {
846  // Note that Vendor-Specific info option (RFC3925) has a
847  // different option format than Vendor-Spec info for
848  // DHCPv6. (there's additional layer of data-length)
849  uint8_t data_len = buf[offset++];
850 
851  if (offset + data_len > buf.size()) {
852  // The option is truncated.
854  "Attempt to parse truncated vendor option");
855  }
856 
857  uint8_t offset_end = offset + data_len;
858 
859  // beginning of data-chunk parser
860  while (offset < offset_end) {
861  uint8_t opt_type = buf[offset++];
862 
863  // No DHO_END or DHO_PAD in vendor options
864 
865  if (offset + 1 > offset_end) {
866  // opt_type must be cast to integer so as it is not
867  // treated as unsigned char value (a number is
868  // presented in error message).
870  "Attempt to parse truncated vendor option "
871  << static_cast<int>(opt_type));
872  }
873 
874  uint8_t opt_len = buf[offset++];
875  if (offset + opt_len > offset_end) {
877  "Option parse failed. Tried to parse "
878  << offset + opt_len << " bytes from " << buf.size()
879  << "-byte long buffer.");
880  }
881 
882  OptionPtr opt;
883  opt.reset();
884 
885  if (idx) {
886  // Get all definitions with the particular option
887  // code. Note that option code is non-unique within
888  // this container however at this point we expect to
889  // get one option definition with the particular
890  // code. If more are returned we report an error.
891  const OptionDefContainerTypeRange& range =
892  idx->equal_range(opt_type);
893  // Get the number of returned option definitions for
894  // the option code.
895  size_t num_defs = std::distance(range.first, range.second);
896 
897  if (num_defs > 1) {
898  // Multiple options of the same code are not
899  // supported right now!
900  isc_throw(isc::Unexpected, "Internal error: multiple"
901  " option definitions for option type "
902  << opt_type << " returned. Currently it is"
903  " not supported to initialize multiple option"
904  " definitions for the same option code."
905  " This will be supported once support for"
906  " option spaces is implemented");
907  } else if (num_defs == 1) {
908  // The option definition has been found. Use it to create
909  // the option instance from the provided buffer chunk.
910  const OptionDefinitionPtr& def = *(range.first);
911  isc_throw_assert(def);
912  opt = def->optionFactory(Option::V4, opt_type,
913  buf.begin() + offset,
914  buf.begin() + offset + opt_len);
915  }
916  }
917 
918  if (!opt) {
919  opt = OptionPtr(new Option(Option::V4, opt_type,
920  buf.begin() + offset,
921  buf.begin() + offset + opt_len));
922  }
923 
924  options.insert(std::make_pair(opt_type, opt));
925  offset += opt_len;
926 
927  } // end of data-chunk
928 
929  break; // end of the vendor block.
930  }
931  return (offset);
932 }
933 
934 void
935 LibDHCP::packOptions4(OutputBuffer& buf, const OptionCollection& options,
936  bool top, bool check) {
937  OptionCollection agent;
938  OptionPtr end;
939 
940  // We only look for type when we're the top level
941  // call that starts packing for options for a packet.
942  // This way we avoid doing type logic in all ensuing
943  // recursive calls.
944  if (top) {
945  auto x = options.find(DHO_DHCP_MESSAGE_TYPE);
946  if (x != options.end()) {
947  x->second->pack(buf, check);
948  }
949  }
950 
951  for (auto const& option : options) {
952  // TYPE is already done, RAI and END options must be last.
953  switch (option.first) {
955  break;
957  agent.insert(make_pair(DHO_DHCP_AGENT_OPTIONS, option.second));
958  break;
959  case DHO_END:
960  end = option.second;
961  break;
962  default:
963  option.second->pack(buf, check);
964  break;
965  }
966  }
967 
968  // Add the RAI option if it exists.
969  for (auto const& option : agent) {
970  option.second->pack(buf, check);
971  }
972 
973  // And at the end the END option.
974  if (end) {
975  end->pack(buf, check);
976  }
977 }
978 
979 bool
980 LibDHCP::splitOptions4(OptionCollection& options,
981  ScopedOptionsCopyContainer& scoped_options,
982  uint32_t used) {
983  bool result = false;
984  // We need to loop until all options have been split.
985  for (;;) {
986  bool found = false;
987  // Make a copy of the options so we can safely iterate over the
988  // old container.
989  OptionCollection copy = options;
990  // Iterate over all options in the container.
991  for (auto const& option : options) {
992  OptionPtr candidate = option.second;
993  OptionCollection& sub_options = candidate->getMutableOptions();
994  // Split suboptions recursively, if any.
995  OptionCollection distinct_options;
996  bool updated = false;
997  bool found_suboptions = false;
998  if (sub_options.size()) {
999  ScopedOptionsCopyPtr candidate_scoped_options(new ScopedSubOptionsCopy(candidate));
1000  found_suboptions = LibDHCP::splitOptions4(sub_options, scoped_options,
1001  used + candidate->getHeaderLen());
1002  // Also split if the overflow is caused by adding the suboptions
1003  // to the option data.
1004  if (found_suboptions || candidate->len() > 255) {
1005  updated = true;
1006  scoped_options.push_back(candidate_scoped_options);
1007  // Erase the old options from the new container so that only
1008  // the new options are present.
1009  copy.erase(option.first);
1010  result = true;
1011  // If there are suboptions which have been split, one parent
1012  // option will be created for each of the chunk of the
1013  // suboptions. If the suboptions have not been split,
1014  // but they cause overflow when added to the option data,
1015  // one parent option will contain the option data and one
1016  // parent option will be created for each suboption.
1017  // This will guarantee that none of the options plus
1018  // suboptions will have more than 255 bytes.
1019  for (auto sub_option : candidate->getMutableOptions()) {
1020  OptionPtr data_sub_option(new Option(candidate->getUniverse(),
1021  candidate->getType(),
1022  OptionBuffer(0)));
1023  data_sub_option->addOption(sub_option.second);
1024  distinct_options.insert(make_pair(candidate->getType(), data_sub_option));
1025  }
1026  }
1027  }
1028  // Create a new option containing only data that needs to be split
1029  // and no suboptions (which are inserted in completely separate
1030  // options which are added at the end).
1031  OptionPtr data_option(new Option(candidate->getUniverse(),
1032  candidate->getType(),
1033  OptionBuffer(candidate->getData().begin(),
1034  candidate->getData().end())));
1035  OutputBuffer buf(0);
1036  data_option->pack(buf, false);
1037  uint32_t header_len = candidate->getHeaderLen();
1038  // At least 1 + header length bytes must be available.
1039  if (used >= 255 - header_len) {
1040  isc_throw(BadValue, "there is no space left to split option "
1041  << candidate->getType() << " after parent already used "
1042  << used);
1043  }
1044  // Maximum option buffer size is 255 - header size - buffer size
1045  // already used by parent options.
1046  uint8_t len = 255 - header_len - used;
1047  // Current option size after split is the sum of the data and the
1048  // header size. The suboptions are serialized in separate options.
1049  // The header is duplicated in all new options, but the rest of the
1050  // data must be split and serialized.
1051  uint32_t size = buf.getLength() - header_len;
1052  // Only split if data does not fit in the current option.
1053  if (size > len) {
1054  // Erase the old option from the new container so that only new
1055  // options are present.
1056  if (!updated) {
1057  updated = true;
1058  // Erase the old options from the new container so that only
1059  // the new options are present.
1060  copy.erase(option.first);
1061  result = true;
1062  }
1063  uint32_t offset = 0;
1064  // Drain the option buffer in multiple new options until all
1065  // data is serialized.
1066  for (; offset != size;) {
1067  // Adjust the data length of the new option if remaining
1068  // data is less than the 255 - header size (for the last
1069  // option).
1070  if (size - offset < len) {
1071  len = size - offset;
1072  }
1073  // Create new option with data starting from offset and
1074  // containing truncated length.
1075  const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
1076  data += header_len;
1077  OptionPtr new_option(new Option(candidate->getUniverse(),
1078  candidate->getType(),
1079  OptionBuffer(data + offset,
1080  data + offset + len)));
1081  // Adjust the offset for remaining data to be written to the
1082  // next new option.
1083  offset += len;
1084  // Add the new option to the new container.
1085  copy.insert(make_pair(candidate->getType(), new_option));
1086  }
1087  } else if (candidate->len() > 255 && size) {
1088  // Also split if the overflow is caused by adding the suboptions
1089  // to the option data (which should be of non zero size).
1090  // Add the new option to the new container.
1091  copy.insert(make_pair(candidate->getType(), data_option));
1092  }
1093  if (updated) {
1094  // Add the new options containing the split suboptions, if any,
1095  // to the new container.
1096  copy.insert(distinct_options.begin(), distinct_options.end());
1097  // After all new options have been split and added, update the
1098  // option container with the new container.
1099  options = copy;
1100  // Other options might need splitting, so we need to iterate
1101  // again until no option needs splitting.
1102  found = true;
1103  break;
1104  }
1105  }
1106  // No option needs splitting, so we can exit the loop.
1107  if (!found) {
1108  break;
1109  }
1110  }
1111  return (result);
1112 }
1113 
1114 void
1115 LibDHCP::packOptions6(OutputBuffer& buf, const OptionCollection& options) {
1116  for (auto const& option : options) {
1117  option.second->pack(buf);
1118  }
1119 }
1120 
1121 void
1122 LibDHCP::OptionFactoryRegister(Option::Universe u, uint16_t opt_type,
1123  Option::Factory* factory) {
1124  switch (u) {
1125  case Option::V6:
1126  {
1127  if (v6factories_.find(opt_type) != v6factories_.end()) {
1128  isc_throw(BadValue, "There is already DHCPv6 factory registered "
1129  << "for option type " << opt_type);
1130  }
1131  v6factories_[opt_type] = factory;
1132  return;
1133  }
1134  case Option::V4:
1135  {
1136  // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
1137  // instantiated as an Option object, but rather consumed during packet parsing.
1138  if (opt_type == 0) {
1139  isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
1140  }
1141  // Option 255 is never instantiated as an option object. It is special
1142  // (a one-octet equal 255) option that is added at the end of all options
1143  // during packet assembly. It is also silently consumed during packet parsing.
1144  if (opt_type > 254) {
1145  isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
1146  }
1147  if (v4factories_.find(opt_type) != v4factories_.end()) {
1148  isc_throw(BadValue, "There is already DHCPv4 factory registered "
1149  << "for option type " << opt_type);
1150  }
1151  v4factories_[opt_type] = factory;
1152  return;
1153  }
1154  default:
1155  isc_throw(BadValue, "Invalid universe type specified.");
1156  }
1157 
1158  return;
1159 }
1160 
1161 bool
1162 LibDHCP::initOptionDefs() {
1163  for (uint32_t i = 0; OPTION_DEF_PARAMS[i].optionDefParams; ++i) {
1164  string space = OPTION_DEF_PARAMS[i].space;
1165  option_defs_[space] = OptionDefContainerPtr(new OptionDefContainer);
1166  initOptionSpace(option_defs_[space],
1167  OPTION_DEF_PARAMS[i].optionDefParams,
1168  OPTION_DEF_PARAMS[i].size);
1169  }
1170 
1171  return (true);
1172 }
1173 
1174 uint32_t
1175 LibDHCP::optionSpaceToVendorId(const string& option_space) {
1176  // 8 is a minimal length of "vendor-X" format
1177  if ((option_space.size() < 8) || (option_space.substr(0,7) != "vendor-")) {
1178  return (0);
1179  }
1180 
1181  int64_t check;
1182  try {
1183  // text after "vendor-", supposedly numbers only
1184  string x = option_space.substr(7);
1185 
1186  check = boost::lexical_cast<int64_t>(x);
1187  } catch (const boost::bad_lexical_cast &) {
1188  return (0);
1189  }
1190 
1191  if ((check < 0) || (check > std::numeric_limits<uint32_t>::max())) {
1192  return (0);
1193  }
1194 
1195  // value is small enough to fit
1196  return (static_cast<uint32_t>(check));
1197 }
1198 
1199 void
1201  size_t params_size) {
1202  // Container holding vendor options is typically not initialized, as it
1203  // is held in map of null pointers. We need to initialize here in this
1204  // case.
1205  if (!defs) {
1206  defs.reset(new OptionDefContainer());
1207  } else {
1208  defs->clear();
1209  }
1210 
1211  for (size_t i = 0; i < params_size; ++i) {
1212  string encapsulates(params[i].encapsulates);
1213  if (!encapsulates.empty() && params[i].array) {
1214  isc_throw(isc::BadValue, "invalid standard option definition: "
1215  << "option with code '" << params[i].code
1216  << "' may not encapsulate option space '"
1217  << encapsulates << "' because the definition"
1218  << " indicates that this option comprises an array"
1219  << " of values");
1220  }
1221 
1222  // Depending whether an option encapsulates an option space or not
1223  // we pick different constructor to create an instance of the option
1224  // definition.
1225  OptionDefinitionPtr definition;
1226  if (encapsulates.empty()) {
1227  // Option does not encapsulate any option space.
1228  definition.reset(new OptionDefinition(params[i].name,
1229  params[i].code,
1230  params[i].space,
1231  params[i].type,
1232  params[i].array));
1233  } else {
1234  // Option does encapsulate an option space.
1235  definition.reset(new OptionDefinition(params[i].name,
1236  params[i].code,
1237  params[i].space,
1238  params[i].type,
1239  params[i].encapsulates));
1240 
1241  }
1242 
1243  for (size_t rec = 0; rec < params[i].records_size; ++rec) {
1244  definition->addRecordField(params[i].records[rec]);
1245  }
1246 
1247  try {
1248  definition->validate();
1249  } catch (const isc::Exception&) {
1250  // This is unlikely event that validation fails and may
1251  // be only caused by programming error. To guarantee the
1252  // data consistency we clear all option definitions that
1253  // have been added so far and pass the exception forward.
1254  defs->clear();
1255  throw;
1256  }
1257 
1258  // option_defs is a multi-index container with no unique indexes
1259  // so push_back can't fail).
1260  static_cast<void>(defs->push_back(definition));
1261  }
1262 }
Encapsulation of option definition parameters and the structure size.
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition: isc_assert.h:18
Base class representing a DHCP option definition.
void addItem(const OptionDefinitionPtr &def)
Adds a new option definition to the container.
ItemsContainerPtr getItems(const Selector &option_space) const
Get all items for the particular option space.
#define V4V6_BIND_OPTION_SPACE
Exception thrown during option unpacking This exception is thrown when an error has occurred...
Definition: option.h:52
#define V4V6_RULE_OPTION_SPACE
std::list< Selector > getOptionSpaceNames() const
Get a list of existing option spaces.
OptionDefContainer::nth_index< 1 >::type OptionDefContainerTypeIndex
Type of the index #1 - option type.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
STL namespace.
Parameters being used to make up an option definition.
#define DOCSIS3_V4_OPTION_SPACE
global docsis3 option spaces
std::vector< ScopedOptionsCopyPtr > ScopedOptionsCopyContainer
A container of ScopedOptionsCopyPtr objects.
Definition: libdhcp++.h:32
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.
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...
#define LW_V6_OPTION_SPACE
Class of option definition space container.
const OptionDefParams DOCSIS3_V6_OPTION_DEFINITIONS[]
Definitions of standard DHCPv6 options.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
#define MAPT_V6_OPTION_SPACE
Definition: edns.h:19
#define MAPE_V6_OPTION_SPACE
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1360
const char * DOCSIS3_CLASS_MODEM
DOCSIS3.0 compatible cable modem.
Definition: libdhcp++.cc:82
A generic exception that is thrown when an unexpected error condition occurs.
#define ISC_V6_OPTION_SPACE
#define DHCP_AGENT_OPTION_SPACE
encapsulated option spaces
const char * DOCSIS3_CLASS_EROUTER
The class as specified in vendor-class option by the devices.
Definition: libdhcp++.cc:85
#define LAST_RESORT_V4_OPTION_SPACE
boost::shared_ptr< OptionDefContainer > OptionDefContainerPtr
Pointer to an option definition container.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
uint32_t readUint32(const uint8_t *buffer, size_t length)
Read Unsigned 32-Bit Integer from Buffer.
Definition: io_utilities.h:79
const OptionDefParams DOCSIS3_V4_OPTION_DEFINITIONS[]
Definitions of standard DHCPv4 options.
std::map< std::string, OptionDefContainerPtr > OptionDefContainers
Container that holds option definitions for various option spaces.
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
const int DOCSIS3_V6_OPTION_DEFINITIONS_SIZE
Number of option definitions defined.
uint16_t readUint16(const void *buffer, size_t length)
Read Unsigned 16-Bit Integer from Buffer.
Definition: io_utilities.h:28
#define DHCP6_OPTION_SPACE
#define DHCP4_OPTION_SPACE
global std option spaces
std::shared_ptr< ScopedSubOptionsCopy > ScopedOptionsCopyPtr
A pointer to a ScopedSubOptionsCopy object.
Definition: libdhcp++.h:30
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
Exception thrown during option unpacking This exception is thrown when an error has occurred unpackin...
Definition: option.h:67
#define DOCSIS3_V6_OPTION_SPACE
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
RAII object enabling duplication of the stored options and restoring the original options on destruct...
Definition: pkt.h:867
const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer())
#define VENDOR_ID_CABLE_LABS
This class implements set/commit mechanism for a single object.
Definition: staged_value.h:32
This class represents vendor-specific information option.
Definition: option_vendor.h:30
void initOptionSpace(OptionDefContainerPtr &defs, const OptionDefParams *params, size_t params_size)
Definition: libdhcp++.cc:1200
OptionDefContainer::nth_index< 2 >::type OptionDefContainerNameIndex
Type of the index #2 - option name.
const int DOCSIS3_V4_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...