config_parser_unittest.cc 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. // Copyright (C) 2012-2013 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 <config/ccsession.h>
  16. #include <dhcp/libdhcp++.h>
  17. #include <dhcp/option6_ia.h>
  18. #include <dhcp6/config_parser.h>
  19. #include <dhcp6/dhcp6_srv.h>
  20. #include <dhcpsrv/cfgmgr.h>
  21. #include <dhcpsrv/subnet.h>
  22. #include <boost/foreach.hpp>
  23. #include <gtest/gtest.h>
  24. #include <fstream>
  25. #include <iostream>
  26. #include <sstream>
  27. #include <arpa/inet.h>
  28. using namespace std;
  29. using namespace isc;
  30. using namespace isc::dhcp;
  31. using namespace isc::asiolink;
  32. using namespace isc::data;
  33. using namespace isc::config;
  34. namespace {
  35. class Dhcp6ParserTest : public ::testing::Test {
  36. public:
  37. Dhcp6ParserTest() :rcode_(-1), srv_(0) {
  38. // srv_(0) means to not open any sockets. We don't want to
  39. // deal with sockets here, just check if configuration handling
  40. // is sane.
  41. }
  42. ~Dhcp6ParserTest() {
  43. // Reset configuration database after each test.
  44. resetConfiguration();
  45. };
  46. // Checks if config_result (result of DHCP server configuration) has
  47. // expected code (0 for success, other for failures).
  48. // Also stores result in rcode_ and comment_.
  49. void checkResult(ConstElementPtr status, int expected_code) {
  50. ASSERT_TRUE(status);
  51. comment_ = parseAnswer(rcode_, status);
  52. EXPECT_EQ(expected_code, rcode_);
  53. }
  54. /// @brief Create the simple configuration with single option.
  55. ///
  56. /// This function allows to set one of the parameters that configure
  57. /// option value. These parameters are: "name", "code", "data" and
  58. /// "csv-format".
  59. ///
  60. /// @param param_value string holiding option parameter value to be
  61. /// injected into the configuration string.
  62. /// @param parameter name of the parameter to be configured with
  63. /// param value.
  64. std::string createConfigWithOption(const std::string& param_value,
  65. const std::string& parameter) {
  66. std::map<std::string, std::string> params;
  67. if (parameter == "name") {
  68. params["name"] = param_value;
  69. params["code"] = "80";
  70. params["data"] = "AB CDEF0105";
  71. params["csv-format"] = "False";
  72. } else if (parameter == "code") {
  73. params["name"] = "option_foo";
  74. params["code"] = param_value;
  75. params["data"] = "AB CDEF0105";
  76. params["csv-format"] = "False";
  77. } else if (parameter == "data") {
  78. params["name"] = "option_foo";
  79. params["code"] = "80";
  80. params["data"] = param_value;
  81. params["csv-format"] = "False";
  82. } else if (parameter == "csv-format") {
  83. params["name"] = "option_foo";
  84. params["code"] = "80";
  85. params["data"] = "AB CDEF0105";
  86. params["csv-format"] = param_value;
  87. }
  88. return (createConfigWithOption(params));
  89. }
  90. std::string createConfigWithOption(const std::map<std::string,
  91. std::string>& params)
  92. {
  93. std::ostringstream stream;
  94. stream << "{ \"interface\": [ \"all\" ],"
  95. "\"preferred-lifetime\": 3000,"
  96. "\"rebind-timer\": 2000, "
  97. "\"renew-timer\": 1000, "
  98. "\"subnet6\": [ { "
  99. " \"pool\": [ \"2001:db8:1::/80\" ],"
  100. " \"subnet\": \"2001:db8:1::/64\", "
  101. " \"option-data\": [ {";
  102. bool first = true;
  103. typedef std::pair<std::string, std::string> ParamPair;
  104. BOOST_FOREACH(ParamPair param, params) {
  105. if (!first) {
  106. stream << ", ";
  107. } else {
  108. // cppcheck-suppress unreadVariable
  109. first = false;
  110. }
  111. if (param.first == "name") {
  112. stream << "\"name\": \"" << param.second << "\"";
  113. } else if (param.first == "code") {
  114. stream << "\"code\": " << param.second;;
  115. } else if (param.first == "data") {
  116. stream << "\"data\": \"" << param.second << "\"";
  117. } else if (param.first == "csv-format") {
  118. stream << "\"csv-format\": " << param.second;
  119. }
  120. }
  121. stream <<
  122. " } ]"
  123. " } ],"
  124. "\"valid-lifetime\": 4000 }";
  125. return (stream.str());
  126. }
  127. /// @brief Reset configuration database.
  128. ///
  129. /// This function resets configuration data base by
  130. /// removing all subnets and option-data. Reset must
  131. /// be performed after each test to make sure that
  132. /// contents of the database do not affect result of
  133. /// subsequent tests.
  134. void resetConfiguration() {
  135. ConstElementPtr status;
  136. string config = "{ \"interface\": [ \"all\" ],"
  137. "\"preferred-lifetime\": 3000,"
  138. "\"rebind-timer\": 2000, "
  139. "\"renew-timer\": 1000, "
  140. "\"valid-lifetime\": 4000, "
  141. "\"subnet6\": [ ], "
  142. "\"option-def\": [ ], "
  143. "\"option-data\": [ ] }";
  144. try {
  145. ElementPtr json = Element::fromJSON(config);
  146. status = configureDhcp6Server(srv_, json);
  147. } catch (const std::exception& ex) {
  148. FAIL() << "Fatal error: unable to reset configuration database"
  149. << " after the test. The following configuration was used"
  150. << " to reset database: " << std::endl
  151. << config << std::endl
  152. << " and the following error message was returned:"
  153. << ex.what() << std::endl;
  154. }
  155. // status object must not be NULL
  156. if (!status) {
  157. FAIL() << "Fatal error: unable to reset configuration database"
  158. << " after the test. Configuration function returned"
  159. << " NULL pointer" << std::endl;
  160. }
  161. comment_ = parseAnswer(rcode_, status);
  162. // returned value should be 0 (configuration success)
  163. if (rcode_ != 0) {
  164. FAIL() << "Fatal error: unable to reset configuration database"
  165. << " after the test. Configuration function returned"
  166. << " error code " << rcode_ << std::endl;
  167. }
  168. }
  169. /// @brief Test invalid option parameter value.
  170. ///
  171. /// This test function constructs the simple configuration
  172. /// string and injects invalid option configuration into it.
  173. /// It expects that parser will fail with provided option code.
  174. ///
  175. /// @param param_value string holding invalid option parameter value
  176. /// to be injected into configuration string.
  177. /// @param parameter name of the parameter to be configured with
  178. /// param_value (can be any of "name", "code", "data")
  179. void testInvalidOptionParam(const std::string& param_value,
  180. const std::string& parameter) {
  181. ConstElementPtr x;
  182. std::string config = createConfigWithOption(param_value, parameter);
  183. ElementPtr json = Element::fromJSON(config);
  184. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  185. ASSERT_TRUE(x);
  186. comment_ = parseAnswer(rcode_, x);
  187. ASSERT_EQ(1, rcode_);
  188. }
  189. /// @brief Test option against given code and data.
  190. ///
  191. /// @param option_desc option descriptor that carries the option to
  192. /// be tested.
  193. /// @param expected_code expected code of the option.
  194. /// @param expected_data expected data in the option.
  195. /// @param expected_data_len length of the reference data.
  196. /// @param extra_data if true extra data is allowed in an option
  197. /// after tested data.
  198. void testOption(const Subnet::OptionDescriptor& option_desc,
  199. uint16_t expected_code, const uint8_t* expected_data,
  200. size_t expected_data_len,
  201. bool extra_data = false) {
  202. // Check if option descriptor contains valid option pointer.
  203. ASSERT_TRUE(option_desc.option);
  204. // Verify option type.
  205. EXPECT_EQ(expected_code, option_desc.option->getType());
  206. // We may have many different option types being created. Some of them
  207. // have dedicated classes derived from Option class. In such case if
  208. // we want to verify the option contents against expected_data we have
  209. // to prepare raw buffer with the contents of the option. The easiest
  210. // way is to call pack() which will prepare on-wire data.
  211. util::OutputBuffer buf(option_desc.option->getData().size());
  212. option_desc.option->pack(buf);
  213. if (extra_data) {
  214. // The length of the buffer must be at least equal to size of the
  215. // reference data but it can sometimes be greater than that. This is
  216. // because some options carry suboptions that increase the overall
  217. // length.
  218. ASSERT_GE(buf.getLength() - option_desc.option->getHeaderLen(),
  219. expected_data_len);
  220. } else {
  221. ASSERT_EQ(buf.getLength() - option_desc.option->getHeaderLen(),
  222. expected_data_len);
  223. }
  224. // Verify that the data is correct. Do not verify suboptions and a header.
  225. const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
  226. EXPECT_EQ(0, memcmp(expected_data, data + option_desc.option->getHeaderLen(),
  227. expected_data_len));
  228. }
  229. Dhcpv6Srv srv_;
  230. int rcode_;
  231. ConstElementPtr comment_;
  232. };
  233. // Goal of this test is a verification if a very simple config update
  234. // with just a bumped version number. That's the simplest possible
  235. // config update.
  236. TEST_F(Dhcp6ParserTest, version) {
  237. ConstElementPtr x;
  238. EXPECT_NO_THROW(x = configureDhcp6Server(srv_,
  239. Element::fromJSON("{\"version\": 0}")));
  240. // returned value must be 0 (configuration accepted)
  241. ASSERT_TRUE(x);
  242. comment_ = parseAnswer(rcode_, x);
  243. EXPECT_EQ(0, rcode_);
  244. }
  245. /// The goal of this test is to verify that the code accepts only
  246. /// valid commands and malformed or unsupported parameters are rejected.
  247. TEST_F(Dhcp6ParserTest, bogusCommand) {
  248. ConstElementPtr x;
  249. EXPECT_NO_THROW(x = configureDhcp6Server(srv_,
  250. Element::fromJSON("{\"bogus\": 5}")));
  251. // returned value must be 1 (configuration parse error)
  252. ASSERT_TRUE(x);
  253. comment_ = parseAnswer(rcode_, x);
  254. EXPECT_EQ(1, rcode_);
  255. }
  256. /// The goal of this test is to verify if wrongly defined subnet will
  257. /// be rejected. Properly defined subnet must include at least one
  258. /// pool definition.
  259. TEST_F(Dhcp6ParserTest, emptySubnet) {
  260. ConstElementPtr status;
  261. EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
  262. Element::fromJSON("{ \"interface\": [ \"all\" ],"
  263. "\"preferred-lifetime\": 3000,"
  264. "\"rebind-timer\": 2000, "
  265. "\"renew-timer\": 1000, "
  266. "\"subnet6\": [ ], "
  267. "\"valid-lifetime\": 4000 }")));
  268. // returned value should be 0 (success)
  269. ASSERT_TRUE(status);
  270. comment_ = parseAnswer(rcode_, status);
  271. EXPECT_EQ(0, rcode_);
  272. }
  273. /// The goal of this test is to verify if defined subnet uses global
  274. /// parameter timer definitions.
  275. TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
  276. ConstElementPtr status;
  277. string config = "{ \"interface\": [ \"all\" ],"
  278. "\"preferred-lifetime\": 3000,"
  279. "\"rebind-timer\": 2000, "
  280. "\"renew-timer\": 1000, "
  281. "\"subnet6\": [ { "
  282. " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
  283. " \"subnet\": \"2001:db8:1::/64\" } ],"
  284. "\"valid-lifetime\": 4000 }";
  285. cout << config << endl;
  286. ElementPtr json = Element::fromJSON(config);
  287. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  288. // check if returned status is OK
  289. ASSERT_TRUE(status);
  290. comment_ = parseAnswer(rcode_, status);
  291. EXPECT_EQ(0, rcode_);
  292. // Now check if the configuration was indeed handled and we have
  293. // expected pool configured.
  294. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  295. ASSERT_TRUE(subnet);
  296. EXPECT_EQ(1000, subnet->getT1());
  297. EXPECT_EQ(2000, subnet->getT2());
  298. EXPECT_EQ(3000, subnet->getPreferred());
  299. EXPECT_EQ(4000, subnet->getValid());
  300. }
  301. // This test checks if it is possible to override global values
  302. // on a per subnet basis.
  303. TEST_F(Dhcp6ParserTest, subnetLocal) {
  304. ConstElementPtr status;
  305. string config = "{ \"interface\": [ \"all\" ],"
  306. "\"preferred-lifetime\": 3000,"
  307. "\"rebind-timer\": 2000, "
  308. "\"renew-timer\": 1000, "
  309. "\"subnet6\": [ { "
  310. " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
  311. " \"renew-timer\": 1, "
  312. " \"rebind-timer\": 2, "
  313. " \"preferred-lifetime\": 3,"
  314. " \"valid-lifetime\": 4,"
  315. " \"subnet\": \"2001:db8:1::/64\" } ],"
  316. "\"valid-lifetime\": 4000 }";
  317. cout << config << endl;
  318. ElementPtr json = Element::fromJSON(config);
  319. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  320. // returned value should be 0 (configuration success)
  321. ASSERT_TRUE(status);
  322. comment_ = parseAnswer(rcode_, status);
  323. EXPECT_EQ(0, rcode_);
  324. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  325. ASSERT_TRUE(subnet);
  326. EXPECT_EQ(1, subnet->getT1());
  327. EXPECT_EQ(2, subnet->getT2());
  328. EXPECT_EQ(3, subnet->getPreferred());
  329. EXPECT_EQ(4, subnet->getValid());
  330. }
  331. // Test verifies that a subnet with pool values that do not belong to that
  332. // pool are rejected.
  333. TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
  334. ConstElementPtr status;
  335. string config = "{ \"interface\": [ \"all\" ],"
  336. "\"preferred-lifetime\": 3000,"
  337. "\"rebind-timer\": 2000, "
  338. "\"renew-timer\": 1000, "
  339. "\"subnet6\": [ { "
  340. " \"pool\": [ \"4001:db8:1::/80\" ],"
  341. " \"subnet\": \"2001:db8:1::/64\" } ],"
  342. "\"valid-lifetime\": 4000 }";
  343. cout << config << endl;
  344. ElementPtr json = Element::fromJSON(config);
  345. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  346. // returned value must be 1 (values error)
  347. // as the pool does not belong to that subnet
  348. ASSERT_TRUE(status);
  349. comment_ = parseAnswer(rcode_, status);
  350. EXPECT_EQ(1, rcode_);
  351. }
  352. // Goal of this test is to verify if pools can be defined
  353. // using prefix/length notation. There is no separate test for min-max
  354. // notation as it was tested in several previous tests.
  355. TEST_F(Dhcp6ParserTest, poolPrefixLen) {
  356. ConstElementPtr x;
  357. string config = "{ \"interface\": [ \"all\" ],"
  358. "\"preferred-lifetime\": 3000,"
  359. "\"rebind-timer\": 2000, "
  360. "\"renew-timer\": 1000, "
  361. "\"subnet6\": [ { "
  362. " \"pool\": [ \"2001:db8:1::/80\" ],"
  363. " \"subnet\": \"2001:db8:1::/64\" } ],"
  364. "\"valid-lifetime\": 4000 }";
  365. cout << config << endl;
  366. ElementPtr json = Element::fromJSON(config);
  367. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  368. // returned value must be 1 (configuration parse error)
  369. ASSERT_TRUE(x);
  370. comment_ = parseAnswer(rcode_, x);
  371. EXPECT_EQ(0, rcode_);
  372. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  373. ASSERT_TRUE(subnet);
  374. EXPECT_EQ(1000, subnet->getT1());
  375. EXPECT_EQ(2000, subnet->getT2());
  376. EXPECT_EQ(3000, subnet->getPreferred());
  377. EXPECT_EQ(4000, subnet->getValid());
  378. }
  379. // The goal of this test is to check whether an option definition
  380. // that defines an option carrying an IPv6 address can be created.
  381. TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
  382. // Configuration string.
  383. std::string config =
  384. "{ \"option-def\": [ {"
  385. " \"name\": \"foo\","
  386. " \"code\": 100,"
  387. " \"type\": \"ipv6-address\","
  388. " \"array\": False,"
  389. " \"record-types\": \"\","
  390. " \"space\": \"isc\""
  391. " } ]"
  392. "}";
  393. ElementPtr json = Element::fromJSON(config);
  394. // Make sure that the particular option definition does not exist.
  395. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  396. ASSERT_FALSE(def);
  397. // Use the configuration string to create new option definition.
  398. ConstElementPtr status;
  399. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  400. ASSERT_TRUE(status);
  401. // The option definition should now be available in the CfgMgr.
  402. def = CfgMgr::instance().getOptionDef("isc", 100);
  403. ASSERT_TRUE(def);
  404. // Verify that the option definition data is valid.
  405. EXPECT_EQ("foo", def->getName());
  406. EXPECT_EQ(100, def->getCode());
  407. EXPECT_FALSE(def->getArrayType());
  408. EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
  409. }
  410. // The goal of this test is to check whether an option definiiton
  411. // that defines an option carrying a record of data fields can
  412. // be created.
  413. TEST_F(Dhcp6ParserTest, optionDefRecord) {
  414. // Configuration string.
  415. std::string config =
  416. "{ \"option-def\": [ {"
  417. " \"name\": \"foo\","
  418. " \"code\": 100,"
  419. " \"type\": \"record\","
  420. " \"array\": False,"
  421. " \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
  422. " \"space\": \"isc\""
  423. " } ]"
  424. "}";
  425. ElementPtr json = Element::fromJSON(config);
  426. // Make sure that the particular option definition does not exist.
  427. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  428. ASSERT_FALSE(def);
  429. // Use the configuration string to create new option definition.
  430. ConstElementPtr status;
  431. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  432. ASSERT_TRUE(status);
  433. checkResult(status, 0);
  434. // The option definition should now be available in the CfgMgr.
  435. def = CfgMgr::instance().getOptionDef("isc", 100);
  436. ASSERT_TRUE(def);
  437. // Check the option data.
  438. EXPECT_EQ("foo", def->getName());
  439. EXPECT_EQ(100, def->getCode());
  440. EXPECT_EQ(OPT_RECORD_TYPE, def->getType());
  441. EXPECT_FALSE(def->getArrayType());
  442. // The option comprises the record of data fields. Verify that all
  443. // fields are present and they are of the expected types.
  444. const OptionDefinition::RecordFieldsCollection& record_fields =
  445. def->getRecordFields();
  446. ASSERT_EQ(4, record_fields.size());
  447. EXPECT_EQ(OPT_UINT16_TYPE, record_fields[0]);
  448. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, record_fields[1]);
  449. EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, record_fields[2]);
  450. EXPECT_EQ(OPT_STRING_TYPE, record_fields[3]);
  451. }
  452. // The goal of this test is to verify that multiple option definitions
  453. // can be created.
  454. TEST_F(Dhcp6ParserTest, optionDefMultiple) {
  455. // Configuration string.
  456. std::string config =
  457. "{ \"option-def\": [ {"
  458. " \"name\": \"foo\","
  459. " \"code\": 100,"
  460. " \"type\": \"uint32\","
  461. " \"array\": False,"
  462. " \"record-types\": \"\","
  463. " \"space\": \"isc\""
  464. " },"
  465. " {"
  466. " \"name\": \"foo-2\","
  467. " \"code\": 101,"
  468. " \"type\": \"ipv4-address\","
  469. " \"array\": False,"
  470. " \"record-types\": \"\","
  471. " \"space\": \"isc\""
  472. " } ]"
  473. "}";
  474. ElementPtr json = Element::fromJSON(config);
  475. // Make sure that the option definitions do not exist yet.
  476. ASSERT_FALSE(CfgMgr::instance().getOptionDef("isc", 100));
  477. ASSERT_FALSE(CfgMgr::instance().getOptionDef("isc", 101));
  478. // Use the configuration string to create new option definitions.
  479. ConstElementPtr status;
  480. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  481. ASSERT_TRUE(status);
  482. checkResult(status, 0);
  483. // Check the first definition we have created.
  484. OptionDefinitionPtr def1 = CfgMgr::instance().getOptionDef("isc", 100);
  485. ASSERT_TRUE(def1);
  486. // Check the option data.
  487. EXPECT_EQ("foo", def1->getName());
  488. EXPECT_EQ(100, def1->getCode());
  489. EXPECT_EQ(OPT_UINT32_TYPE, def1->getType());
  490. EXPECT_FALSE(def1->getArrayType());
  491. // Check the second option definition we have created.
  492. OptionDefinitionPtr def2 = CfgMgr::instance().getOptionDef("isc", 101);
  493. ASSERT_TRUE(def2);
  494. // Check the option data.
  495. EXPECT_EQ("foo-2", def2->getName());
  496. EXPECT_EQ(101, def2->getCode());
  497. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def2->getType());
  498. EXPECT_FALSE(def2->getArrayType());
  499. }
  500. // The goal of this test is to verify that the duplicated option
  501. // definition is not accepted.
  502. TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
  503. // Configuration string. Both option definitions have
  504. // the same code and belong to the same option space.
  505. // This configuration should not be accepted.
  506. std::string config =
  507. "{ \"option-def\": [ {"
  508. " \"name\": \"foo\","
  509. " \"code\": 100,"
  510. " \"type\": \"uint32\","
  511. " \"array\": False,"
  512. " \"record-types\": \"\","
  513. " \"space\": \"isc\""
  514. " },"
  515. " {"
  516. " \"name\": \"foo-2\","
  517. " \"code\": 100,"
  518. " \"type\": \"ipv4-address\","
  519. " \"array\": False,"
  520. " \"record-types\": \"\","
  521. " \"space\": \"isc\""
  522. " } ]"
  523. "}";
  524. ElementPtr json = Element::fromJSON(config);
  525. // Make sure that the option definition does not exist yet.
  526. ASSERT_FALSE(CfgMgr::instance().getOptionDef("isc", 100));
  527. // Use the configuration string to create new option definitions.
  528. ConstElementPtr status;
  529. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  530. ASSERT_TRUE(status);
  531. checkResult(status, 1);
  532. }
  533. // The goal of this test is to verify that the option definition
  534. // comprising an array of uint32 values can be created.
  535. TEST_F(Dhcp6ParserTest, optionDefArray) {
  536. // Configuration string. Created option definition should
  537. // comprise an array of uint32 values.
  538. std::string config =
  539. "{ \"option-def\": [ {"
  540. " \"name\": \"foo\","
  541. " \"code\": 100,"
  542. " \"type\": \"uint32\","
  543. " \"array\": True,"
  544. " \"record-types\": \"\","
  545. " \"space\": \"isc\""
  546. " } ]"
  547. "}";
  548. ElementPtr json = Element::fromJSON(config);
  549. // Make sure that the particular option definition does not exist.
  550. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
  551. ASSERT_FALSE(def);
  552. // Use the configuration string to create new option definition.
  553. ConstElementPtr status;
  554. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  555. ASSERT_TRUE(status);
  556. checkResult(status, 0);
  557. // The option definition should now be available in the CfgMgr.
  558. def = CfgMgr::instance().getOptionDef("isc", 100);
  559. ASSERT_TRUE(def);
  560. // Check the option data.
  561. EXPECT_EQ("foo", def->getName());
  562. EXPECT_EQ(100, def->getCode());
  563. EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
  564. EXPECT_TRUE(def->getArrayType());
  565. }
  566. /// The purpose of this test is to verify that the option definition
  567. /// with invalid name is not accepted.
  568. TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
  569. // Configuration string. The option name is invalid as it
  570. // contains the % character.
  571. std::string config =
  572. "{ \"option-def\": [ {"
  573. " \"name\": \"invalid%name\","
  574. " \"code\": 100,"
  575. " \"type\": \"string\","
  576. " \"array\": False,"
  577. " \"record-types\": \"\","
  578. " \"space\": \"isc\""
  579. " } ]"
  580. "}";
  581. ElementPtr json = Element::fromJSON(config);
  582. // Use the configuration string to create new option definition.
  583. ConstElementPtr status;
  584. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  585. ASSERT_TRUE(status);
  586. // Expecting parsing error (error code 1).
  587. checkResult(status, 1);
  588. }
  589. /// The purpose of this test is to verify that the option definition
  590. /// with invalid type is not accepted.
  591. TEST_F(Dhcp6ParserTest, optionDefInvalidType) {
  592. // Configuration string. The option type is invalid. It is
  593. // "sting" instead of "string".
  594. std::string config =
  595. "{ \"option-def\": [ {"
  596. " \"name\": \"foo\","
  597. " \"code\": 100,"
  598. " \"type\": \"sting\","
  599. " \"array\": False,"
  600. " \"record-types\": \"\","
  601. " \"space\": \"isc\""
  602. " } ]"
  603. "}";
  604. ElementPtr json = Element::fromJSON(config);
  605. // Use the configuration string to create new option definition.
  606. ConstElementPtr status;
  607. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  608. ASSERT_TRUE(status);
  609. // Expecting parsing error (error code 1).
  610. checkResult(status, 1);
  611. }
  612. /// The purpose of this test is to verify that the option definition
  613. /// with invalid type is not accepted.
  614. TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
  615. // Configuration string. The third of the record fields
  616. // is invalid. It is "sting" instead of "string".
  617. std::string config =
  618. "{ \"option-def\": [ {"
  619. " \"name\": \"foo\","
  620. " \"code\": 100,"
  621. " \"type\": \"record\","
  622. " \"array\": False,"
  623. " \"record-types\": \"uint32,uint8,sting\","
  624. " \"space\": \"isc\""
  625. " } ]"
  626. "}";
  627. ElementPtr json = Element::fromJSON(config);
  628. // Use the configuration string to create new option definition.
  629. ConstElementPtr status;
  630. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  631. ASSERT_TRUE(status);
  632. // Expecting parsing error (error code 1).
  633. checkResult(status, 1);
  634. }
  635. /// The purpose of this test is to verify that it is not allowed
  636. /// to override the standard option (that belongs to dhcp4 option
  637. /// space) and that it is allowed to define option in the dhcp4
  638. /// option space that has a code which is not used by any of the
  639. /// standard options.
  640. TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
  641. // Configuration string. The option code 100 is unassigned
  642. // so it can be used for a custom option definition in
  643. // dhcp6 option space.
  644. std::string config =
  645. "{ \"option-def\": [ {"
  646. " \"name\": \"foo\","
  647. " \"code\": 100,"
  648. " \"type\": \"string\","
  649. " \"array\": False,"
  650. " \"record-types\": \"\","
  651. " \"space\": \"dhcp6\""
  652. " } ]"
  653. "}";
  654. ElementPtr json = Element::fromJSON(config);
  655. OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("dhcp6", 100);
  656. ASSERT_FALSE(def);
  657. // Use the configuration string to create new option definition.
  658. ConstElementPtr status;
  659. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  660. ASSERT_TRUE(status);
  661. checkResult(status, 0);
  662. // The option definition should now be available in the CfgMgr.
  663. def = CfgMgr::instance().getOptionDef("dhcp6", 100);
  664. ASSERT_TRUE(def);
  665. // Check the option data.
  666. EXPECT_EQ("foo", def->getName());
  667. EXPECT_EQ(100, def->getCode());
  668. EXPECT_EQ(OPT_STRING_TYPE, def->getType());
  669. EXPECT_FALSE(def->getArrayType());
  670. // The combination of option space and code is
  671. // invalid. The 'dhcp6' option space groups
  672. // standard options and the code 3 is reserved
  673. // for one of them.
  674. config =
  675. "{ \"option-def\": [ {"
  676. " \"name\": \"foo\","
  677. " \"code\": 3,"
  678. " \"type\": \"string\","
  679. " \"array\": False,"
  680. " \"record-types\": \"\","
  681. " \"space\": \"dhcp6\""
  682. " } ]"
  683. "}";
  684. json = Element::fromJSON(config);
  685. // Use the configuration string to create new option definition.
  686. EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
  687. ASSERT_TRUE(status);
  688. // Expecting parsing error (error code 1).
  689. checkResult(status, 1);
  690. }
  691. // Goal of this test is to verify that global option
  692. // data is configured for the subnet if the subnet
  693. // configuration does not include options configuration.
  694. TEST_F(Dhcp6ParserTest, optionDataDefaults) {
  695. ConstElementPtr x;
  696. string config = "{ \"interface\": [ \"all\" ],"
  697. "\"preferred-lifetime\": 3000,"
  698. "\"rebind-timer\": 2000,"
  699. "\"renew-timer\": 1000,"
  700. "\"option-data\": [ {"
  701. " \"name\": \"option_foo\","
  702. " \"code\": 100,"
  703. " \"data\": \"AB CDEF0105\","
  704. " \"csv-format\": False"
  705. " },"
  706. " {"
  707. " \"name\": \"option_foo2\","
  708. " \"code\": 101,"
  709. " \"data\": \"01\","
  710. " \"csv-format\": False"
  711. " } ],"
  712. "\"subnet6\": [ { "
  713. " \"pool\": [ \"2001:db8:1::/80\" ],"
  714. " \"subnet\": \"2001:db8:1::/64\""
  715. " } ],"
  716. "\"valid-lifetime\": 4000 }";
  717. ElementPtr json = Element::fromJSON(config);
  718. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  719. ASSERT_TRUE(x);
  720. comment_ = parseAnswer(rcode_, x);
  721. ASSERT_EQ(0, rcode_);
  722. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  723. ASSERT_TRUE(subnet);
  724. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
  725. ASSERT_EQ(2, options->size());
  726. // Get the search index. Index #1 is to search using option code.
  727. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  728. // Get the options for specified index. Expecting one option to be
  729. // returned but in theory we may have multiple options with the same
  730. // code so we get the range.
  731. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  732. Subnet::OptionContainerTypeIndex::const_iterator> range =
  733. idx.equal_range(100);
  734. // Expect single option with the code equal to 100.
  735. ASSERT_EQ(1, std::distance(range.first, range.second));
  736. const uint8_t foo_expected[] = {
  737. 0xAB, 0xCD, 0xEF, 0x01, 0x05
  738. };
  739. // Check if option is valid in terms of code and carried data.
  740. testOption(*range.first, 100, foo_expected, sizeof(foo_expected));
  741. range = idx.equal_range(101);
  742. ASSERT_EQ(1, std::distance(range.first, range.second));
  743. // Do another round of testing with second option.
  744. const uint8_t foo2_expected[] = {
  745. 0x01
  746. };
  747. testOption(*range.first, 101, foo2_expected, sizeof(foo2_expected));
  748. // Check that options with other option codes are not returned.
  749. for (uint16_t code = 102; code < 110; ++code) {
  750. range = idx.equal_range(code);
  751. EXPECT_EQ(0, std::distance(range.first, range.second));
  752. }
  753. }
  754. // Goal of this test is to verify options configuration
  755. // for a single subnet. In particular this test checks
  756. // that local options configuration overrides global
  757. // option setting.
  758. TEST_F(Dhcp6ParserTest, optionDataInSingleSubnet) {
  759. ConstElementPtr x;
  760. string config = "{ \"interface\": [ \"all\" ],"
  761. "\"preferred-lifetime\": 3000,"
  762. "\"rebind-timer\": 2000, "
  763. "\"renew-timer\": 1000, "
  764. "\"option-data\": [ {"
  765. " \"name\": \"option_foo\","
  766. " \"code\": 100,"
  767. " \"data\": \"AB\","
  768. " \"csv-format\": False"
  769. " } ],"
  770. "\"subnet6\": [ { "
  771. " \"pool\": [ \"2001:db8:1::/80\" ],"
  772. " \"subnet\": \"2001:db8:1::/64\", "
  773. " \"option-data\": [ {"
  774. " \"name\": \"option_foo\","
  775. " \"code\": 100,"
  776. " \"data\": \"AB CDEF0105\","
  777. " \"csv-format\": False"
  778. " },"
  779. " {"
  780. " \"name\": \"option_foo2\","
  781. " \"code\": 101,"
  782. " \"data\": \"01\","
  783. " \"csv-format\": False"
  784. " } ]"
  785. " } ],"
  786. "\"valid-lifetime\": 4000 }";
  787. ElementPtr json = Element::fromJSON(config);
  788. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  789. ASSERT_TRUE(x);
  790. comment_ = parseAnswer(rcode_, x);
  791. ASSERT_EQ(0, rcode_);
  792. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  793. ASSERT_TRUE(subnet);
  794. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
  795. ASSERT_EQ(2, options->size());
  796. // Get the search index. Index #1 is to search using option code.
  797. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  798. // Get the options for specified index. Expecting one option to be
  799. // returned but in theory we may have multiple options with the same
  800. // code so we get the range.
  801. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  802. Subnet::OptionContainerTypeIndex::const_iterator> range =
  803. idx.equal_range(100);
  804. // Expect single option with the code equal to 100.
  805. ASSERT_EQ(1, std::distance(range.first, range.second));
  806. const uint8_t foo_expected[] = {
  807. 0xAB, 0xCD, 0xEF, 0x01, 0x05
  808. };
  809. // Check if option is valid in terms of code and carried data.
  810. testOption(*range.first, 100, foo_expected, sizeof(foo_expected));
  811. range = idx.equal_range(101);
  812. ASSERT_EQ(1, std::distance(range.first, range.second));
  813. // Do another round of testing with second option.
  814. const uint8_t foo2_expected[] = {
  815. 0x01
  816. };
  817. testOption(*range.first, 101, foo2_expected, sizeof(foo2_expected));
  818. }
  819. // Goal of this test is to verify options configuration
  820. // for multiple subnets.
  821. TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
  822. ConstElementPtr x;
  823. string config = "{ \"interface\": [ \"all\" ],"
  824. "\"preferred-lifetime\": 3000,"
  825. "\"rebind-timer\": 2000, "
  826. "\"renew-timer\": 1000, "
  827. "\"subnet6\": [ { "
  828. " \"pool\": [ \"2001:db8:1::/80\" ],"
  829. " \"subnet\": \"2001:db8:1::/64\", "
  830. " \"option-data\": [ {"
  831. " \"name\": \"option_foo\","
  832. " \"code\": 100,"
  833. " \"data\": \"0102030405060708090A\","
  834. " \"csv-format\": False"
  835. " } ]"
  836. " },"
  837. " {"
  838. " \"pool\": [ \"2001:db8:2::/80\" ],"
  839. " \"subnet\": \"2001:db8:2::/64\", "
  840. " \"option-data\": [ {"
  841. " \"name\": \"option_foo2\","
  842. " \"code\": 101,"
  843. " \"data\": \"FFFEFDFCFB\","
  844. " \"csv-format\": False"
  845. " } ]"
  846. " } ],"
  847. "\"valid-lifetime\": 4000 }";
  848. ElementPtr json = Element::fromJSON(config);
  849. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  850. ASSERT_TRUE(x);
  851. comment_ = parseAnswer(rcode_, x);
  852. ASSERT_EQ(0, rcode_);
  853. Subnet6Ptr subnet1 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  854. ASSERT_TRUE(subnet1);
  855. Subnet::OptionContainerPtr options1 = subnet1->getOptionDescriptors("dhcp6");
  856. ASSERT_EQ(1, options1->size());
  857. // Get the search index. Index #1 is to search using option code.
  858. const Subnet::OptionContainerTypeIndex& idx1 = options1->get<1>();
  859. // Get the options for specified index. Expecting one option to be
  860. // returned but in theory we may have multiple options with the same
  861. // code so we get the range.
  862. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  863. Subnet::OptionContainerTypeIndex::const_iterator> range1 =
  864. idx1.equal_range(100);
  865. // Expect single option with the code equal to 100.
  866. ASSERT_EQ(1, std::distance(range1.first, range1.second));
  867. const uint8_t foo_expected[] = {
  868. 0x01, 0x02, 0x03, 0x04, 0x05,
  869. 0x06, 0x07, 0x08, 0x09, 0x0A
  870. };
  871. // Check if option is valid in terms of code and carried data.
  872. testOption(*range1.first, 100, foo_expected, sizeof(foo_expected));
  873. // Test another subnet in the same way.
  874. Subnet6Ptr subnet2 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:2::4"));
  875. ASSERT_TRUE(subnet2);
  876. Subnet::OptionContainerPtr options2 = subnet2->getOptionDescriptors("dhcp6");
  877. ASSERT_EQ(1, options2->size());
  878. const Subnet::OptionContainerTypeIndex& idx2 = options2->get<1>();
  879. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  880. Subnet::OptionContainerTypeIndex::const_iterator> range2 =
  881. idx2.equal_range(101);
  882. ASSERT_EQ(1, std::distance(range2.first, range2.second));
  883. const uint8_t foo2_expected[] = {
  884. 0xFF, 0xFE, 0xFD, 0xFC, 0xFB
  885. };
  886. testOption(*range2.first, 101, foo2_expected, sizeof(foo2_expected));
  887. }
  888. // Verify that empty option name is rejected in the configuration.
  889. TEST_F(Dhcp6ParserTest, optionNameEmpty) {
  890. // Empty option names not allowed.
  891. testInvalidOptionParam("", "name");
  892. }
  893. // Verify that empty option name with spaces is rejected
  894. // in the configuration.
  895. TEST_F(Dhcp6ParserTest, optionNameSpaces) {
  896. // Spaces in option names not allowed.
  897. testInvalidOptionParam("option foo", "name");
  898. }
  899. // Verify that negative option code is rejected in the configuration.
  900. TEST_F(Dhcp6ParserTest, optionCodeNegative) {
  901. // Check negative option code -4. This should fail too.
  902. testInvalidOptionParam("-4", "code");
  903. }
  904. // Verify that out of bounds option code is rejected in the configuration.
  905. TEST_F(Dhcp6ParserTest, optionCodeNonUint16) {
  906. // The valid option codes are uint16_t values so passing
  907. // uint16_t maximum value incremented by 1 should result
  908. // in failure.
  909. testInvalidOptionParam("65536", "code");
  910. }
  911. // Verify that out of bounds option code is rejected in the configuration.
  912. TEST_F(Dhcp6ParserTest, optionCodeHighNonUint16) {
  913. // Another check for uint16_t overflow but this time
  914. // let's pass even greater option code value.
  915. testInvalidOptionParam("70000", "code");
  916. }
  917. // Verify that zero option code is rejected in the configuration.
  918. TEST_F(Dhcp6ParserTest, optionCodeZero) {
  919. // Option code 0 is reserved and should not be accepted
  920. // by configuration parser.
  921. testInvalidOptionParam("0", "code");
  922. }
  923. // Verify that option data which contains non hexadecimal characters
  924. // is rejected by the configuration.
  925. TEST_F(Dhcp6ParserTest, optionDataInvalidChar) {
  926. // Option code 0 is reserved and should not be accepted
  927. // by configuration parser.
  928. testInvalidOptionParam("01020R", "data");
  929. }
  930. // Verify that option data containins '0x' prefix is rejected
  931. // by the configuration.
  932. TEST_F(Dhcp6ParserTest, optionDataUnexpectedPrefix) {
  933. // Option code 0 is reserved and should not be accepted
  934. // by configuration parser.
  935. testInvalidOptionParam("0x0102", "data");
  936. }
  937. // Verify that option data consisting od an odd number of
  938. // hexadecimal digits is rejected in the configuration.
  939. TEST_F(Dhcp6ParserTest, optionDataOddLength) {
  940. // Option code 0 is reserved and should not be accepted
  941. // by configuration parser.
  942. testInvalidOptionParam("123", "data");
  943. }
  944. // Verify that either lower or upper case characters are allowed
  945. // to specify the option data.
  946. TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
  947. ConstElementPtr x;
  948. std::string config = createConfigWithOption("0a0b0C0D", "data");
  949. ElementPtr json = Element::fromJSON(config);
  950. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  951. ASSERT_TRUE(x);
  952. comment_ = parseAnswer(rcode_, x);
  953. ASSERT_EQ(0, rcode_);
  954. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  955. ASSERT_TRUE(subnet);
  956. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
  957. ASSERT_EQ(1, options->size());
  958. // Get the search index. Index #1 is to search using option code.
  959. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  960. // Get the options for specified index. Expecting one option to be
  961. // returned but in theory we may have multiple options with the same
  962. // code so we get the range.
  963. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  964. Subnet::OptionContainerTypeIndex::const_iterator> range =
  965. idx.equal_range(80);
  966. // Expect single option with the code equal to 100.
  967. ASSERT_EQ(1, std::distance(range.first, range.second));
  968. const uint8_t foo_expected[] = {
  969. 0x0A, 0x0B, 0x0C, 0x0D
  970. };
  971. // Check if option is valid in terms of code and carried data.
  972. testOption(*range.first, 80, foo_expected, sizeof(foo_expected));
  973. }
  974. // Verify that specific option object is returned for standard
  975. // option which has dedicated option class derived from Option.
  976. TEST_F(Dhcp6ParserTest, stdOptionData) {
  977. ConstElementPtr x;
  978. std::map<std::string, std::string> params;
  979. params["name"] = "OPTION_IA_NA";
  980. // Option code 3 means OPTION_IA_NA.
  981. params["code"] = "3";
  982. params["data"] = "12345, 6789, 1516";
  983. params["csv-format"] = "True";
  984. std::string config = createConfigWithOption(params);
  985. ElementPtr json = Element::fromJSON(config);
  986. EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
  987. ASSERT_TRUE(x);
  988. comment_ = parseAnswer(rcode_, x);
  989. ASSERT_EQ(0, rcode_);
  990. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
  991. ASSERT_TRUE(subnet);
  992. Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
  993. ASSERT_EQ(1, options->size());
  994. // Get the search index. Index #1 is to search using option code.
  995. const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  996. // Get the options for specified index. Expecting one option to be
  997. // returned but in theory we may have multiple options with the same
  998. // code so we get the range.
  999. std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
  1000. Subnet::OptionContainerTypeIndex::const_iterator> range =
  1001. idx.equal_range(D6O_IA_NA);
  1002. // Expect single option with the code equal to IA_NA option code.
  1003. ASSERT_EQ(1, std::distance(range.first, range.second));
  1004. // The actual pointer to the option is held in the option field
  1005. // in the structure returned.
  1006. OptionPtr option = range.first->option;
  1007. ASSERT_TRUE(option);
  1008. // Option object returned for here is expected to be Option6IA
  1009. // which is derived from Option. This class is dedicated to
  1010. // represent standard option IA_NA.
  1011. boost::shared_ptr<Option6IA> optionIA =
  1012. boost::dynamic_pointer_cast<Option6IA>(option);
  1013. // If cast is unsuccessful than option returned was of a
  1014. // differnt type than Option6IA. This is wrong.
  1015. ASSERT_TRUE(optionIA);
  1016. // If cast was successful we may use accessors exposed by
  1017. // Option6IA to validate that the content of this option
  1018. // has been set correctly.
  1019. EXPECT_EQ(12345, optionIA->getIAID());
  1020. EXPECT_EQ(6789, optionIA->getT1());
  1021. EXPECT_EQ(1516, optionIA->getT2());
  1022. }
  1023. };