libdhcp++_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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 <iostream>
  16. #include <sstream>
  17. #include <arpa/inet.h>
  18. #include <gtest/gtest.h>
  19. #include <util/buffer.h>
  20. #include <dhcp/dhcp4.h>
  21. #include <dhcp/dhcp6.h>
  22. #include <dhcp/libdhcp++.h>
  23. #include <dhcp/option6_ia.h>
  24. #include <dhcp/option6_iaaddr.h>
  25. #include <dhcp/option6_int.h>
  26. #include <dhcp/option6_int_array.h>
  27. #include <dhcp/option6_addrlst.h>
  28. #include "config.h"
  29. using namespace std;
  30. using namespace isc;
  31. using namespace isc::dhcp;
  32. using namespace isc::util;
  33. namespace {
  34. class LibDhcpTest : public ::testing::Test {
  35. public:
  36. LibDhcpTest() {
  37. }
  38. /// @brief Generic factory function to create any option.
  39. ///
  40. /// Generic factory function to create any option.
  41. ///
  42. /// @param u universe (V4 or V6)
  43. /// @param type option-type
  44. /// @param buf option-buffer
  45. static OptionPtr genericOptionFactory(Option::Universe u, uint16_t type,
  46. const OptionBuffer& buf) {
  47. Option* option = new Option(u, type, buf);
  48. return OptionPtr(option);
  49. }
  50. /// @brief Test option option definition.
  51. ///
  52. /// This function tests if option definition for standard
  53. /// option has been initialized correctly.
  54. ///
  55. /// @param code option code.
  56. /// @param bug buffer to be used to create option instance.
  57. /// @param expected_type type of the option created by the
  58. /// factory function returned by the option definition.
  59. static void testInitOptionDefs6(const uint16_t code,
  60. const OptionBuffer& buf,
  61. const std::type_info& expected_type) {
  62. OptionDefContainer options;
  63. LibDHCP::initStdOptionDefs6(options);
  64. const OptionDefContainerTypeIndex& idx = options.get<1>();
  65. OptionDefContainerTypeRange range = idx.equal_range(code);
  66. ASSERT_EQ(1, std::distance(range.first, range.second));
  67. OptionDefinitionPtr def = *(range.first);
  68. ASSERT_TRUE(def);
  69. ASSERT_NO_THROW(def->validate());
  70. Option::Factory* factory = NULL;
  71. ASSERT_NO_THROW(factory = def->getFactory());
  72. OptionPtr option;
  73. ASSERT_NO_THROW(option = factory(Option::V6, code, buf));
  74. ASSERT_TRUE(option);
  75. EXPECT_TRUE(typeid(*option) == expected_type);
  76. }
  77. };
  78. static const uint8_t packed[] = {
  79. 0, 12, 0, 5, 100, 101, 102, 103, 104, // opt1 (9 bytes)
  80. 0, 13, 0, 3, 105, 106, 107, // opt2 (7 bytes)
  81. 0, 14, 0, 2, 108, 109, // opt3 (6 bytes)
  82. 1, 0, 0, 4, 110, 111, 112, 113, // opt4 (8 bytes)
  83. 1, 1, 0, 1, 114 // opt5 (5 bytes)
  84. };
  85. TEST(LibDhcpTest, optionFactory) {
  86. OptionBuffer buf;
  87. // Factory functions for specific options must be registered before
  88. // they can be used to create options instances. Otherwise exception
  89. // is rised.
  90. EXPECT_THROW(LibDHCP::optionFactory(Option::V4, DHO_SUBNET_MASK, buf),
  91. isc::BadValue);
  92. // Let's register some factory functions (two v4 and one v6 function).
  93. // Registration may trigger exception if function for the specified
  94. // option has been registered already.
  95. ASSERT_NO_THROW(
  96. LibDHCP::OptionFactoryRegister(Option::V4, DHO_SUBNET_MASK,
  97. &LibDhcpTest::genericOptionFactory);
  98. );
  99. ASSERT_NO_THROW(
  100. LibDHCP::OptionFactoryRegister(Option::V4, DHO_TIME_OFFSET,
  101. &LibDhcpTest::genericOptionFactory);
  102. );
  103. ASSERT_NO_THROW(
  104. LibDHCP::OptionFactoryRegister(Option::V6, D6O_CLIENTID,
  105. &LibDhcpTest::genericOptionFactory);
  106. );
  107. // Invoke factory functions for all options (check if registration
  108. // was successful).
  109. OptionPtr opt_subnet_mask;
  110. opt_subnet_mask = LibDHCP::optionFactory(Option::V4,
  111. DHO_SUBNET_MASK,
  112. buf);
  113. // Check if non-NULL DHO_SUBNET_MASK option pointer has been returned.
  114. ASSERT_TRUE(opt_subnet_mask);
  115. // Validate if type and universe is correct.
  116. EXPECT_EQ(Option::V4, opt_subnet_mask->getUniverse());
  117. EXPECT_EQ(DHO_SUBNET_MASK, opt_subnet_mask->getType());
  118. // Expect that option does not have content..
  119. EXPECT_EQ(0, opt_subnet_mask->len() - opt_subnet_mask->getHeaderLen());
  120. // Fill the time offset buffer with 4 bytes of data. Each byte set to 1.
  121. OptionBuffer time_offset_buf(4, 1);
  122. OptionPtr opt_time_offset;
  123. opt_time_offset = LibDHCP::optionFactory(Option::V4,
  124. DHO_TIME_OFFSET,
  125. time_offset_buf);
  126. // Check if non-NULL DHO_TIME_OFFSET option pointer has been returned.
  127. ASSERT_TRUE(opt_time_offset);
  128. // Validate if option length, type and universe is correct.
  129. EXPECT_EQ(Option::V4, opt_time_offset->getUniverse());
  130. EXPECT_EQ(DHO_TIME_OFFSET, opt_time_offset->getType());
  131. EXPECT_EQ(time_offset_buf.size(),
  132. opt_time_offset->len() - opt_time_offset->getHeaderLen());
  133. // Validate data in the option.
  134. EXPECT_TRUE(std::equal(time_offset_buf.begin(), time_offset_buf.end(),
  135. opt_time_offset->getData().begin()));
  136. // Fill the client id buffer with 20 bytes of data. Each byte set to 2.
  137. OptionBuffer clientid_buf(20, 2);
  138. OptionPtr opt_clientid;
  139. opt_clientid = LibDHCP::optionFactory(Option::V6,
  140. D6O_CLIENTID,
  141. clientid_buf);
  142. // Check if non-NULL D6O_CLIENTID option pointer has been returned.
  143. ASSERT_TRUE(opt_clientid);
  144. // Validate if option length, type and universe is correct.
  145. EXPECT_EQ(Option::V6, opt_clientid->getUniverse());
  146. EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType());
  147. EXPECT_EQ(clientid_buf.size(), opt_clientid->len() - opt_clientid->getHeaderLen());
  148. // Validate data in the option.
  149. EXPECT_TRUE(std::equal(clientid_buf.begin(), clientid_buf.end(),
  150. opt_clientid->getData().begin()));
  151. }
  152. TEST(LibDhcpTest, packOptions6) {
  153. OptionBuffer buf(512);
  154. isc::dhcp::Option::OptionCollection opts; // list of options
  155. // generate content for options
  156. for (int i = 0; i < 64; i++) {
  157. buf[i]=i+100;
  158. }
  159. OptionPtr opt1(new Option(Option::V6, 12, buf.begin() + 0, buf.begin() + 5));
  160. OptionPtr opt2(new Option(Option::V6, 13, buf.begin() + 5, buf.begin() + 8));
  161. OptionPtr opt3(new Option(Option::V6, 14, buf.begin() + 8, buf.begin() + 10));
  162. OptionPtr opt4(new Option(Option::V6,256, buf.begin() + 10,buf.begin() + 14));
  163. OptionPtr opt5(new Option(Option::V6,257, buf.begin() + 14,buf.begin() + 15));
  164. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt1));
  165. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt2));
  166. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt3));
  167. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt4));
  168. opts.insert(pair<int, OptionPtr >(opt1->getType(), opt5));
  169. OutputBuffer assembled(512);
  170. EXPECT_NO_THROW(LibDHCP::packOptions6(assembled, opts));
  171. EXPECT_EQ(35, assembled.getLength()); // options should take 35 bytes
  172. EXPECT_EQ(0, memcmp(assembled.getData(), packed, 35) );
  173. }
  174. TEST(LibDhcpTest, unpackOptions6) {
  175. // just couple of random options
  176. // Option is used as a simple option implementation
  177. // More advanced uses are validated in tests dedicated for
  178. // specific derived classes.
  179. isc::dhcp::Option::OptionCollection options; // list of options
  180. OptionBuffer buf(512);
  181. memcpy(&buf[0], packed, 35);
  182. EXPECT_NO_THROW ({
  183. LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin()+35), options);
  184. });
  185. EXPECT_EQ(options.size(), 5); // there should be 5 options
  186. isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
  187. ASSERT_FALSE(x == options.end()); // option 1 should exist
  188. EXPECT_EQ(12, x->second->getType()); // this should be option 12
  189. ASSERT_EQ(9, x->second->len()); // it should be of length 9
  190. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+4, 5)); // data len=5
  191. x = options.find(13);
  192. ASSERT_FALSE(x == options.end()); // option 13 should exist
  193. EXPECT_EQ(13, x->second->getType()); // this should be option 13
  194. ASSERT_EQ(7, x->second->len()); // it should be of length 7
  195. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+13, 3)); // data len=3
  196. x = options.find(14);
  197. ASSERT_FALSE(x == options.end()); // option 3 should exist
  198. EXPECT_EQ(14, x->second->getType()); // this should be option 14
  199. ASSERT_EQ(6, x->second->len()); // it should be of length 6
  200. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+20, 2)); // data len=2
  201. x = options.find(256);
  202. ASSERT_FALSE(x == options.end()); // option 256 should exist
  203. EXPECT_EQ(256, x->second->getType()); // this should be option 256
  204. ASSERT_EQ(8, x->second->len()); // it should be of length 7
  205. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+26, 4)); // data len=4
  206. x = options.find(257);
  207. ASSERT_FALSE(x == options.end()); // option 257 should exist
  208. EXPECT_EQ(257, x->second->getType()); // this should be option 257
  209. ASSERT_EQ(5, x->second->len()); // it should be of length 5
  210. EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+34, 1)); // data len=1
  211. x = options.find(0);
  212. EXPECT_TRUE(x == options.end()); // option 0 not found
  213. x = options.find(1); // 1 is htons(256) on little endians. Worth checking
  214. EXPECT_TRUE(x == options.end()); // option 1 not found
  215. x = options.find(2);
  216. EXPECT_TRUE(x == options.end()); // option 2 not found
  217. x = options.find(32000);
  218. EXPECT_TRUE(x == options.end()); // option 32000 not found
  219. }
  220. static uint8_t v4Opts[] = {
  221. 12, 3, 0, 1, 2,
  222. 13, 3, 10, 11, 12,
  223. 14, 3, 20, 21, 22,
  224. 254, 3, 30, 31, 32,
  225. 128, 3, 40, 41, 42
  226. };
  227. TEST(LibDhcpTest, packOptions4) {
  228. vector<uint8_t> payload[5];
  229. for (int i = 0; i < 5; i++) {
  230. payload[i].resize(3);
  231. payload[i][0] = i*10;
  232. payload[i][1] = i*10+1;
  233. payload[i][2] = i*10+2;
  234. }
  235. OptionPtr opt1(new Option(Option::V4, 12, payload[0]));
  236. OptionPtr opt2(new Option(Option::V4, 13, payload[1]));
  237. OptionPtr opt3(new Option(Option::V4, 14, payload[2]));
  238. OptionPtr opt4(new Option(Option::V4,254, payload[3]));
  239. OptionPtr opt5(new Option(Option::V4,128, payload[4]));
  240. isc::dhcp::Option::OptionCollection opts; // list of options
  241. opts.insert(make_pair(opt1->getType(), opt1));
  242. opts.insert(make_pair(opt1->getType(), opt2));
  243. opts.insert(make_pair(opt1->getType(), opt3));
  244. opts.insert(make_pair(opt1->getType(), opt4));
  245. opts.insert(make_pair(opt1->getType(), opt5));
  246. vector<uint8_t> expVect(v4Opts, v4Opts + sizeof(v4Opts));
  247. OutputBuffer buf(100);
  248. EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
  249. ASSERT_EQ(buf.getLength(), sizeof(v4Opts));
  250. EXPECT_EQ(0, memcmp(v4Opts, buf.getData(), sizeof(v4Opts)));
  251. }
  252. TEST(LibDhcpTest, unpackOptions4) {
  253. vector<uint8_t> packed(v4Opts, v4Opts + sizeof(v4Opts));
  254. isc::dhcp::Option::OptionCollection options; // list of options
  255. ASSERT_NO_THROW(
  256. LibDHCP::unpackOptions4(packed, options);
  257. );
  258. isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
  259. ASSERT_FALSE(x == options.end()); // option 1 should exist
  260. EXPECT_EQ(12, x->second->getType()); // this should be option 12
  261. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  262. EXPECT_EQ(5, x->second->len()); // total option length 5
  263. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+2, 3)); // data len=3
  264. x = options.find(13);
  265. ASSERT_FALSE(x == options.end()); // option 1 should exist
  266. EXPECT_EQ(13, x->second->getType()); // this should be option 13
  267. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  268. EXPECT_EQ(5, x->second->len()); // total option length 5
  269. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+7, 3)); // data len=3
  270. x = options.find(14);
  271. ASSERT_FALSE(x == options.end()); // option 3 should exist
  272. EXPECT_EQ(14, x->second->getType()); // this should be option 14
  273. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  274. EXPECT_EQ(5, x->second->len()); // total option length 5
  275. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+12, 3)); // data len=3
  276. x = options.find(254);
  277. ASSERT_FALSE(x == options.end()); // option 3 should exist
  278. EXPECT_EQ(254, x->second->getType()); // this should be option 254
  279. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  280. EXPECT_EQ(5, x->second->len()); // total option length 5
  281. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+17, 3)); // data len=3
  282. x = options.find(128);
  283. ASSERT_FALSE(x == options.end()); // option 3 should exist
  284. EXPECT_EQ(128, x->second->getType()); // this should be option 254
  285. ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
  286. EXPECT_EQ(5, x->second->len()); // total option length 5
  287. EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+22, 3)); // data len=3
  288. x = options.find(0);
  289. EXPECT_TRUE(x == options.end()); // option 0 not found
  290. x = options.find(1);
  291. EXPECT_TRUE(x == options.end()); // option 1 not found
  292. x = options.find(2);
  293. EXPECT_TRUE(x == options.end()); // option 2 not found
  294. }
  295. // Test that definitions of standard options have been initialized
  296. // correctly.
  297. // @todo Only limited number of option definitions are now created
  298. // This test have to be extended once all option definitions are
  299. // created.
  300. TEST(LibDhcpTest, initStdOptionDefs) {
  301. LibDhcpTest::testInitOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
  302. typeid(Option));
  303. LibDhcpTest::testInitOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
  304. typeid(Option));
  305. LibDhcpTest::testInitOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
  306. typeid(Option6IA));
  307. LibDhcpTest::testInitOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
  308. typeid(Option6IAAddr));
  309. LibDhcpTest::testInitOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
  310. typeid(Option6IntArray<uint16_t>));
  311. LibDhcpTest::testInitOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
  312. typeid(Option6Int<uint16_t>));
  313. LibDhcpTest::testInitOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
  314. typeid(Option));
  315. LibDhcpTest::testInitOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
  316. typeid(Option));
  317. LibDhcpTest::testInitOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
  318. typeid(Option6AddrLst));
  319. }
  320. }