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