option_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. // Copyright (C) 2011-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 <dhcp/dhcp6.h>
  16. #include <dhcp/option.h>
  17. #include <exceptions/exceptions.h>
  18. #include <util/buffer.h>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/scoped_ptr.hpp>
  21. #include <gtest/gtest.h>
  22. #include <iostream>
  23. #include <sstream>
  24. #include <arpa/inet.h>
  25. using namespace std;
  26. using namespace isc;
  27. using namespace isc::dhcp;
  28. using namespace isc::util;
  29. using boost::scoped_ptr;
  30. namespace {
  31. class OptionTest : public ::testing::Test {
  32. public:
  33. OptionTest(): buf_(255), outBuf_(255) {
  34. for (int i = 0; i < 255; i++) {
  35. buf_[i] = 255 - i;
  36. }
  37. }
  38. OptionBuffer buf_;
  39. OutputBuffer outBuf_;
  40. };
  41. // V4 is not really implemented yet. A simple test will do for now.
  42. TEST_F(OptionTest, v4_basic) {
  43. scoped_ptr<Option> opt;
  44. EXPECT_NO_THROW(opt.reset(new Option(Option::V4, 17)));
  45. EXPECT_EQ(Option::V4, opt->getUniverse());
  46. EXPECT_EQ(17, opt->getType());
  47. EXPECT_EQ(0, opt->getData().size());
  48. EXPECT_EQ(2, opt->len()); // just v4 header
  49. EXPECT_NO_THROW(opt.reset());
  50. // V4 options have type 0...255
  51. EXPECT_THROW(opt.reset(new Option(Option::V4, 256)), BadValue);
  52. // 0 is a special PAD option
  53. EXPECT_THROW(opt.reset(new Option(Option::V4, 0)), BadValue);
  54. // 255 is a special END option
  55. EXPECT_THROW(opt.reset(new Option(Option::V4, 255)), BadValue);
  56. }
  57. const uint8_t dummyPayload[] =
  58. { 1, 2, 3, 4};
  59. TEST_F(OptionTest, v4_data1) {
  60. vector<uint8_t> data(dummyPayload, dummyPayload + sizeof(dummyPayload));
  61. scoped_ptr<Option> opt;
  62. // Create DHCPv4 option of type 123 that contains 4 bytes of data.
  63. ASSERT_NO_THROW(opt.reset(new Option(Option::V4, 123, data)));
  64. // Check that content is reported properly
  65. EXPECT_EQ(123, opt->getType());
  66. vector<uint8_t> optData = opt->getData();
  67. ASSERT_EQ(optData.size(), data.size());
  68. EXPECT_TRUE(optData == data);
  69. EXPECT_EQ(2, opt->getHeaderLen());
  70. EXPECT_EQ(6, opt->len());
  71. // Now store that option into a buffer
  72. OutputBuffer buf(100);
  73. EXPECT_NO_THROW(opt->pack(buf));
  74. // Check content of that buffer:
  75. // 2 byte header + 4 bytes data
  76. ASSERT_EQ(6, buf.getLength());
  77. // That's how this option is supposed to look like
  78. uint8_t exp[] = { 123, 4, 1, 2, 3, 4 };
  79. /// TODO: use vector<uint8_t> getData() when it will be implemented
  80. EXPECT_EQ(0, memcmp(exp, buf.getData(), 6));
  81. // Check that we can destroy that option
  82. EXPECT_NO_THROW(opt.reset());
  83. }
  84. // This is almost the same test as v4_data1, but it uses a different
  85. // constructor
  86. TEST_F(OptionTest, v4_data2) {
  87. vector<uint8_t> data(dummyPayload, dummyPayload + sizeof(dummyPayload));
  88. vector<uint8_t> expData = data;
  89. // Add fake data in front and end. Main purpose of this test is to check
  90. // that only subset of the whole vector can be used for creating option.
  91. data.insert(data.begin(), 56);
  92. data.push_back(67);
  93. // Data contains extra garbage at beginning and at the end. It should be
  94. // ignored, as we pass interators to proper data. Only subset (limited by
  95. // iterators) of the vector should be used.
  96. // expData contains expected content (just valid data, without garbage).
  97. scoped_ptr<Option> opt;
  98. // Create DHCPv4 option of type 123 that contains
  99. // 4 bytes (sizeof(dummyPayload).
  100. ASSERT_NO_THROW(
  101. opt.reset(new Option(Option::V4, 123, data.begin() + 1,
  102. data.end() - 1));
  103. );
  104. // Check that content is reported properly
  105. EXPECT_EQ(123, opt->getType());
  106. vector<uint8_t> optData = opt->getData();
  107. ASSERT_EQ(optData.size(), expData.size());
  108. EXPECT_TRUE(optData == expData);
  109. EXPECT_EQ(2, opt->getHeaderLen());
  110. EXPECT_EQ(6, opt->len());
  111. // Now store that option into a buffer
  112. OutputBuffer buf(100);
  113. EXPECT_NO_THROW(opt->pack(buf));
  114. // Check content of that buffer
  115. // 2 byte header + 4 bytes data
  116. ASSERT_EQ(6, buf.getLength());
  117. // That's how this option is supposed to look like
  118. uint8_t exp[] = { 123, 4, 1, 2, 3, 4 };
  119. /// TODO: use vector<uint8_t> getData() when it will be implemented
  120. EXPECT_EQ(0, memcmp(exp, buf.getData(), 6));
  121. // Check that we can destroy that option
  122. EXPECT_NO_THROW(opt.reset());
  123. }
  124. TEST_F(OptionTest, v4_toText) {
  125. vector<uint8_t> buf(3);
  126. buf[0] = 0;
  127. buf[1] = 0xf;
  128. buf[2] = 0xff;
  129. Option opt(Option::V4, 253, buf);
  130. EXPECT_EQ("type=253, len=3: 00:0f:ff", opt.toText());
  131. }
  132. // Tests simple constructor
  133. TEST_F(OptionTest, v6_basic) {
  134. scoped_ptr<Option> opt(new Option(Option::V6, 1));
  135. EXPECT_EQ(Option::V6, opt->getUniverse());
  136. EXPECT_EQ(1, opt->getType());
  137. EXPECT_EQ(0, opt->getData().size());
  138. EXPECT_EQ(4, opt->len()); // Just v6 header
  139. EXPECT_NO_THROW(opt.reset());
  140. }
  141. // Tests constructor used in packet reception. Option contains actual data
  142. TEST_F(OptionTest, v6_data1) {
  143. for (int i = 0; i < 32; i++) {
  144. buf_[i] = 100 + i;
  145. }
  146. // Create option with seven bytes of data.
  147. scoped_ptr<Option> opt(new Option(Option::V6, 333, // Type
  148. buf_.begin() + 3, // Begin offset
  149. buf_.begin() + 10)); // End offset
  150. EXPECT_EQ(333, opt->getType());
  151. ASSERT_EQ(11, opt->len());
  152. ASSERT_EQ(7, opt->getData().size());
  153. EXPECT_EQ(0, memcmp(&buf_[3], &opt->getData()[0], 7) );
  154. opt->pack(outBuf_);
  155. EXPECT_EQ(11, outBuf_.getLength());
  156. const uint8_t* out = static_cast<const uint8_t*>(outBuf_.getData());
  157. EXPECT_EQ(out[0], 333 / 256); // Type
  158. EXPECT_EQ(out[1], 333 % 256);
  159. EXPECT_EQ(out[2], 0); // Length
  160. EXPECT_EQ(out[3], 7);
  161. // Payload
  162. EXPECT_EQ(0, memcmp(&buf_[3], out + 4, 7));
  163. EXPECT_NO_THROW(opt.reset());
  164. }
  165. // Another test that tests the same thing, just with different input parameters.
  166. TEST_F(OptionTest, v6_data2) {
  167. buf_[0] = 0xa1;
  168. buf_[1] = 0xa2;
  169. buf_[2] = 0xa3;
  170. buf_[3] = 0xa4;
  171. // Create an option (unpack content)
  172. scoped_ptr<Option> opt(new Option(Option::V6, D6O_CLIENTID,
  173. buf_.begin(), buf_.begin() + 4));
  174. // Pack this option
  175. opt->pack(outBuf_);
  176. // 4 bytes header + 4 bytes content
  177. EXPECT_EQ(8, opt->len());
  178. EXPECT_EQ(D6O_CLIENTID, opt->getType());
  179. EXPECT_EQ(8, outBuf_.getLength());
  180. // Check if pack worked properly:
  181. // If option type is correct
  182. const uint8_t* out = static_cast<const uint8_t*>(outBuf_.getData());
  183. EXPECT_EQ(D6O_CLIENTID, out[0] * 256 + out[1]);
  184. // If option length is correct
  185. EXPECT_EQ(4, out[2] * 256 + out[3]);
  186. // If option content is correct
  187. EXPECT_EQ(0, memcmp(&buf_[0], out + 4, 4));
  188. EXPECT_NO_THROW(opt.reset());
  189. }
  190. // Check that an option can contain 2 suboptions:
  191. // opt1
  192. // +----opt2
  193. // |
  194. // +----opt3
  195. //
  196. TEST_F(OptionTest, v6_suboptions1) {
  197. for (int i = 0; i < 128; i++) {
  198. buf_[i] = 100 + i;
  199. }
  200. scoped_ptr<Option> opt1(new Option(Option::V6, 65535, // Type
  201. buf_.begin(), // 3 bytes of data
  202. buf_.begin() + 3));
  203. OptionPtr opt2(new Option(Option::V6, 13));
  204. OptionPtr opt3(new Option(Option::V6, 7,
  205. buf_.begin() + 3,
  206. buf_.begin() + 8)); // 5 bytes of data
  207. opt1->addOption(opt2);
  208. opt1->addOption(opt3);
  209. // opt2 len = 4 (just header)
  210. // opt3 len = 9 4(header)+5(data)
  211. // opt1 len = 7 + suboptions() = 7 + 4 + 9 = 20
  212. EXPECT_EQ(4, opt2->len());
  213. EXPECT_EQ(9, opt3->len());
  214. EXPECT_EQ(20, opt1->len());
  215. uint8_t expected[] = {
  216. 0xff, 0xff, 0, 16, 100, 101, 102,
  217. 0, 7, 0, 5, 103, 104, 105, 106, 107,
  218. 0, 13, 0, 0 // no data at all
  219. };
  220. opt1->pack(outBuf_);
  221. EXPECT_EQ(20, outBuf_.getLength());
  222. // Payload
  223. EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 20) );
  224. EXPECT_NO_THROW(opt1.reset());
  225. }
  226. // Check that an option can contain nested suboptions:
  227. // opt1
  228. // +----opt2
  229. // |
  230. // +----opt3
  231. //
  232. TEST_F(OptionTest, v6_suboptions2) {
  233. for (int i = 0; i < 128; i++) {
  234. buf_[i] = 100 + i;
  235. }
  236. scoped_ptr<Option> opt1(new Option(Option::V6, 65535, // Type
  237. buf_.begin(), buf_.begin() + 3));
  238. OptionPtr opt2(new Option(Option::V6, 13));
  239. OptionPtr opt3(new Option(Option::V6, 7,
  240. buf_.begin() + 3,
  241. buf_.begin() + 8));
  242. opt1->addOption(opt2);
  243. opt2->addOption(opt3);
  244. // opt3 len = 9 4(header)+5(data)
  245. // opt2 len = 4 (just header) + len(opt3)
  246. // opt1 len = 7 + len(opt2)
  247. uint8_t expected[] = {
  248. 0xff, 0xff, 0, 16, 100, 101, 102,
  249. 0, 13, 0, 9,
  250. 0, 7, 0, 5, 103, 104, 105, 106, 107,
  251. };
  252. opt1->pack(outBuf_);
  253. EXPECT_EQ(20, outBuf_.getLength());
  254. // Payload
  255. EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 20) );
  256. EXPECT_NO_THROW(opt1.reset());
  257. }
  258. TEST_F(OptionTest, v6_addgetdel) {
  259. for (int i = 0; i < 128; i++) {
  260. buf_[i] = 100 + i;
  261. }
  262. scoped_ptr<Option> parent(new Option(Option::V6, 65535)); // Type
  263. OptionPtr opt1(new Option(Option::V6, 1));
  264. OptionPtr opt2(new Option(Option::V6, 2));
  265. OptionPtr opt3(new Option(Option::V6, 2));
  266. parent->addOption(opt1);
  267. parent->addOption(opt2);
  268. // getOption() test
  269. EXPECT_EQ(opt1, parent->getOption(1));
  270. EXPECT_EQ(opt2, parent->getOption(2));
  271. // Expect NULL
  272. EXPECT_EQ(OptionPtr(), parent->getOption(4));
  273. // Now there are 2 options of type 2
  274. parent->addOption(opt3);
  275. // Let's delete one of them
  276. EXPECT_EQ(true, parent->delOption(2));
  277. // There still should be the other option 2
  278. EXPECT_NE(OptionPtr(), parent->getOption(2));
  279. // Let's delete the other option 2
  280. EXPECT_EQ(true, parent->delOption(2));
  281. // No more options with type=2
  282. EXPECT_EQ(OptionPtr(), parent->getOption(2));
  283. // Let's try to delete - should fail
  284. EXPECT_TRUE(false == parent->delOption(2));
  285. }
  286. TEST_F(OptionTest, v6_toText) {
  287. buf_[0] = 0;
  288. buf_[1] = 0xf;
  289. buf_[2] = 0xff;
  290. OptionPtr opt(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3 ));
  291. EXPECT_EQ("type=258, len=3: 00:0f:ff", opt->toText());
  292. }
  293. TEST_F(OptionTest, getUintX) {
  294. buf_[0] = 0x5;
  295. buf_[1] = 0x4;
  296. buf_[2] = 0x3;
  297. buf_[3] = 0x2;
  298. buf_[4] = 0x1;
  299. // Five options with varying lengths
  300. OptionPtr opt1(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 1));
  301. OptionPtr opt2(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
  302. OptionPtr opt3(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3));
  303. OptionPtr opt4(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 4));
  304. OptionPtr opt5(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 5));
  305. EXPECT_EQ(5, opt1->getUint8());
  306. EXPECT_THROW(opt1->getUint16(), OutOfRange);
  307. EXPECT_THROW(opt1->getUint32(), OutOfRange);
  308. EXPECT_EQ(5, opt2->getUint8());
  309. EXPECT_EQ(0x0504, opt2->getUint16());
  310. EXPECT_THROW(opt2->getUint32(), OutOfRange);
  311. EXPECT_EQ(5, opt3->getUint8());
  312. EXPECT_EQ(0x0504, opt3->getUint16());
  313. EXPECT_THROW(opt3->getUint32(), OutOfRange);
  314. EXPECT_EQ(5, opt4->getUint8());
  315. EXPECT_EQ(0x0504, opt4->getUint16());
  316. EXPECT_EQ(0x05040302, opt4->getUint32());
  317. // The same as for 4-byte long, just get first 1,2 or 4 bytes
  318. EXPECT_EQ(5, opt5->getUint8());
  319. EXPECT_EQ(0x0504, opt5->getUint16());
  320. EXPECT_EQ(0x05040302, opt5->getUint32());
  321. }
  322. TEST_F(OptionTest, setUintX) {
  323. OptionPtr opt1(new Option(Option::V4, 125));
  324. OptionPtr opt2(new Option(Option::V4, 125));
  325. OptionPtr opt4(new Option(Option::V4, 125));
  326. // Verify setUint8
  327. opt1->setUint8(255);
  328. EXPECT_EQ(255, opt1->getUint8());
  329. opt1->pack(outBuf_);
  330. EXPECT_EQ(3, opt1->len());
  331. EXPECT_EQ(3, outBuf_.getLength());
  332. uint8_t exp1[] = {125, 1, 255};
  333. EXPECT_TRUE(0 == memcmp(exp1, outBuf_.getData(), 3));
  334. // Verify getUint16
  335. outBuf_.clear();
  336. opt2->setUint16(12345);
  337. opt2->pack(outBuf_);
  338. EXPECT_EQ(12345, opt2->getUint16());
  339. EXPECT_EQ(4, opt2->len());
  340. EXPECT_EQ(4, outBuf_.getLength());
  341. uint8_t exp2[] = {125, 2, 12345/256, 12345%256};
  342. EXPECT_TRUE(0 == memcmp(exp2, outBuf_.getData(), 4));
  343. // Verify getUint32
  344. outBuf_.clear();
  345. opt4->setUint32(0x12345678);
  346. opt4->pack(outBuf_);
  347. EXPECT_EQ(0x12345678, opt4->getUint32());
  348. EXPECT_EQ(6, opt4->len());
  349. EXPECT_EQ(6, outBuf_.getLength());
  350. uint8_t exp4[] = {125, 4, 0x12, 0x34, 0x56, 0x78};
  351. EXPECT_TRUE(0 == memcmp(exp4, outBuf_.getData(), 6));
  352. }
  353. TEST_F(OptionTest, setData) {
  354. // Verify data override with new buffer larger than initial option buffer
  355. // size.
  356. OptionPtr opt1(new Option(Option::V4, 125,
  357. buf_.begin(), buf_.begin() + 10));
  358. buf_.resize(20, 1);
  359. opt1->setData(buf_.begin(), buf_.end());
  360. opt1->pack(outBuf_);
  361. ASSERT_EQ(outBuf_.getLength() - opt1->getHeaderLen(), buf_.size());
  362. const uint8_t* test_data = static_cast<const uint8_t*>(outBuf_.getData());
  363. EXPECT_TRUE(0 == memcmp(&buf_[0], test_data + opt1->getHeaderLen(),
  364. buf_.size()));
  365. // Verify data override with new buffer shorter than initial option buffer
  366. // size.
  367. OptionPtr opt2(new Option(Option::V4, 125,
  368. buf_.begin(), buf_.begin() + 10));
  369. outBuf_.clear();
  370. buf_.resize(5, 1);
  371. opt2->setData(buf_.begin(), buf_.end());
  372. opt2->pack(outBuf_);
  373. ASSERT_EQ(outBuf_.getLength() - opt1->getHeaderLen(), buf_.size());
  374. test_data = static_cast<const uint8_t*>(outBuf_.getData());
  375. EXPECT_TRUE(0 == memcmp(&buf_[0], test_data + opt1->getHeaderLen(),
  376. buf_.size()));
  377. }
  378. // This test verifies that options can be compared using equal() method.
  379. TEST_F(OptionTest, equal) {
  380. // Five options with varying lengths
  381. OptionPtr opt1(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 1));
  382. OptionPtr opt2(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
  383. OptionPtr opt3(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3));
  384. // The same content as opt2, but different type
  385. OptionPtr opt4(new Option(Option::V6, 1, buf_.begin(), buf_.begin() + 2));
  386. // Another instance with the same type and content as opt2
  387. OptionPtr opt5(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
  388. EXPECT_TRUE(opt1->equal(opt1));
  389. EXPECT_FALSE(opt1->equal(opt2));
  390. EXPECT_FALSE(opt1->equal(opt3));
  391. EXPECT_FALSE(opt1->equal(opt4));
  392. EXPECT_TRUE(opt2->equal(opt5));
  393. }
  394. }