libdhcp++_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // Copyright (C) 2011-2012 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/option6_addrlst.h>
  19. #include <dhcp/option6_ia.h>
  20. #include <dhcp/option6_iaaddr.h>
  21. #include <dhcp/option6_int.h>
  22. #include <dhcp/option6_int_array.h>
  23. #include <util/buffer.h>
  24. #include <gtest/gtest.h>
  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::util;
  32. namespace {
  33. class LibDhcpTest : public ::testing::Test {
  34. public:
  35. LibDhcpTest() { }
  36. /// @brief Generic factory function to create any option.
  37. ///
  38. /// Generic factory function to create any option.
  39. ///
  40. /// @param u universe (V4 or V6)
  41. /// @param type option-type
  42. /// @param buf option-buffer
  43. static OptionPtr genericOptionFactory(Option::Universe u, uint16_t type,
  44. const OptionBuffer& buf) {
  45. Option* option = new Option(u, type, buf);
  46. return OptionPtr(option);
  47. }
  48. /// @brief Test option option definition.
  49. ///
  50. /// This function tests if option definition for standard
  51. /// option has been initialized correctly.
  52. ///
  53. /// @param code option code.
  54. /// @param bug buffer to be used to create option instance.
  55. /// @param expected_type type of the option created by the
  56. /// factory function returned by the option definition.
  57. static void testStdOptionDefs6(const uint16_t code,
  58. const OptionBuffer& buf,
  59. const std::type_info& expected_type) {
  60. // Get all option definitions, we will use them to extract
  61. // the definition for a particular option code.
  62. // We don't have to initialize option deinitions here because they
  63. // are initialized in the class'es constructor.
  64. OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
  65. // Get the container index #1. This one allows for searching
  66. // option definitions using option code.
  67. const OptionDefContainerTypeIndex& idx = options.get<1>();
  68. // Get 'all' option definitions for a particular option code.
  69. // For standard options we expect that the range returned
  70. // will contain single option as their codes are unique.
  71. OptionDefContainerTypeRange range = idx.equal_range(code);
  72. ASSERT_EQ(1, std::distance(range.first, range.second));
  73. // If we have single option definition returned, the
  74. // first iterator holds it.
  75. OptionDefinitionPtr def = *(range.first);
  76. // It should not happen that option definition is NULL but
  77. // let's make sure (test should take things like that into
  78. // account).
  79. ASSERT_TRUE(def);
  80. // Check that option definition is valid.
  81. ASSERT_NO_THROW(def->validate());
  82. OptionPtr option;
  83. // Create the option.
  84. ASSERT_NO_THROW(option = def->optionFactory(Option::V6, code, buf));
  85. // Make sure it is not NULL.
  86. ASSERT_TRUE(option);
  87. // And the actual object type is the one that we expect.
  88. // Note that for many options there are dedicated classes
  89. // derived from Option class to represent them.
  90. EXPECT_TRUE(typeid(*option) == expected_type);
  91. }
  92. };
  93. static const uint8_t packed[] = {
  94. 0, 1, 0, 5, 100, 101, 102, 103, 104, // CLIENT_ID (9 bytes)
  95. 0, 2, 0, 3, 105, 106, 107, // SERVER_ID (7 bytes)
  96. 0, 14, 0, 0, // RAPID_COMMIT (0 bytes)
  97. 0, 6, 0, 4, 108, 109, 110, 111, // ORO (8 bytes)
  98. 0, 8, 0, 2, 112, 113 // ELAPSED_TIME (6 bytes)
  99. };
  100. TEST_F(LibDhcpTest, optionFactory) {
  101. OptionBuffer buf;
  102. // Factory functions for specific options must be registered before
  103. // they can be used to create options instances. Otherwise exception
  104. // is rised.
  105. EXPECT_THROW(LibDHCP::optionFactory(Option::V4, DHO_SUBNET_MASK, buf),
  106. isc::BadValue);
  107. // Let's register some factory functions (two v4 and one v6 function).
  108. // Registration may trigger exception if function for the specified
  109. // option has been registered already.
  110. ASSERT_NO_THROW(
  111. LibDHCP::OptionFactoryRegister(Option::V4, DHO_SUBNET_MASK,
  112. &LibDhcpTest::genericOptionFactory);
  113. );
  114. ASSERT_NO_THROW(
  115. LibDHCP::OptionFactoryRegister(Option::V4, DHO_TIME_OFFSET,
  116. &LibDhcpTest::genericOptionFactory);
  117. );
  118. ASSERT_NO_THROW(
  119. LibDHCP::OptionFactoryRegister(Option::V6, D6O_CLIENTID,
  120. &LibDhcpTest::genericOptionFactory);
  121. );
  122. // Invoke factory functions for all options (check if registration
  123. // was successful).
  124. OptionPtr opt_subnet_mask;
  125. opt_subnet_mask = LibDHCP::optionFactory(Option::V4,
  126. DHO_SUBNET_MASK,
  127. buf);
  128. // Check if non-NULL DHO_SUBNET_MASK option pointer has been returned.
  129. ASSERT_TRUE(opt_subnet_mask);
  130. // Validate if type and universe is correct.
  131. EXPECT_EQ(Option::V4, opt_subnet_mask->getUniverse());
  132. EXPECT_EQ(DHO_SUBNET_MASK, opt_subnet_mask->getType());
  133. // Expect that option does not have content..
  134. EXPECT_EQ(0, opt_subnet_mask->len() - opt_subnet_mask->getHeaderLen());
  135. // Fill the time offset buffer with 4 bytes of data. Each byte set to 1.
  136. OptionBuffer time_offset_buf(4, 1);
  137. OptionPtr opt_time_offset;
  138. opt_time_offset = LibDHCP::optionFactory(Option::V4,
  139. DHO_TIME_OFFSET,
  140. time_offset_buf);
  141. // Check if non-NULL DHO_TIME_OFFSET option pointer has been returned.
  142. ASSERT_TRUE(opt_time_offset);
  143. // Validate if option length, type and universe is correct.
  144. EXPECT_EQ(Option::V4, opt_time_offset->getUniverse());
  145. EXPECT_EQ(DHO_TIME_OFFSET, opt_time_offset->getType());
  146. EXPECT_EQ(time_offset_buf.size(),
  147. opt_time_offset->len() - opt_time_offset->getHeaderLen());
  148. // Validate data in the option.
  149. EXPECT_TRUE(std::equal(time_offset_buf.begin(), time_offset_buf.end(),
  150. opt_time_offset->getData().begin()));
  151. // Fill the client id buffer with 20 bytes of data. Each byte set to 2.
  152. OptionBuffer clientid_buf(20, 2);
  153. OptionPtr opt_clientid;
  154. opt_clientid = LibDHCP::optionFactory(Option::V6,
  155. D6O_CLIENTID,
  156. clientid_buf);
  157. // Check if non-NULL D6O_CLIENTID option pointer has been returned.
  158. ASSERT_TRUE(opt_clientid);
  159. // Validate if option length, type and universe is correct.
  160. EXPECT_EQ(Option::V6, opt_clientid->getUniverse());
  161. EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType());
  162. EXPECT_EQ(clientid_buf.size(), opt_clientid->len() - opt_clientid->getHeaderLen());
  163. // Validate data in the option.
  164. EXPECT_TRUE(std::equal(clientid_buf.begin(), clientid_buf.end(),
  165. opt_clientid->getData().begin()));
  166. }
  167. TEST_F(LibDhcpTest, packOptions6) {
  168. OptionBuffer buf(512);
  169. isc::dhcp::Option::OptionCollection opts; // list of options
  170. // generate content for options
  171. for (int i = 0; i < 64; i++) {
  172. buf[i]=i+100;
  173. }
  174. OptionPtr opt1(new Option(Option::V6, 1, buf.begin() + 0, buf.begin() + 5));
  175. OptionPtr opt2(new Option(Option::V6, 2, buf.begin() + 5, buf.begin() + 8));
  176. OptionPtr opt3(new Option(Option::V6, 14, buf.begin() + 8, buf.begin() + 8));
  177. OptionPtr opt4(new Option(Option::V6, 6, buf.begin() + 8, buf.begin() + 12));
  178. OptionPtr opt5(new Option(Option::V6, 8, buf.begin() + 12, buf.begin() + 14));
  179. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt1));
  180. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt2));
  181. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt3));
  182. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt4));
  183. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt5));
  184. OutputBuffer assembled(512);
  185. EXPECT_NO_THROW(LibDHCP::packOptions6(assembled, opts));
  186. EXPECT_EQ(sizeof(packed), assembled.getLength());
  187. EXPECT_EQ(0, memcmp(assembled.getData(), packed, sizeof(packed)));
  188. }
  189. TEST_F(LibDhcpTest, unpackOptions6) {
  190. // just couple of random options
  191. // Option is used as a simple option implementation
  192. // More advanced uses are validated in tests dedicated for
  193. // specific derived classes.
  194. isc::dhcp::Option::OptionCollection options; // list of options
  195. OptionBuffer buf(512);
  196. memcpy(&buf[0], packed, sizeof(packed));
  197. EXPECT_NO_THROW ({
  198. LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(packed)),
  199. options);
  200. });
  201. EXPECT_EQ(options.size(), 5); // there should be 5 options
  202. isc::dhcp::Option::OptionCollection::const_iterator x = options.find(1);
  203. ASSERT_FALSE(x == options.end()); // option 1 should exist
  204. EXPECT_EQ(1, x->second->getType()); // this should be option 1
  205. ASSERT_EQ(9, x->second->len()); // it should be of length 9
  206. ASSERT_EQ(5, x->second->getData().size());
  207. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed + 4, 5)); // data len=5
  208. x = options.find(2);
  209. ASSERT_FALSE(x == options.end()); // option 2 should exist
  210. EXPECT_EQ(2, x->second->getType()); // this should be option 2
  211. ASSERT_EQ(7, x->second->len()); // it should be of length 7
  212. ASSERT_EQ(3, x->second->getData().size());
  213. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed + 13, 3)); // data len=3
  214. x = options.find(14);
  215. ASSERT_FALSE(x == options.end()); // option 14 should exist
  216. EXPECT_EQ(14, x->second->getType()); // this should be option 14
  217. ASSERT_EQ(4, x->second->len()); // it should be of length 4
  218. EXPECT_EQ(0, x->second->getData().size()); // data len = 0
  219. x = options.find(6);
  220. ASSERT_FALSE(x == options.end()); // option 6 should exist
  221. EXPECT_EQ(6, x->second->getType()); // this should be option 6
  222. ASSERT_EQ(8, x->second->len()); // it should be of length 8
  223. // Option with code 6 is the OPTION_ORO. This option is
  224. // represented by the Option6IntArray<uint16_t> class which
  225. // comprises the set of uint16_t values. We need to cast the
  226. // returned pointer to this type to get values stored in it.
  227. boost::shared_ptr<Option6IntArray<uint16_t> > opt_oro =
  228. boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(x->second);
  229. // This value will be NULL if cast was unsuccessful. This is the case
  230. // when returned option has different type than expected.
  231. ASSERT_TRUE(opt_oro);
  232. // Get set of uint16_t values.
  233. std::vector<uint16_t> opts = opt_oro->getValues();
  234. // Prepare the refrence data.
  235. std::vector<uint16_t> expected_opts;
  236. expected_opts.push_back(0x6C6D); // equivalent to: 108, 109
  237. expected_opts.push_back(0x6E6F); // equivalent to 110, 111
  238. ASSERT_EQ(expected_opts.size(), opts.size());
  239. // Validated if option has been unpacked correctly.
  240. EXPECT_TRUE(std::equal(expected_opts.begin(), expected_opts.end(),
  241. opts.begin()));
  242. x = options.find(8);
  243. ASSERT_FALSE(x == options.end()); // option 8 should exist
  244. EXPECT_EQ(8, x->second->getType()); // this should be option 8
  245. ASSERT_EQ(6, x->second->len()); // it should be of length 9
  246. // Option with code 8 is OPTION_ELAPSED_TIME. This option is
  247. // represented by Option6Int<uint16_t> value that holds single
  248. // uint16_t value.
  249. boost::shared_ptr<Option6Int<uint16_t> > opt_elapsed_time =
  250. boost::dynamic_pointer_cast<Option6Int<uint16_t> >(x->second);
  251. // This value will be NULL if cast was unsuccessful. This is the case
  252. // when returned option has different type than expected.
  253. ASSERT_TRUE(opt_elapsed_time);
  254. // Returned value should be equivalent to two byte values: 112, 113
  255. EXPECT_EQ(0x7071, opt_elapsed_time->getValue());
  256. x = options.find(0);
  257. EXPECT_TRUE(x == options.end()); // option 0 not found
  258. x = options.find(256); // 256 is htons(1) on little endians. Worth checking
  259. EXPECT_TRUE(x == options.end()); // option 1 not found
  260. x = options.find(7);
  261. EXPECT_TRUE(x == options.end()); // option 2 not found
  262. x = options.find(32000);
  263. EXPECT_TRUE(x == options.end()); // option 32000 not found */
  264. }
  265. static uint8_t v4Opts[] = {
  266. 12, 3, 0, 1, 2,
  267. 13, 3, 10, 11, 12,
  268. 14, 3, 20, 21, 22,
  269. 254, 3, 30, 31, 32,
  270. 128, 3, 40, 41, 42
  271. };
  272. TEST_F(LibDhcpTest, packOptions4) {
  273. vector<uint8_t> payload[5];
  274. for (int i = 0; i < 5; i++) {
  275. payload[i].resize(3);
  276. payload[i][0] = i*10;
  277. payload[i][1] = i*10+1;
  278. payload[i][2] = i*10+2;
  279. }
  280. OptionPtr opt1(new Option(Option::V4, 12, payload[0]));
  281. OptionPtr opt2(new Option(Option::V4, 13, payload[1]));
  282. OptionPtr opt3(new Option(Option::V4, 14, payload[2]));
  283. OptionPtr opt4(new Option(Option::V4,254, payload[3]));
  284. OptionPtr opt5(new Option(Option::V4,128, payload[4]));
  285. isc::dhcp::Option::OptionCollection opts; // list of options
  286. opts.insert(make_pair(opt1->getType(), opt1));
  287. opts.insert(make_pair(opt1->getType(), opt2));
  288. opts.insert(make_pair(opt1->getType(), opt3));
  289. opts.insert(make_pair(opt1->getType(), opt4));
  290. opts.insert(make_pair(opt1->getType(), opt5));
  291. vector<uint8_t> expVect(v4Opts, v4Opts + sizeof(v4Opts));
  292. OutputBuffer buf(100);
  293. EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
  294. ASSERT_EQ(buf.getLength(), sizeof(v4Opts));
  295. EXPECT_EQ(0, memcmp(v4Opts, buf.getData(), sizeof(v4Opts)));
  296. }
  297. TEST_F(LibDhcpTest, unpackOptions4) {
  298. vector<uint8_t> packed(v4Opts, v4Opts + sizeof(v4Opts));
  299. isc::dhcp::Option::OptionCollection options; // list of options
  300. ASSERT_NO_THROW(
  301. LibDHCP::unpackOptions4(packed, options);
  302. );
  303. isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
  304. ASSERT_FALSE(x == options.end()); // option 1 should exist
  305. EXPECT_EQ(12, x->second->getType()); // this should be option 12
  306. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  307. EXPECT_EQ(5, x->second->len()); // total option length 5
  308. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+2, 3)); // data len=3
  309. x = options.find(13);
  310. ASSERT_FALSE(x == options.end()); // option 1 should exist
  311. EXPECT_EQ(13, x->second->getType()); // this should be option 13
  312. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  313. EXPECT_EQ(5, x->second->len()); // total option length 5
  314. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+7, 3)); // data len=3
  315. x = options.find(14);
  316. ASSERT_FALSE(x == options.end()); // option 3 should exist
  317. EXPECT_EQ(14, x->second->getType()); // this should be option 14
  318. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  319. EXPECT_EQ(5, x->second->len()); // total option length 5
  320. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+12, 3)); // data len=3
  321. x = options.find(254);
  322. ASSERT_FALSE(x == options.end()); // option 3 should exist
  323. EXPECT_EQ(254, x->second->getType()); // this should be option 254
  324. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  325. EXPECT_EQ(5, x->second->len()); // total option length 5
  326. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+17, 3)); // data len=3
  327. x = options.find(128);
  328. ASSERT_FALSE(x == options.end()); // option 3 should exist
  329. EXPECT_EQ(128, x->second->getType()); // this should be option 254
  330. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  331. EXPECT_EQ(5, x->second->len()); // total option length 5
  332. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+22, 3)); // data len=3
  333. x = options.find(0);
  334. EXPECT_TRUE(x == options.end()); // option 0 not found
  335. x = options.find(1);
  336. EXPECT_TRUE(x == options.end()); // option 1 not found
  337. x = options.find(2);
  338. EXPECT_TRUE(x == options.end()); // option 2 not found
  339. }
  340. // Test that definitions of standard options have been initialized
  341. // correctly.
  342. // @todo Only limited number of option definitions are now created
  343. // This test have to be extended once all option definitions are
  344. // created.
  345. TEST_F(LibDhcpTest, stdOptionDefs6) {
  346. LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
  347. typeid(Option));
  348. LibDhcpTest::testStdOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
  349. typeid(Option));
  350. LibDhcpTest::testStdOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
  351. typeid(Option6IA));
  352. LibDhcpTest::testStdOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
  353. typeid(Option6IAAddr));
  354. LibDhcpTest::testStdOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
  355. typeid(Option6IntArray<uint16_t>));
  356. LibDhcpTest::testStdOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
  357. typeid(Option6Int<uint16_t>));
  358. LibDhcpTest::testStdOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
  359. typeid(Option));
  360. LibDhcpTest::testStdOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
  361. typeid(Option));
  362. LibDhcpTest::testStdOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
  363. typeid(Option6AddrLst));
  364. }
  365. }