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