libdhcp++.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. // Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <dhcp/dhcp4.h>
  16. #include <dhcp/dhcp6.h>
  17. #include <dhcp/libdhcp++.h>
  18. #include <dhcp/option.h>
  19. #include <dhcp/option_vendor.h>
  20. #include <dhcp/option6_ia.h>
  21. #include <dhcp/option6_iaaddr.h>
  22. #include <dhcp/option_definition.h>
  23. #include <dhcp/option_int_array.h>
  24. #include <dhcp/std_option_defs.h>
  25. #include <dhcp/docsis3_option_defs.h>
  26. #include <exceptions/exceptions.h>
  27. #include <util/buffer.h>
  28. #include <dhcp/option_definition.h>
  29. #include <boost/shared_array.hpp>
  30. #include <boost/shared_ptr.hpp>
  31. #include <list>
  32. using namespace std;
  33. using namespace isc::dhcp;
  34. using namespace isc::util;
  35. // static array with factories for options
  36. std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
  37. // static array with factories for options
  38. std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
  39. // Static container with DHCPv4 option definitions.
  40. OptionDefContainer LibDHCP::v4option_defs_;
  41. // Static container with DHCPv6 option definitions.
  42. OptionDefContainer LibDHCP::v6option_defs_;
  43. VendorOptionDefContainers LibDHCP::vendor4_defs_;
  44. VendorOptionDefContainers LibDHCP::vendor6_defs_;
  45. // Static container with option definitions created in runtime.
  46. OptionDefSpaceContainer LibDHCP::runtime_option_defs_;
  47. // Those two vendor classes are used for cable modems:
  48. /// DOCSIS3.0 compatible cable modem
  49. const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
  50. /// DOCSIS3.0 cable modem that has router built-in
  51. const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
  52. // Let's keep it in .cc file. Moving it to .h would require including optionDefParams
  53. // definitions there
  54. void initOptionSpace(OptionDefContainer& defs,
  55. const OptionDefParams* params,
  56. size_t params_size);
  57. const OptionDefContainer&
  58. LibDHCP::getOptionDefs(const Option::Universe u) {
  59. switch (u) {
  60. case Option::V4:
  61. if (v4option_defs_.empty()) {
  62. initStdOptionDefs4();
  63. initVendorOptsDocsis4();
  64. }
  65. return (v4option_defs_);
  66. case Option::V6:
  67. if (v6option_defs_.empty()) {
  68. initStdOptionDefs6();
  69. initVendorOptsDocsis6();
  70. }
  71. return (v6option_defs_);
  72. default:
  73. isc_throw(isc::BadValue, "invalid universe " << u << " specified");
  74. }
  75. }
  76. const OptionDefContainer*
  77. LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
  78. if (vendor_id == VENDOR_ID_CABLE_LABS &&
  79. vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
  80. initVendorOptsDocsis4();
  81. }
  82. VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
  83. if (def == vendor4_defs_.end()) {
  84. // No such vendor-id space
  85. return (NULL);
  86. }
  87. return (&(def->second));
  88. }
  89. const OptionDefContainer*
  90. LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
  91. if (vendor_id == VENDOR_ID_CABLE_LABS &&
  92. vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) {
  93. initVendorOptsDocsis6();
  94. }
  95. if (vendor_id == ENTERPRISE_ID_ISC &&
  96. vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) {
  97. initVendorOptsIsc6();
  98. }
  99. VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
  100. if (def == vendor6_defs_.end()) {
  101. // No such vendor-id space
  102. return (NULL);
  103. }
  104. return (&(def->second));
  105. }
  106. OptionDefinitionPtr
  107. LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
  108. const OptionDefContainer& defs = getOptionDefs(u);
  109. const OptionDefContainerTypeIndex& idx = defs.get<1>();
  110. const OptionDefContainerTypeRange& range = idx.equal_range(code);
  111. if (range.first != range.second) {
  112. return (*range.first);
  113. }
  114. return (OptionDefinitionPtr());
  115. }
  116. OptionDefinitionPtr
  117. LibDHCP::getOptionDef(const Option::Universe u, const std::string& name) {
  118. const OptionDefContainer& defs = getOptionDefs(u);
  119. const OptionDefContainerNameIndex& idx = defs.get<2>();
  120. const OptionDefContainerNameRange& range = idx.equal_range(name);
  121. if (range.first != range.second) {
  122. return (*range.first);
  123. }
  124. return (OptionDefinitionPtr());
  125. }
  126. OptionDefinitionPtr
  127. LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
  128. const std::string& name) {
  129. const OptionDefContainer* defs = NULL;
  130. if (u == Option::V4) {
  131. defs = getVendorOption4Defs(vendor_id);
  132. } else if (u == Option::V6) {
  133. defs = getVendorOption6Defs(vendor_id);
  134. }
  135. if (!defs) {
  136. return (OptionDefinitionPtr());
  137. }
  138. const OptionDefContainerNameIndex& idx = defs->get<2>();
  139. const OptionDefContainerNameRange& range = idx.equal_range(name);
  140. if (range.first != range.second) {
  141. return (*range.first);
  142. }
  143. return (OptionDefinitionPtr());
  144. }
  145. OptionDefinitionPtr
  146. LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
  147. const uint16_t code) {
  148. const OptionDefContainer* defs = NULL;
  149. if (u == Option::V4) {
  150. defs = getVendorOption4Defs(vendor_id);
  151. } else if (u == Option::V6) {
  152. defs = getVendorOption6Defs(vendor_id);
  153. }
  154. if (!defs) {
  155. // Weird universe or unknown vendor_id. We don't care. No definitions
  156. // one way or another
  157. // What is it anyway?
  158. return (OptionDefinitionPtr());
  159. }
  160. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  161. const OptionDefContainerTypeRange& range = idx.equal_range(code);
  162. if (range.first != range.second) {
  163. return (*range.first);
  164. }
  165. return (OptionDefinitionPtr());
  166. }
  167. OptionDefinitionPtr
  168. LibDHCP::getRuntimeOptionDef(const std::string& space, const uint16_t code) {
  169. OptionDefContainerPtr container = runtime_option_defs_.getItems(space);
  170. const OptionDefContainerTypeIndex& index = container->get<1>();
  171. const OptionDefContainerTypeRange& range = index.equal_range(code);
  172. if (range.first != range.second) {
  173. return (*range.first);
  174. }
  175. return (OptionDefinitionPtr());
  176. }
  177. OptionDefinitionPtr
  178. LibDHCP::getRuntimeOptionDef(const std::string& space, const std::string& name) {
  179. OptionDefContainerPtr container = runtime_option_defs_.getItems(space);
  180. const OptionDefContainerNameIndex& index = container->get<2>();
  181. const OptionDefContainerNameRange& range = index.equal_range(name);
  182. if (range.first != range.second) {
  183. return (*range.first);
  184. }
  185. return (OptionDefinitionPtr());
  186. }
  187. OptionDefContainerPtr
  188. LibDHCP::getRuntimeOptionDefs(const std::string& space) {
  189. return (runtime_option_defs_.getItems(space));
  190. }
  191. void
  192. LibDHCP::setRuntimeOptionDefs(const OptionDefSpaceContainer& defs) {
  193. std::list<std::string> option_space_names = defs.getOptionSpaceNames();
  194. for (std::list<std::string>::const_iterator name = option_space_names.begin();
  195. name != option_space_names.end(); ++name) {
  196. OptionDefContainerPtr container = defs.getItems(*name);
  197. for (OptionDefContainer::const_iterator def = container->begin();
  198. def != container->end(); ++def) {
  199. OptionDefinitionPtr def_copy(new OptionDefinition(**def));
  200. runtime_option_defs_.addItem(def_copy, *name);
  201. }
  202. }
  203. }
  204. void
  205. LibDHCP::clearRuntimeOptionDefs() {
  206. runtime_option_defs_.clearItems();
  207. }
  208. bool
  209. LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
  210. if (u == Option::V6) {
  211. if (code < 79 &&
  212. code != 10 &&
  213. code != 35) {
  214. return (true);
  215. }
  216. } else if (u == Option::V4) {
  217. if (!(code == 84 ||
  218. code == 96 ||
  219. (code > 101 && code < 112) ||
  220. code == 115 ||
  221. code == 126 ||
  222. code == 127 ||
  223. (code > 146 && code < 150) ||
  224. (code > 177 && code < 208) ||
  225. (code > 213 && code < 220) ||
  226. (code > 221 && code < 255))) {
  227. return (true);
  228. }
  229. }
  230. return (false);
  231. }
  232. OptionPtr
  233. LibDHCP::optionFactory(Option::Universe u,
  234. uint16_t type,
  235. const OptionBuffer& buf) {
  236. FactoryMap::iterator it;
  237. if (u == Option::V4) {
  238. it = v4factories_.find(type);
  239. if (it == v4factories_.end()) {
  240. isc_throw(BadValue, "factory function not registered "
  241. "for DHCP v4 option type " << type);
  242. }
  243. } else if (u == Option::V6) {
  244. it = v6factories_.find(type);
  245. if (it == v6factories_.end()) {
  246. isc_throw(BadValue, "factory function not registered "
  247. "for DHCPv6 option type " << type);
  248. }
  249. } else {
  250. isc_throw(BadValue, "invalid universe specified (expected "
  251. "Option::V4 or Option::V6");
  252. }
  253. return (it->second(u, type, buf));
  254. }
  255. size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
  256. const std::string& option_space,
  257. isc::dhcp::OptionCollection& options,
  258. size_t* relay_msg_offset /* = 0 */,
  259. size_t* relay_msg_len /* = 0 */) {
  260. size_t offset = 0;
  261. size_t length = buf.size();
  262. size_t last_offset = 0;
  263. // Get the list of standard option definitions.
  264. OptionDefContainer option_defs;
  265. if (option_space == "dhcp6") {
  266. option_defs = LibDHCP::getOptionDefs(Option::V6);
  267. } else {
  268. OptionDefContainerPtr option_defs_ptr =
  269. LibDHCP::getRuntimeOptionDefs(option_space);
  270. if (option_defs_ptr) {
  271. option_defs = *option_defs_ptr;
  272. }
  273. }
  274. // @todo Once we implement other option spaces we should add else clause
  275. // here and gather option definitions for them. For now leaving option_defs
  276. // empty will imply creation of generic Option.
  277. // Get the search index #1. It allows to search for option definitions
  278. // using option code.
  279. const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
  280. // The buffer being read comprises a set of options, each starting with
  281. // a two-byte type code and a two-byte length field.
  282. while (offset < length) {
  283. // Save the current offset for backtracking
  284. last_offset = offset;
  285. // Check if there is room for another option
  286. if (offset + 4 > length) {
  287. // Still something but smaller than an option
  288. return (last_offset);
  289. }
  290. // Parse the option header
  291. uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
  292. offset += 2;
  293. uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
  294. offset += 2;
  295. if (offset + opt_len > length) {
  296. // We peeked at the option header of the next option, but
  297. // discovered that it would end up beyond buffer end, so
  298. // the option is truncated. Hence we can't parse
  299. // it. Therefore we revert back by those bytes (as if
  300. // we never parsed them).
  301. //
  302. // @note it is the responsibility of the caller to throw
  303. // an exception on partial parsing
  304. return (last_offset);
  305. }
  306. if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
  307. // remember offset of the beginning of the relay-msg option
  308. *relay_msg_offset = offset;
  309. *relay_msg_len = opt_len;
  310. // do not create that relay-msg option
  311. offset += opt_len;
  312. continue;
  313. }
  314. if (opt_type == D6O_VENDOR_OPTS) {
  315. if (offset + 4 > length) {
  316. // Truncated vendor-option. We expect at least
  317. // 4 bytes for the enterprise-id field. Let's roll back
  318. // option code + option length (4 bytes) and return.
  319. return (last_offset);
  320. }
  321. // Parse this as vendor option
  322. OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
  323. buf.begin() + offset + opt_len));
  324. options.insert(std::make_pair(opt_type, vendor_opt));
  325. offset += opt_len;
  326. continue;
  327. }
  328. // Get all definitions with the particular option code. Note
  329. // that option code is non-unique within this container
  330. // however at this point we expect to get one option
  331. // definition with the particular code. If more are returned
  332. // we report an error.
  333. const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
  334. // Get the number of returned option definitions for the option code.
  335. size_t num_defs = distance(range.first, range.second);
  336. OptionPtr opt;
  337. if (num_defs > 1) {
  338. // Multiple options of the same code are not supported right now!
  339. isc_throw(isc::Unexpected, "Internal error: multiple option"
  340. " definitions for option type " << opt_type <<
  341. " returned. Currently it is not supported to initialize"
  342. " multiple option definitions for the same option code."
  343. " This will be supported once support for option spaces"
  344. " is implemented");
  345. } else if (num_defs == 0) {
  346. // @todo Don't crash if definition does not exist because
  347. // only a few option definitions are initialized right
  348. // now. In the future we will initialize definitions for
  349. // all options and we will remove this elseif. For now,
  350. // return generic option.
  351. opt = OptionPtr(new Option(Option::V6, opt_type,
  352. buf.begin() + offset,
  353. buf.begin() + offset + opt_len));
  354. } else {
  355. // The option definition has been found. Use it to create
  356. // the option instance from the provided buffer chunk.
  357. const OptionDefinitionPtr& def = *(range.first);
  358. assert(def);
  359. opt = def->optionFactory(Option::V6, opt_type,
  360. buf.begin() + offset,
  361. buf.begin() + offset + opt_len);
  362. }
  363. // add option to options
  364. options.insert(std::make_pair(opt_type, opt));
  365. offset += opt_len;
  366. }
  367. last_offset = offset;
  368. return (last_offset);
  369. }
  370. size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
  371. const std::string& option_space,
  372. isc::dhcp::OptionCollection& options) {
  373. size_t offset = 0;
  374. size_t last_offset = 0;
  375. // Get the list of standard option definitions.
  376. OptionDefContainer option_defs;
  377. if (option_space == "dhcp4") {
  378. option_defs = LibDHCP::getOptionDefs(Option::V4);
  379. }
  380. // @todo Once we implement other option spaces we should add else clause
  381. // here and gather option definitions for them. For now leaving option_defs
  382. // empty will imply creation of generic Option.
  383. // Get the search index #1. It allows to search for option definitions
  384. // using option code.
  385. const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
  386. // The buffer being read comprises a set of options, each starting with
  387. // a one-byte type code and a one-byte length field.
  388. while (offset < buf.size()) {
  389. // Save the current offset for backtracking
  390. last_offset = offset;
  391. // Get the option type
  392. uint8_t opt_type = buf[offset++];
  393. // DHO_END is a special, one octet long option
  394. if (opt_type == DHO_END) {
  395. // just return. Don't need to add DHO_END option
  396. // Don't return offset because it makes this condition
  397. // and partial parsing impossible to recognize.
  398. return (last_offset);
  399. }
  400. // DHO_PAD is just a padding after DHO_END. Let's continue parsing
  401. // in case we receive a message without DHO_END.
  402. if (opt_type == DHO_PAD)
  403. continue;
  404. if (offset + 1 > buf.size()) {
  405. // We peeked at the option header of the next option, but
  406. // discovered that it would end up beyond buffer end, so
  407. // the option is truncated. Hence we can't parse
  408. // it. Therefore we revert back (as if we never parsed it).
  409. //
  410. // @note it is the responsibility of the caller to throw
  411. // an exception on partial parsing
  412. return (last_offset);
  413. }
  414. uint8_t opt_len = buf[offset++];
  415. if (offset + opt_len > buf.size()) {
  416. // We peeked at the option header of the next option, but
  417. // discovered that it would end up beyond buffer end, so
  418. // the option is truncated. Hence we can't parse
  419. // it. Therefore we revert back (as if we never parsed it).
  420. return (last_offset);
  421. }
  422. // Get all definitions with the particular option code. Note
  423. // that option code is non-unique within this container
  424. // however at this point we expect to get one option
  425. // definition with the particular code. If more are returned
  426. // we report an error.
  427. const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
  428. // Get the number of returned option definitions for the option code.
  429. size_t num_defs = distance(range.first, range.second);
  430. OptionPtr opt;
  431. if (num_defs > 1) {
  432. // Multiple options of the same code are not supported right now!
  433. isc_throw(isc::Unexpected, "Internal error: multiple option"
  434. " definitions for option type " <<
  435. static_cast<int>(opt_type) <<
  436. " returned. Currently it is not supported to initialize"
  437. " multiple option definitions for the same option code."
  438. " This will be supported once support for option spaces"
  439. " is implemented");
  440. } else if (num_defs == 0) {
  441. opt = OptionPtr(new Option(Option::V4, opt_type,
  442. buf.begin() + offset,
  443. buf.begin() + offset + opt_len));
  444. } else {
  445. // The option definition has been found. Use it to create
  446. // the option instance from the provided buffer chunk.
  447. const OptionDefinitionPtr& def = *(range.first);
  448. assert(def);
  449. opt = def->optionFactory(Option::V4, opt_type,
  450. buf.begin() + offset,
  451. buf.begin() + offset + opt_len);
  452. }
  453. options.insert(std::make_pair(opt_type, opt));
  454. offset += opt_len;
  455. }
  456. last_offset = offset;
  457. return (last_offset);
  458. }
  459. size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
  460. const OptionBuffer& buf,
  461. isc::dhcp::OptionCollection& options) {
  462. size_t offset = 0;
  463. size_t length = buf.size();
  464. // Get the list of option definitions for this particular vendor-id
  465. const OptionDefContainer* option_defs =
  466. LibDHCP::getVendorOption6Defs(vendor_id);
  467. // Get the search index #1. It allows to search for option definitions
  468. // using option code. If there's no such vendor-id space, we're out of luck
  469. // anyway.
  470. const OptionDefContainerTypeIndex* idx = NULL;
  471. if (option_defs) {
  472. idx = &(option_defs->get<1>());
  473. }
  474. // The buffer being read comprises a set of options, each starting with
  475. // a two-byte type code and a two-byte length field.
  476. while (offset < length) {
  477. if (offset + 4 > length) {
  478. isc_throw(OutOfRange,
  479. "Vendor option parse failed: truncated header");
  480. }
  481. uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
  482. offset += 2;
  483. uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
  484. offset += 2;
  485. if (offset + opt_len > length) {
  486. isc_throw(OutOfRange, "Vendor option parse failed. Tried to parse "
  487. << offset + opt_len << " bytes from " << length
  488. << "-byte long buffer.");
  489. }
  490. OptionPtr opt;
  491. opt.reset();
  492. // If there is a definition for such a vendor option...
  493. if (idx) {
  494. // Get all definitions with the particular option
  495. // code. Note that option code is non-unique within this
  496. // container however at this point we expect to get one
  497. // option definition with the particular code. If more are
  498. // returned we report an error.
  499. const OptionDefContainerTypeRange& range =
  500. idx->equal_range(opt_type);
  501. // Get the number of returned option definitions for the
  502. // option code.
  503. size_t num_defs = distance(range.first, range.second);
  504. if (num_defs > 1) {
  505. // Multiple options of the same code are not supported
  506. // right now!
  507. isc_throw(isc::Unexpected, "Internal error: multiple option"
  508. " definitions for option type " << opt_type <<
  509. " returned. Currently it is not supported to"
  510. " initialize multiple option definitions for the"
  511. " same option code. This will be supported once"
  512. " support for option spaces is implemented");
  513. } else if (num_defs == 1) {
  514. // The option definition has been found. Use it to create
  515. // the option instance from the provided buffer chunk.
  516. const OptionDefinitionPtr& def = *(range.first);
  517. assert(def);
  518. opt = def->optionFactory(Option::V6, opt_type,
  519. buf.begin() + offset,
  520. buf.begin() + offset + opt_len);
  521. }
  522. }
  523. // This can happen in one of 2 cases:
  524. // 1. we do not have definitions for that vendor-space
  525. // 2. we do have definitions, but that particular option was
  526. // not defined
  527. if (!opt) {
  528. opt = OptionPtr(new Option(Option::V6, opt_type,
  529. buf.begin() + offset,
  530. buf.begin() + offset + opt_len));
  531. }
  532. // add option to options
  533. if (opt) {
  534. options.insert(std::make_pair(opt_type, opt));
  535. }
  536. offset += opt_len;
  537. }
  538. return (offset);
  539. }
  540. size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
  541. isc::dhcp::OptionCollection& options) {
  542. size_t offset = 0;
  543. // Get the list of stdandard option definitions.
  544. const OptionDefContainer* option_defs =
  545. LibDHCP::getVendorOption4Defs(vendor_id);
  546. // Get the search index #1. It allows to search for option definitions
  547. // using option code.
  548. const OptionDefContainerTypeIndex* idx = NULL;
  549. if (option_defs) {
  550. idx = &(option_defs->get<1>());
  551. }
  552. // The buffer being read comprises a set of options, each starting with
  553. // a one-byte type code and a one-byte length field.
  554. while (offset < buf.size()) {
  555. // Note that Vendor-Specific info option (RFC3925) has a
  556. // different option format than Vendor-Spec info for
  557. // DHCPv6. (there's additional layer of data-length)
  558. uint8_t data_len = buf[offset++];
  559. if (offset + data_len > buf.size()) {
  560. // The option is truncated.
  561. isc_throw(OutOfRange, "Attempt to parse truncated vendor option");
  562. }
  563. uint8_t offset_end = offset + data_len;
  564. // beginning of data-chunk parser
  565. while (offset < offset_end) {
  566. uint8_t opt_type = buf[offset++];
  567. // No DHO_END or DHO_PAD in vendor options
  568. if (offset + 1 > offset_end) {
  569. // opt_type must be cast to integer so as it is not
  570. // treated as unsigned char value (a number is
  571. // presented in error message).
  572. isc_throw(OutOfRange,
  573. "Attempt to parse truncated vendor option "
  574. << static_cast<int>(opt_type));
  575. }
  576. uint8_t opt_len = buf[offset++];
  577. if (offset + opt_len > offset_end) {
  578. isc_throw(OutOfRange, "Option parse failed. Tried to parse "
  579. << offset + opt_len << " bytes from " << buf.size()
  580. << "-byte long buffer.");
  581. }
  582. OptionPtr opt;
  583. opt.reset();
  584. if (idx) {
  585. // Get all definitions with the particular option
  586. // code. Note that option code is non-unique within
  587. // this container however at this point we expect to
  588. // get one option definition with the particular
  589. // code. If more are returned we report an error.
  590. const OptionDefContainerTypeRange& range =
  591. idx->equal_range(opt_type);
  592. // Get the number of returned option definitions for
  593. // the option code.
  594. size_t num_defs = distance(range.first, range.second);
  595. if (num_defs > 1) {
  596. // Multiple options of the same code are not
  597. // supported right now!
  598. isc_throw(isc::Unexpected, "Internal error: multiple"
  599. " option definitions for option type "
  600. << opt_type << " returned. Currently it is"
  601. " not supported to initialize multiple option"
  602. " definitions for the same option code."
  603. " This will be supported once support for"
  604. " option spaces is implemented");
  605. } else if (num_defs == 1) {
  606. // The option definition has been found. Use it to create
  607. // the option instance from the provided buffer chunk.
  608. const OptionDefinitionPtr& def = *(range.first);
  609. assert(def);
  610. opt = def->optionFactory(Option::V4, opt_type,
  611. buf.begin() + offset,
  612. buf.begin() + offset + opt_len);
  613. }
  614. }
  615. if (!opt) {
  616. opt = OptionPtr(new Option(Option::V4, opt_type,
  617. buf.begin() + offset,
  618. buf.begin() + offset + opt_len));
  619. }
  620. options.insert(std::make_pair(opt_type, opt));
  621. offset += opt_len;
  622. } // end of data-chunk
  623. break; // end of the vendor block.
  624. }
  625. return (offset);
  626. }
  627. void
  628. LibDHCP::packOptions(isc::util::OutputBuffer& buf,
  629. const OptionCollection& options) {
  630. for (OptionCollection::const_iterator it = options.begin();
  631. it != options.end(); ++it) {
  632. it->second->pack(buf);
  633. }
  634. }
  635. void LibDHCP::OptionFactoryRegister(Option::Universe u,
  636. uint16_t opt_type,
  637. Option::Factory* factory) {
  638. switch (u) {
  639. case Option::V6: {
  640. if (v6factories_.find(opt_type) != v6factories_.end()) {
  641. isc_throw(BadValue, "There is already DHCPv6 factory registered "
  642. << "for option type " << opt_type);
  643. }
  644. v6factories_[opt_type]=factory;
  645. return;
  646. }
  647. case Option::V4:
  648. {
  649. // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
  650. // instantiated as an Option object, but rather consumed during packet parsing.
  651. if (opt_type == 0) {
  652. isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
  653. }
  654. // Option 255 is never instantiated as an option object. It is special
  655. // (a one-octet equal 255) option that is added at the end of all options
  656. // during packet assembly. It is also silently consumed during packet parsing.
  657. if (opt_type > 254) {
  658. isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
  659. }
  660. if (v4factories_.find(opt_type)!=v4factories_.end()) {
  661. isc_throw(BadValue, "There is already DHCPv4 factory registered "
  662. << "for option type " << opt_type);
  663. }
  664. v4factories_[opt_type]=factory;
  665. return;
  666. }
  667. default:
  668. isc_throw(BadValue, "Invalid universe type specified.");
  669. }
  670. return;
  671. }
  672. void
  673. LibDHCP::initStdOptionDefs4() {
  674. initOptionSpace(v4option_defs_, OPTION_DEF_PARAMS4, OPTION_DEF_PARAMS_SIZE4);
  675. }
  676. void
  677. LibDHCP::initStdOptionDefs6() {
  678. initOptionSpace(v6option_defs_, OPTION_DEF_PARAMS6, OPTION_DEF_PARAMS_SIZE6);
  679. }
  680. void
  681. LibDHCP::initVendorOptsDocsis4() {
  682. initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE);
  683. }
  684. void
  685. LibDHCP::initVendorOptsDocsis6() {
  686. vendor6_defs_[VENDOR_ID_CABLE_LABS] = OptionDefContainer();
  687. initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
  688. }
  689. void
  690. LibDHCP::initVendorOptsIsc6() {
  691. vendor6_defs_[ENTERPRISE_ID_ISC] = OptionDefContainer();
  692. initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
  693. }
  694. void initOptionSpace(OptionDefContainer& defs,
  695. const OptionDefParams* params,
  696. size_t params_size) {
  697. defs.clear();
  698. for (size_t i = 0; i < params_size; ++i) {
  699. std::string encapsulates(params[i].encapsulates);
  700. if (!encapsulates.empty() && params[i].array) {
  701. isc_throw(isc::BadValue, "invalid standard option definition: "
  702. << "option with code '" << params[i].code
  703. << "' may not encapsulate option space '"
  704. << encapsulates << "' because the definition"
  705. << " indicates that this option comprises an array"
  706. << " of values");
  707. }
  708. // Depending whether an option encapsulates an option space or not
  709. // we pick different constructor to create an instance of the option
  710. // definition.
  711. OptionDefinitionPtr definition;
  712. if (encapsulates.empty()) {
  713. // Option does not encapsulate any option space.
  714. definition.reset(new OptionDefinition(params[i].name,
  715. params[i].code,
  716. params[i].type,
  717. params[i].array));
  718. } else {
  719. // Option does encapsulate an option space.
  720. definition.reset(new OptionDefinition(params[i].name,
  721. params[i].code,
  722. params[i].type,
  723. params[i].encapsulates));
  724. }
  725. for (size_t rec = 0; rec < params[i].records_size; ++rec) {
  726. definition->addRecordField(params[i].records[rec]);
  727. }
  728. try {
  729. definition->validate();
  730. } catch (const isc::Exception&) {
  731. // This is unlikely event that validation fails and may
  732. // be only caused by programming error. To guarantee the
  733. // data consistency we clear all option definitions that
  734. // have been added so far and pass the exception forward.
  735. defs.clear();
  736. throw;
  737. }
  738. defs.push_back(definition);
  739. }
  740. }