libdhcp++.cc 30 KB

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