cfg_option_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. // Copyright (C) 2014 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/option.h>
  16. #include <dhcp/option_int.h>
  17. #include <dhcp/option_space.h>
  18. #include <dhcpsrv/cfg_option.h>
  19. #include <boost/pointer_cast.hpp>
  20. #include <gtest/gtest.h>
  21. using namespace isc;
  22. using namespace isc::dhcp;
  23. namespace {
  24. // This test verifies that the option configurations can be compared.
  25. TEST(CfgOptionTest, equals) {
  26. CfgOption cfg1;
  27. CfgOption cfg2;
  28. // Initially the configurations should be equal.
  29. ASSERT_TRUE(cfg1 == cfg2);
  30. ASSERT_FALSE(cfg1 != cfg2);
  31. // Add 9 options to two different option spaces. Each option have different
  32. // option code and content.
  33. for (uint16_t code = 1; code < 10; ++code) {
  34. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, code)));
  35. ASSERT_NO_THROW(cfg1.add(option, false, "isc"));
  36. ASSERT_NO_THROW(cfg1.add(option, true, "vendor-123"));
  37. }
  38. // Configurations should now be different.
  39. ASSERT_FALSE(cfg1 == cfg2);
  40. ASSERT_TRUE(cfg1 != cfg2);
  41. // Add 8 options (excluding the option with code 1) to the same option
  42. // spaces.
  43. for (uint16_t code = 2; code < 10; ++code) {
  44. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, code)));
  45. ASSERT_NO_THROW(cfg2.add(option, false, "isc"));
  46. ASSERT_NO_THROW(cfg2.add(option, true, "vendor-123"));
  47. }
  48. // Configurations should still be unequal.
  49. ASSERT_FALSE(cfg1 == cfg2);
  50. ASSERT_TRUE(cfg1 != cfg2);
  51. // Add missing option to the option space isc.
  52. ASSERT_NO_THROW(cfg2.add(OptionPtr(new Option(Option::V6, 1,
  53. OptionBuffer(10, 0x01))),
  54. false, "isc"));
  55. // Configurations should still be unequal because option with code 1
  56. // is missing in the option space vendor-123.
  57. ASSERT_FALSE(cfg1 == cfg2);
  58. ASSERT_TRUE(cfg1 != cfg2);
  59. // Add missing option.
  60. ASSERT_NO_THROW(cfg2.add(OptionPtr(new Option(Option::V6, 1,
  61. OptionBuffer(10, 0x01))),
  62. true, "vendor-123"));
  63. // Configurations should now be equal.
  64. ASSERT_TRUE(cfg1 == cfg2);
  65. ASSERT_FALSE(cfg1 != cfg2);
  66. }
  67. // This test verifies that multiple options can be added to the configuration
  68. // and that they can be retrieved using the option space name.
  69. TEST(CfgOptionTest, add) {
  70. CfgOption cfg;
  71. // Differentiate options by their codes (100-109)
  72. for (uint16_t code = 100; code < 110; ++code) {
  73. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  74. ASSERT_NO_THROW(cfg.add(option, false, "dhcp6"));
  75. }
  76. // Add 7 options to another option space. The option codes partially overlap
  77. // with option codes that we have added to dhcp6 option space.
  78. for (uint16_t code = 105; code < 112; ++code) {
  79. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  80. ASSERT_NO_THROW(cfg.add(option, false, "isc"));
  81. }
  82. // Get options from the Subnet and check if all 10 are there.
  83. OptionContainerPtr options = cfg.getAll("dhcp6");
  84. ASSERT_TRUE(options);
  85. ASSERT_EQ(10, options->size());
  86. // Validate codes of options added to dhcp6 option space.
  87. uint16_t expected_code = 100;
  88. for (OptionContainer::const_iterator option_desc = options->begin();
  89. option_desc != options->end(); ++option_desc) {
  90. ASSERT_TRUE(option_desc->option);
  91. EXPECT_EQ(expected_code, option_desc->option->getType());
  92. ++expected_code;
  93. }
  94. options = cfg.getAll("isc");
  95. ASSERT_TRUE(options);
  96. ASSERT_EQ(7, options->size());
  97. // Validate codes of options added to isc option space.
  98. expected_code = 105;
  99. for (OptionContainer::const_iterator option_desc = options->begin();
  100. option_desc != options->end(); ++option_desc) {
  101. ASSERT_TRUE(option_desc->option);
  102. EXPECT_EQ(expected_code, option_desc->option->getType());
  103. ++expected_code;
  104. }
  105. // Try to get options from a non-existing option space.
  106. options = cfg.getAll("abcd");
  107. ASSERT_TRUE(options);
  108. EXPECT_TRUE(options->empty());
  109. }
  110. // This test verifies that two option configurations can be merged.
  111. TEST(CfgOption, merge) {
  112. CfgOption cfg_src;
  113. CfgOption cfg_dst;
  114. // Create collection of options in option space dhcp6, with option codes
  115. // from the range of 100 to 109 and holding one byte of data equal to 0xFF.
  116. for (uint16_t code = 100; code < 110; ++code) {
  117. OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
  118. ASSERT_NO_THROW(cfg_src.add(option, false, "dhcp6"));
  119. }
  120. // Create collection of options in vendor space 123, with option codes
  121. // from the range of 100 to 109 and holding one byte of data equal to 0xFF.
  122. for (uint16_t code = 100; code < 110; code += 2) {
  123. OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
  124. ASSERT_NO_THROW(cfg_src.add(option, false, "vendor-123"));
  125. }
  126. // Create destination configuration (configuration that we merge the
  127. // other configuration to).
  128. // Create collection of options having even option codes in the range of
  129. // 100 to 108.
  130. for (uint16_t code = 100; code < 110; code += 2) {
  131. OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
  132. ASSERT_NO_THROW(cfg_dst.add(option, false, "dhcp6"));
  133. }
  134. // Create collection of options having odd option codes in the range of
  135. // 101 to 109.
  136. for (uint16_t code = 101; code < 110; code += 2) {
  137. OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
  138. ASSERT_NO_THROW(cfg_dst.add(option, false, "vendor-123"));
  139. }
  140. // Merge source configuration to the destination configuration. The options
  141. // in the destination should be preserved. The options from the source
  142. // configuration should be added.
  143. ASSERT_NO_THROW(cfg_src.merge(cfg_dst));
  144. // Validate the options in the dhcp6 option space in the destination.
  145. for (uint16_t code = 100; code < 110; ++code) {
  146. OptionDescriptor desc = cfg_dst.get("dhcp6", code);
  147. ASSERT_TRUE(desc.option);
  148. ASSERT_EQ(1, desc.option->getData().size());
  149. // The options with even option codes should hold one byte of data
  150. // equal to 0x1. These are the ones that we have initially added to
  151. // the destination configuration. The other options should hold the
  152. // values of 0xFF which indicates that they have been merged from the
  153. // source configuration.
  154. if ((code % 2) == 0) {
  155. EXPECT_EQ(0x01, desc.option->getData()[0]);
  156. } else {
  157. EXPECT_EQ(0xFF, desc.option->getData()[0]);
  158. }
  159. }
  160. // Validate the options in the vendor space.
  161. for (uint16_t code = 100; code < 110; ++code) {
  162. OptionDescriptor desc = cfg_dst.get(123, code);
  163. ASSERT_TRUE(desc.option);
  164. ASSERT_EQ(1, desc.option->getData().size());
  165. // This time, the options with even option codes should hold a byte
  166. // of data equal to 0xFF. The other options should hold the byte of
  167. // data equal to 0x01.
  168. if ((code % 2) == 0) {
  169. EXPECT_EQ(0xFF, desc.option->getData()[0]);
  170. } else {
  171. EXPECT_EQ(0x01, desc.option->getData()[0]);
  172. }
  173. }
  174. }
  175. // This test verifies that the options configuration can be copied between
  176. // objects.
  177. TEST(CfgOptionTest, copy) {
  178. CfgOption cfg_src;
  179. // Add 10 options to the custom option space in the source configuration.
  180. for (uint16_t code = 100; code < 110; ++code) {
  181. OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
  182. ASSERT_NO_THROW(cfg_src.add(option, false, "foo"));
  183. }
  184. CfgOption cfg_dst;
  185. // Add 20 options to the custom option space in destination configuration.
  186. for (uint16_t code = 100; code < 120; ++code) {
  187. OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
  188. ASSERT_NO_THROW(cfg_dst.add(option, false, "isc"));
  189. }
  190. // Copy entire configuration to the destination. This should override any
  191. // existing data.
  192. ASSERT_NO_THROW(cfg_src.copy(cfg_dst));
  193. // Validate options in the destination configuration.
  194. for (uint16_t code = 100; code < 110; ++code) {
  195. OptionDescriptor desc = cfg_dst.get("foo", code);
  196. ASSERT_TRUE(desc.option);
  197. ASSERT_EQ(1, desc.option->getData().size());
  198. EXPECT_EQ(0x01, desc.option->getData()[0]);
  199. }
  200. // Any existing options should be removed.
  201. OptionContainerPtr container = cfg_dst.getAll("isc");
  202. ASSERT_TRUE(container);
  203. EXPECT_TRUE(container->empty());
  204. // The option space "foo" should contain exactly 10 options.
  205. container = cfg_dst.getAll("foo");
  206. ASSERT_TRUE(container);
  207. EXPECT_EQ(10, container->size());
  208. }
  209. // This test verifies that encapsulated options are added as sub-options
  210. // to the top level options on request.
  211. TEST(CfgOptionTest, encapsulate) {
  212. CfgOption cfg;
  213. // Create top-level options. These options encapsulate "foo" option space.
  214. for (uint16_t code = 1000; code < 1020; ++code) {
  215. OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
  216. code, 1234));
  217. option->setEncapsulatedSpace("foo");
  218. ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
  219. }
  220. // Create sub-options belonging to "foo" option space.
  221. for (uint16_t code = 1; code < 20; ++code) {
  222. OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code,
  223. 0x01));
  224. ASSERT_NO_THROW(cfg.add(option, false, "foo"));
  225. }
  226. // Append options from "foo" space as sub-options.
  227. ASSERT_NO_THROW(cfg.encapsulate());
  228. // Verify that we have 20 top-level options.
  229. OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
  230. ASSERT_EQ(20, options->size());
  231. // Verify that each of them contains expected sub-options.
  232. for (uint16_t code = 1000; code < 1020; ++code) {
  233. OptionUint16Ptr option = boost::dynamic_pointer_cast<
  234. OptionUint16>(cfg.get(DHCP6_OPTION_SPACE, code).option);
  235. ASSERT_TRUE(option) << "option with code " << code << " not found";
  236. EXPECT_EQ(1234, option->getValue());
  237. for (uint16_t subcode = 1; subcode < 20; ++subcode) {
  238. OptionUint8Ptr suboption = boost::dynamic_pointer_cast<
  239. OptionUint8>(option->getOption(subcode));
  240. ASSERT_TRUE(suboption) << "suboption with code " << subcode
  241. << " not found";
  242. EXPECT_EQ(0x01, suboption->getValue());
  243. }
  244. }
  245. }
  246. // This test verifies that single option can be retrieved from the configuration
  247. // using option code and option space.
  248. TEST(CfgOption, get) {
  249. CfgOption cfg;
  250. // Add 10 options to a "dhcp6" option space in the subnet.
  251. for (uint16_t code = 100; code < 110; ++code) {
  252. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  253. ASSERT_NO_THROW(cfg.add(option, false, "dhcp6"));
  254. }
  255. // Check that we can get each added option descriptor using
  256. // individually.
  257. for (uint16_t code = 100; code < 110; ++code) {
  258. std::ostringstream stream;
  259. // First, try the invalid option space name.
  260. OptionDescriptor desc = cfg.get("isc", code);
  261. // Returned descriptor should contain NULL option ptr.
  262. EXPECT_FALSE(desc.option);
  263. // Now, try the valid option space.
  264. desc = cfg.get("dhcp6", code);
  265. // Test that the option code matches the expected code.
  266. ASSERT_TRUE(desc.option);
  267. EXPECT_EQ(code, desc.option->getType());
  268. }
  269. }
  270. // This test verifies that the same options can be added to the configuration
  271. // under different option space.
  272. TEST(CfgOptionTest, addNonUniqueOptions) {
  273. CfgOption cfg;
  274. // Create a set of options with non-unique codes.
  275. for (int i = 0; i < 2; ++i) {
  276. // In the inner loop we create options with unique codes (100-109).
  277. for (uint16_t code = 100; code < 110; ++code) {
  278. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  279. ASSERT_NO_THROW(cfg.add(option, false, "dhcp6"));
  280. }
  281. }
  282. // Sanity check that all options are there.
  283. OptionContainerPtr options = cfg.getAll("dhcp6");
  284. ASSERT_EQ(20, options->size());
  285. // Use container index #1 to get the options by their codes.
  286. OptionContainerTypeIndex& idx = options->get<1>();
  287. // Look for the codes 100-109.
  288. for (uint16_t code = 100; code < 110; ++ code) {
  289. // For each code we should get two instances of options->
  290. std::pair<OptionContainerTypeIndex::const_iterator,
  291. OptionContainerTypeIndex::const_iterator> range =
  292. idx.equal_range(code);
  293. // Distance between iterators indicates how many options
  294. // have been retured for the particular code.
  295. ASSERT_EQ(2, distance(range.first, range.second));
  296. // Check that returned options actually have the expected option code.
  297. for (OptionContainerTypeIndex::const_iterator option_desc = range.first;
  298. option_desc != range.second; ++option_desc) {
  299. ASSERT_TRUE(option_desc->option);
  300. EXPECT_EQ(code, option_desc->option->getType());
  301. }
  302. }
  303. // Let's try to find some non-exiting option.
  304. const uint16_t non_existing_code = 150;
  305. std::pair<OptionContainerTypeIndex::const_iterator,
  306. OptionContainerTypeIndex::const_iterator> range =
  307. idx.equal_range(non_existing_code);
  308. // Empty set is expected.
  309. EXPECT_EQ(0, distance(range.first, range.second));
  310. }
  311. // This test verifies that the option with the persistency flag can be
  312. // added to the configuration and that options with the persistency flags
  313. // can be retrieved.
  314. TEST(Subnet6Test, addPersistentOption) {
  315. CfgOption cfg;
  316. // Add 10 options to the subnet with option codes 100 - 109.
  317. for (uint16_t code = 100; code < 110; ++code) {
  318. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  319. // We create 10 options and want some of them to be flagged
  320. // persistent and some non-persistent. Persistent options are
  321. // those that server sends to clients regardless if they ask
  322. // for them or not. We pick 3 out of 10 options and mark them
  323. // non-persistent and 7 other options persistent.
  324. // Code values: 102, 105 and 108 are divisible by 3
  325. // and options with these codes will be flagged non-persistent.
  326. // Options with other codes will be flagged persistent.
  327. bool persistent = (code % 3) ? true : false;
  328. ASSERT_NO_THROW(cfg.add(option, persistent, "dhcp6"));
  329. }
  330. // Get added options from the subnet.
  331. OptionContainerPtr options = cfg.getAll("dhcp6");
  332. // options->get<2> returns reference to container index #2. This
  333. // index is used to access options by the 'persistent' flag.
  334. OptionContainerPersistIndex& idx = options->get<2>();
  335. // Get all persistent options->
  336. std::pair<OptionContainerPersistIndex::const_iterator,
  337. OptionContainerPersistIndex::const_iterator> range_persistent =
  338. idx.equal_range(true);
  339. // 3 out of 10 options have been flagged persistent.
  340. ASSERT_EQ(7, distance(range_persistent.first, range_persistent.second));
  341. // Get all non-persistent options->
  342. std::pair<OptionContainerPersistIndex::const_iterator,
  343. OptionContainerPersistIndex::const_iterator> range_non_persistent =
  344. idx.equal_range(false);
  345. // 7 out of 10 options have been flagged persistent.
  346. ASSERT_EQ(3, distance(range_non_persistent.first, range_non_persistent.second));
  347. }
  348. // This test verifies that the vendor option can be added to the configuration.
  349. TEST(CfgOptionTest, addVendorOptions) {
  350. CfgOption cfg;
  351. // Differentiate options by their codes (100-109)
  352. for (uint16_t code = 100; code < 110; ++code) {
  353. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  354. ASSERT_NO_THROW(cfg.add(option, false, "vendor-12345678"));
  355. }
  356. // Add 7 options to another option space. The option codes partially overlap
  357. // with option codes that we have added to dhcp6 option space.
  358. for (uint16_t code = 105; code < 112; ++code) {
  359. OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
  360. ASSERT_NO_THROW(cfg.add(option, false, "vendor-87654321"));
  361. }
  362. // Get options from the Subnet and check if all 10 are there.
  363. OptionContainerPtr options = cfg.getAll(12345678);
  364. ASSERT_TRUE(options);
  365. ASSERT_EQ(10, options->size());
  366. // Validate codes of options added to dhcp6 option space.
  367. uint16_t expected_code = 100;
  368. for (OptionContainer::const_iterator option_desc = options->begin();
  369. option_desc != options->end(); ++option_desc) {
  370. ASSERT_TRUE(option_desc->option);
  371. EXPECT_EQ(expected_code, option_desc->option->getType());
  372. ++expected_code;
  373. }
  374. options = cfg.getAll(87654321);
  375. ASSERT_TRUE(options);
  376. ASSERT_EQ(7, options->size());
  377. // Validate codes of options added to isc option space.
  378. expected_code = 105;
  379. for (OptionContainer::const_iterator option_desc = options->begin();
  380. option_desc != options->end(); ++option_desc) {
  381. ASSERT_TRUE(option_desc->option);
  382. EXPECT_EQ(expected_code, option_desc->option->getType());
  383. ++expected_code;
  384. }
  385. // Try to get options from a non-existing option space.
  386. options = cfg.getAll(1111111);
  387. ASSERT_TRUE(options);
  388. EXPECT_TRUE(options->empty());
  389. }
  390. } // end of anonymous namespace