perf_pkt4_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // Copyright (C) 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 <asiolink/io_address.h>
  20. #include <dhcp/option.h>
  21. #include <dhcp/dhcp4.h>
  22. #include "../localized_option.h"
  23. #include "../perf_pkt4.h"
  24. using namespace std;
  25. using namespace isc;
  26. using namespace isc::asiolink;
  27. using namespace isc::dhcp;
  28. using namespace isc::perfdhcp;
  29. typedef PerfPkt4::LocalizedOptionPtr LocalizedOptionPtr;
  30. namespace {
  31. // A dummy MAC address, padded with 0s
  32. const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
  33. 0, 0, 0, 0, 0, 0, 0, 0 };
  34. // Let's use some creative test content here (128 chars + \0)
  35. const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
  36. "adipiscing elit. Proin mollis placerat metus, at "
  37. "lacinia orci ornare vitae. Mauris amet.";
  38. // Yet another type of test content (64 chars + \0)
  39. const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
  40. "adipiscing elit posuere.";
  41. class PerfPkt4Test : public ::testing::Test {
  42. public:
  43. PerfPkt4Test() {
  44. }
  45. /// \brief Returns buffer with sample DHCPDISCOVER message.
  46. ///
  47. /// This method creates buffer containing on-wire data of
  48. /// DHCPDICOSVER message. This buffer is used by tests below
  49. /// to create DHCPv4 test packets.
  50. ///
  51. /// \return vector containing on-wire data
  52. std::vector<uint8_t>& capture() {
  53. // That is only part of the header. It contains all "short" fields,
  54. // larger fields are constructed separately.
  55. uint8_t hdr[] = {
  56. 1, 6, 6, 13, // op, htype, hlen, hops,
  57. 0x12, 0x34, 0x56, 0x78, // transaction-id
  58. 0, 42, 0x80, 0x00, // 42 secs, BROADCAST flags
  59. 192, 0, 2, 1, // ciaddr
  60. 1, 2, 3, 4, // yiaddr
  61. 192, 0, 2, 255, // siaddr
  62. 255, 255, 255, 255, // giaddr
  63. };
  64. uint8_t v4Opts[] = {
  65. DHO_HOST_NAME, 3, 0, 1, 2, // Host name option.
  66. DHO_BOOT_SIZE, 3, 10, 11, 12, // Boot file size option
  67. DHO_MERIT_DUMP, 3, 20, 21, 22, // Merit dump file
  68. DHO_DHCP_MESSAGE_TYPE, 1, 1, // DHCP message type.
  69. 128, 3, 30, 31, 32,
  70. 254, 3, 40, 41, 42,
  71. };
  72. // Initialize the vector with the header fields defined above.
  73. static std::vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
  74. // If this is a first call to this function. Initialize
  75. // remaining data.
  76. if (buf.size() == sizeof(hdr)) {
  77. // Append the large header fields.
  78. std::copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN,
  79. back_inserter(buf));
  80. std::copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN,
  81. back_inserter(buf));
  82. std::copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN,
  83. back_inserter(buf));
  84. // Append magic cookie.
  85. buf.push_back(0x63);
  86. buf.push_back(0x82);
  87. buf.push_back(0x53);
  88. buf.push_back(0x63);
  89. // Append options.
  90. std::copy(v4Opts, v4Opts + sizeof(v4Opts), back_inserter(buf));
  91. }
  92. return buf;
  93. }
  94. };
  95. TEST_F(PerfPkt4Test, Constructor) {
  96. // Initialize some dummy payload.
  97. uint8_t data[250];
  98. for (int i = 0; i < 250; ++i) {
  99. data[i] = i;
  100. }
  101. // Test constructor to be used for incoming messages.
  102. // Use default (1) offset value and don't specify transaction id.
  103. const size_t offset_transid[] = { 1, 10 };
  104. boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(data,
  105. sizeof(data),
  106. offset_transid[0]));
  107. EXPECT_EQ(1, pkt1->getTransidOffset());
  108. // Test constructor to be used for outgoing messages.
  109. // Use non-zero offset and specify transaction id.
  110. const uint32_t transid = 0x010203;
  111. boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data),
  112. offset_transid[1],
  113. transid));
  114. EXPECT_EQ(transid, pkt2->getTransid());
  115. EXPECT_EQ(offset_transid[1], pkt2->getTransidOffset());
  116. // Test default constructor. Transaction id offset is expected to be 1.
  117. boost::scoped_ptr<PerfPkt4> pkt3(new PerfPkt4(data, sizeof(data)));
  118. EXPECT_EQ(1, pkt3->getTransidOffset());
  119. }
  120. TEST_F(PerfPkt4Test, RawPack) {
  121. // Create new packet.
  122. std::vector<uint8_t> buf = capture();
  123. boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
  124. // Initialize options data.
  125. uint8_t buf_hostname[] = { DHO_HOST_NAME, 3, 4, 5, 6 };
  126. uint8_t buf_boot_filesize[] = { DHO_BOOT_SIZE, 3, 1, 2, 3 };
  127. OptionBuffer vec_hostname(buf_hostname + 2,
  128. buf_hostname + sizeof(buf_hostname));
  129. OptionBuffer vec_boot_filesize(buf_boot_filesize + 2,
  130. buf_boot_filesize + sizeof(buf_hostname));
  131. // Create options objects.
  132. const size_t offset_hostname = 240;
  133. LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4,
  134. DHO_HOST_NAME,
  135. vec_hostname,
  136. offset_hostname));
  137. const size_t offset_boot_filesize = 245;
  138. LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4,
  139. DHO_BOOT_SIZE,
  140. vec_boot_filesize,
  141. offset_boot_filesize));
  142. // Try to add options to packet.
  143. ASSERT_NO_THROW(pkt->addOption(pkt_boot_filesize));
  144. ASSERT_NO_THROW(pkt->addOption(pkt_hostname));
  145. // We have valid options addedwith valid offsets so
  146. // pack operation should succeed.
  147. ASSERT_TRUE(pkt->rawPack());
  148. // Buffer should now contain new values of DHO_HOST_NAME and
  149. // DHO_BOOT_SIZE options.
  150. util::OutputBuffer pkt_output = pkt->getBuffer();
  151. ASSERT_EQ(buf.size(), pkt_output.getLength());
  152. const uint8_t* out_buf_data =
  153. static_cast<const uint8_t*>(pkt_output.getData());
  154. // Check if options we read from buffer is valid.
  155. EXPECT_EQ(0, memcmp(buf_hostname,
  156. out_buf_data + offset_hostname,
  157. sizeof(buf_hostname)));
  158. EXPECT_EQ(0, memcmp(buf_boot_filesize,
  159. out_buf_data + offset_boot_filesize,
  160. sizeof(buf_boot_filesize)));
  161. }
  162. TEST_F(PerfPkt4Test, RawUnpack) {
  163. // Create new packet.
  164. std::vector<uint8_t> buf = capture();
  165. boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
  166. // Create options (existing in the packet) and specify their offsets.
  167. const size_t offset_merit = 250;
  168. LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
  169. DHO_MERIT_DUMP,
  170. OptionBuffer(),
  171. offset_merit));
  172. const size_t offset_msg_type = 255;
  173. LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
  174. DHO_DHCP_MESSAGE_TYPE,
  175. OptionBuffer(),
  176. offset_msg_type));
  177. // Addition should be successful
  178. ASSERT_NO_THROW(pkt->addOption(opt_merit));
  179. ASSERT_NO_THROW(pkt->addOption(opt_msg_type));
  180. // Option fit to packet boundaries and offsets are valid,
  181. // so this should unpack successfully.
  182. ASSERT_TRUE(pkt->rawUnpack());
  183. // At this point we should have updated options data (read from buffer).
  184. // Let's try to retrieve them.
  185. opt_merit = boost::dynamic_pointer_cast<LocalizedOption>
  186. (pkt->getOption(DHO_MERIT_DUMP));
  187. opt_msg_type = boost::dynamic_pointer_cast<LocalizedOption>
  188. (pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
  189. ASSERT_TRUE(opt_merit);
  190. ASSERT_TRUE(opt_msg_type);
  191. // Get first option payload.
  192. OptionBuffer opt_merit_data = opt_merit->getData();
  193. // Define reference data.
  194. uint8_t buf_merit[] = { 20, 21, 22 };
  195. // Validate first option data.
  196. ASSERT_EQ(sizeof(buf_merit), opt_merit_data.size());
  197. EXPECT_TRUE(std::equal(opt_merit_data.begin(),
  198. opt_merit_data.end(),
  199. buf_merit));
  200. // Get second option payload.
  201. OptionBuffer opt_msg_type_data = opt_msg_type->getData();
  202. // Expect one byte of message type payload.
  203. ASSERT_EQ(1, opt_msg_type_data.size());
  204. EXPECT_EQ(1, opt_msg_type_data[0]);
  205. }
  206. TEST_F(PerfPkt4Test, InvalidOptions) {
  207. // Create new packet.
  208. std::vector<uint8_t> buf = capture();
  209. boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size()));
  210. // Create option with invalid offset.
  211. // This option is at offset 250 (not 251).
  212. const size_t offset_merit = 251;
  213. LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
  214. DHO_MERIT_DUMP,
  215. OptionBuffer(),
  216. offset_merit));
  217. ASSERT_NO_THROW(pkt1->addOption(opt_merit));
  218. cout << "Testing unpack of invalid options. "
  219. << "This may produce spurious errors." << endl;
  220. // Unpack is expected to fail because it is supposed to read
  221. // option type from buffer and match it with DHO_MERIT_DUMP.
  222. // It will not match because option is shifted by on byte.
  223. ASSERT_FALSE(pkt1->rawUnpack());
  224. // Create another packet.
  225. boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size()));
  226. // Create DHO_DHCP_MESSAGE_TYPE option that has the wrong offset.
  227. // With this offset, option goes beyond packet size (268).
  228. const size_t offset_msg_type = 266;
  229. LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
  230. DHO_DHCP_MESSAGE_TYPE,
  231. OptionBuffer(1, 2),
  232. offset_msg_type));
  233. // Adding option is expected to be successful because no
  234. // offset validation takes place at this point.
  235. ASSERT_NO_THROW(pkt2->addOption(opt_msg_type));
  236. // This is expected to fail because option is out of bounds.
  237. ASSERT_FALSE(pkt2->rawPack());
  238. }
  239. TEST_F(PerfPkt4Test, TruncatedPacket) {
  240. // Get the whole packet and truncate it to 249 bytes.
  241. std::vector<uint8_t> buf = capture();
  242. buf.resize(249);
  243. boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
  244. // Option DHO_BOOT_SIZE is now truncated because whole packet
  245. // is truncated. This option ends at 249 while last index of
  246. // truncated packet is now 248.
  247. const size_t offset_boot_filesize = 245;
  248. LocalizedOptionPtr opt_boot_filesize(new LocalizedOption(Option::V4,
  249. DHO_BOOT_SIZE,
  250. OptionBuffer(3, 1),
  251. offset_boot_filesize));
  252. ASSERT_NO_THROW(pkt->addOption(opt_boot_filesize));
  253. cout << "Testing pack and unpack of options in truncated "
  254. << "packet. This may produce spurious errors." << endl;
  255. // Both pack and unpack are expected to fail because
  256. // added option is out of bounds.
  257. EXPECT_FALSE(pkt->rawUnpack());
  258. EXPECT_FALSE(pkt->rawPack());
  259. }
  260. TEST_F(PerfPkt4Test, PackTransactionId) {
  261. // Create dummy packet that consists of zeros.
  262. std::vector<uint8_t> buf(268, 0);
  263. const size_t offset_transid[] = { 10, 265 };
  264. const uint32_t transid = 0x0102;
  265. // Initialize transaction id 0x00000102 at offset 10.
  266. boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size(),
  267. offset_transid[0],
  268. transid));
  269. // Pack will inject transaction id at offset 10 into the
  270. // packet buffer.
  271. ASSERT_TRUE(pkt1->rawPack());
  272. // Get packet's output buffer and make sure it has valid size.
  273. util::OutputBuffer out_buf = pkt1->getBuffer();
  274. ASSERT_EQ(buf.size(), out_buf.getLength());
  275. const uint8_t *out_buf_data =
  276. static_cast<const uint8_t*>(out_buf.getData());
  277. // Initialize reference data for transaction id.
  278. const uint8_t ref_data[] = { 0, 0, 1, 2 };
  279. // Expect that reference transaction id matches what we have
  280. // read from buffer.
  281. EXPECT_EQ(0, memcmp(ref_data, out_buf_data + offset_transid[0], 4));
  282. cout << "Testing pack with invalid transaction id offset. "
  283. << "This may produce spurious errors" << endl;
  284. // Create packet with invalid transaction id offset.
  285. // Packet length is 268, transaction id is 4 bytes long so last byte of
  286. // transaction id is out of bounds.
  287. boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size(),
  288. offset_transid[1],
  289. transid));
  290. EXPECT_FALSE(pkt2->rawPack());
  291. }
  292. TEST_F(PerfPkt4Test, UnpackTransactionId) {
  293. // Initialize packet data, lebgth 268, zeros only.
  294. std::vector<uint8_t> in_data(268, 0);
  295. // Assume that transaction id is at offset 100.
  296. // Fill 4 bytes at offset 100 with dummy transaction id.
  297. for (int i = 100; i < 104; ++i) {
  298. in_data[i] = i - 99;
  299. }
  300. // Create packet from initialized buffer.
  301. const size_t offset_transid[] = { 100, 270 };
  302. boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
  303. in_data.size(),
  304. offset_transid[0]));
  305. ASSERT_TRUE(pkt1->rawUnpack());
  306. // Get unpacked transaction id and compare with reference.
  307. EXPECT_EQ(0x01020304, pkt1->getTransid());
  308. // Create packet with transaction id at invalid offset.
  309. boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&in_data[0],
  310. in_data.size(),
  311. offset_transid[1]));
  312. cout << "Testing unpack of transaction id at invalid offset. "
  313. << "This may produce spurious errors." << endl;
  314. // Unpack is supposed to fail because transaction id is at
  315. // out of bounds offset.
  316. EXPECT_FALSE(pkt2->rawUnpack());
  317. }
  318. TEST_F(PerfPkt4Test, Writes) {
  319. // Initialize intput buffer with 260 elements set to value 1.
  320. dhcp::OptionBuffer in_data(260, 1);
  321. // Initialize buffer to be used for write: 1,2,3,4,...,9
  322. dhcp::OptionBuffer write_buf(10);
  323. for (int i = 0; i < write_buf.size(); ++i) {
  324. write_buf[i] = i;
  325. }
  326. // Create packet from the input buffer.
  327. const size_t transid_offset = 4;
  328. boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
  329. in_data.size(),
  330. transid_offset));
  331. // Write numbers 4,5,6,7 to the packet's input buffer at position 10.
  332. pkt1->writeAt(10, write_buf.begin() + 3, write_buf.begin() + 7);
  333. // We have to pack data to output buffer here because Pkt4 provides no
  334. // way to retrieve input buffer. If we pack data it will go to
  335. // output buffer that has getter available.
  336. ASSERT_TRUE(pkt1->rawPack());
  337. const util::OutputBuffer& out_buf = pkt1->getBuffer();
  338. ASSERT_EQ(in_data.size(), out_buf.getLength());
  339. // Verify that 4,5,6,7 has been written to the packet's buffer.
  340. const char* out_data = static_cast<const char*>(out_buf.getData());
  341. EXPECT_TRUE(std::equal(write_buf.begin() + 3, write_buf.begin() + 7,
  342. out_data + 10));
  343. // Write 1 octet (0x51) at position 10.
  344. pkt1->writeValueAt<uint8_t>(10, 0x51);
  345. ASSERT_TRUE(pkt1->rawPack());
  346. ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
  347. EXPECT_EQ(0x51, pkt1->getBuffer()[10]);
  348. // Write 2 octets (0x5251) at position 20.
  349. pkt1->writeValueAt<uint16_t>(20, 0x5251);
  350. ASSERT_TRUE(pkt1->rawPack());
  351. ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
  352. EXPECT_EQ(0x52, pkt1->getBuffer()[20]);
  353. EXPECT_EQ(0x51, pkt1->getBuffer()[21]);
  354. // Write 4 octets (0x54535251) at position 30.
  355. pkt1->writeValueAt<uint32_t>(30, 0x54535251);
  356. ASSERT_TRUE(pkt1->rawPack());
  357. ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
  358. EXPECT_EQ(0x54, pkt1->getBuffer()[30]);
  359. EXPECT_EQ(0x53, pkt1->getBuffer()[31]);
  360. EXPECT_EQ(0x52, pkt1->getBuffer()[32]);
  361. EXPECT_EQ(0x51, pkt1->getBuffer()[33]);
  362. }
  363. }