// Copyright (C) 2012, 2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. #include #include #include #include #include #include #include #include #include "../localized_option.h" #include "../perf_pkt6.h" using namespace std; using namespace isc; using namespace isc::dhcp; using namespace isc::perfdhcp; typedef PerfPkt6::LocalizedOptionPtr LocalizedOptionPtr; namespace { class PerfPkt6Test : public ::testing::Test { public: PerfPkt6Test() { } /// \brief Returns captured SOLICIT packet. /// /// Captured SOLICIT packet with transid=0x3d79fb and options: client-id, /// in_na, dns-server, elapsed-time, option-request /// This code was autogenerated /// (see src/bin/dhcp6/tests/iface_mgr_unittest.c), /// but we spent some time to make is less ugly than it used to be. /// /// \return pointer to Pkt6 that represents received SOLICIT PerfPkt6* capture() { uint8_t data[98]; data[0] = 1; data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0; data[5] = 1; data[6] = 0; data[7] = 14; data[8] = 0; data[9] = 1; data[10] = 0; data[11] = 1; data[12] = 21; data[13] = 158; data[14] = 60; data[15] = 22; data[16] = 0; data[17] = 30; data[18] = 140; data[19] = 155; data[20] = 115; data[21] = 73; data[22] = 0; data[23] = 3; data[24] = 0; data[25] = 40; data[26] = 0; data[27] = 0; data[28] = 0; data[29] = 1; data[30] = 255; data[31] = 255; data[32] = 255; data[33] = 255; data[34] = 255; data[35] = 255; data[36] = 255; data[37] = 255; data[38] = 0; data[39] = 5; data[40] = 0; data[41] = 24; data[42] = 32; data[43] = 1; data[44] = 13; data[45] = 184; data[46] = 0; data[47] = 1; data[48] = 0; data[49] = 0; data[50] = 0; data[51] = 0; data[52] = 0; data[53] = 0; data[54] = 0; data[55] = 0; data[56] = 18; data[57] = 52; data[58] = 255; data[59] = 255; data[60] = 255; data[61] = 255; data[62] = 255; data[63] = 255; data[64] = 255; data[65] = 255; data[66] = 0; data[67] = 23; data[68] = 0; data[69] = 16; data[70] = 32; data[71] = 1; data[72] = 13; data[73] = 184; data[74] = 0; data[75] = 1; data[76] = 0; data[77] = 0; data[78] = 0; data[79] = 0; data[80] = 0; data[81] = 0; data[82] = 0; data[83] = 0; data[84] = 221; data[85] = 221; data[86] = 0; data[87] = 8; data[88] = 0; data[89] = 2; data[90] = 0; data[91] = 100; data[92] = 0; data[93] = 6; data[94] = 0; data[95] = 2; data[96] = 0; data[97] = 23; PerfPkt6* pkt = new PerfPkt6(data, sizeof(data)); return (pkt); } /// \brief Returns truncated SOLICIT packet. /// /// Returns truncated SOLICIT packet which will be used for /// negative tests: e.g. pack options out of packet. /// /// \return pointer to Pkt6 that represents truncated SOLICIT PerfPkt6* captureTruncated() { uint8_t data[17]; data[0] = 1; data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0; data[5] = 1; data[6] = 0; data[7] = 14; data[8] = 0; data[9] = 1; data[10] = 0; data[11] = 1; data[12] = 21; data[13] = 158; data[14] = 60; data[15] = 22; data[16] = 0; PerfPkt6* pkt = new PerfPkt6(data, sizeof(data)); return (pkt); } }; TEST_F(PerfPkt6Test, Constructor) { // Data to be used to create packet. uint8_t data[] = { 0, 1, 2, 3, 4, 5 }; // Test constructor to be used for incoming messages. // Use default (1) offset value and don't specify transaction id. boost::scoped_ptr pkt1(new PerfPkt6(data, sizeof(data))); EXPECT_EQ(sizeof(data), pkt1->data_.size()); EXPECT_EQ(0, memcmp(&pkt1->data_[0], data, sizeof(data))); EXPECT_EQ(1, pkt1->getTransidOffset()); // Test constructor to be used for outgoing messages. // Use non-zero offset and specify transaction id. const size_t offset_transid = 10; const uint32_t transid = 0x010203; boost::scoped_ptr pkt2(new PerfPkt6(data, sizeof(data), offset_transid, transid)); EXPECT_EQ(sizeof(data), pkt2->data_.size()); EXPECT_EQ(0, memcmp(&pkt2->data_[0], data, sizeof(data))); EXPECT_EQ(0x010203, pkt2->getTransid()); EXPECT_EQ(10, pkt2->getTransidOffset()); } TEST_F(PerfPkt6Test, RawPackUnpack) { // Create first packet. boost::scoped_ptr pkt1(capture()); // Create some input buffers to initialize options. uint8_t buf_elapsed_time[] = { 1, 1 }; uint8_t buf_duid[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; // Create options. const size_t offset_elapsed_time = 86; OptionBuffer vec_elapsed_time(buf_elapsed_time, buf_elapsed_time + sizeof(buf_elapsed_time)); LocalizedOptionPtr pkt1_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME, vec_elapsed_time, offset_elapsed_time)); const size_t offset_duid = 4; OptionBuffer vec_duid(buf_duid, buf_duid + sizeof(buf_duid)); LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6, D6O_CLIENTID, vec_duid, offset_duid)); // Add option to packet and create on-wire format from added options. // Contents of options will override contents of packet buffer. ASSERT_NO_THROW(pkt1->addOption(pkt1_elapsed_time)); ASSERT_NO_THROW(pkt1->addOption(pkt1_duid)); ASSERT_TRUE(pkt1->rawPack()); // Reset so as we can reuse them for another packet. vec_elapsed_time.clear(); vec_duid.clear(); // Get output buffer from packet 1 to create new packet // that will be later validated. util::OutputBuffer pkt1_output = pkt1->getBuffer(); ASSERT_EQ(pkt1_output.getLength(), pkt1->data_.size()); const uint8_t* pkt1_output_data = static_cast (pkt1_output.getData()); boost::scoped_ptr pkt2(new PerfPkt6(pkt1_output_data, pkt1_output.getLength())); // Create objects specifying options offset in a packet. // Offsets will inform pkt2 object where to read data from. LocalizedOptionPtr pkt2_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME, vec_elapsed_time, offset_elapsed_time)); LocalizedOptionPtr pkt2_duid(new LocalizedOption(Option::V6, D6O_CLIENTID, vec_duid, offset_duid)); // Add options to packet to pass their offsets. pkt2->addOption(pkt2_elapsed_time); pkt2->addOption(pkt2_duid); // Unpack: get relevant parts of buffer data into option objects. ASSERT_TRUE(pkt2->rawUnpack()); // Once option data is stored in options objects we pull it out. pkt2_elapsed_time = boost::dynamic_pointer_cast (pkt2->getOption(D6O_ELAPSED_TIME)); pkt2_duid = boost::dynamic_pointer_cast (pkt2->getOption(D6O_CLIENTID)); // Check if options are present. They have to be there since // we have added them ourselfs. ASSERT_TRUE(pkt2_elapsed_time); ASSERT_TRUE(pkt2_duid); // Expecting option contents be the same as original. OptionBuffer pkt2_elapsed_time_data = pkt2_elapsed_time->getData(); OptionBuffer pkt2_duid_data = pkt2_duid->getData(); EXPECT_EQ(0x0101, pkt2_elapsed_time->getUint16()); EXPECT_TRUE(std::equal(pkt2_duid_data.begin(), pkt2_duid_data.end(), buf_duid)); } TEST_F(PerfPkt6Test, InvalidOptions) { // Create packet. boost::scoped_ptr pkt1(capture()); OptionBuffer vec_server_id; vec_server_id.resize(10); // Testing invalid offset of the option (greater than packet size) const size_t offset_serverid[] = { 150, 85 }; LocalizedOptionPtr pkt1_serverid(new LocalizedOption(Option::V6, D6O_SERVERID, vec_server_id, offset_serverid[0])); pkt1->addOption(pkt1_serverid); // Pack has to fail due to invalid offset. EXPECT_FALSE(pkt1->rawPack()); // Create packet. boost::scoped_ptr pkt2(capture()); // Testing offset of the option (lower than pakcet size but // tail of the option out of bounds). LocalizedOptionPtr pkt2_serverid(new LocalizedOption(Option::V6, D6O_SERVERID, vec_server_id, offset_serverid[1])); pkt2->addOption(pkt2_serverid); // Pack must fail due to invalid offset. EXPECT_FALSE(pkt2->rawPack()); } TEST_F(PerfPkt6Test, TruncatedPacket) { cout << "Testing parsing options from truncated packet." << "This may produce spurious errors" << endl; // Create truncated (in the middle of duid options) boost::scoped_ptr pkt1(captureTruncated()); OptionBuffer vec_duid; vec_duid.resize(30); const size_t offset_duid = 4; LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6, D6O_CLIENTID, vec_duid, offset_duid)); pkt1->addOption(pkt1_duid); // Pack/unpack must fail because length of the option read from buffer // will extend over the actual packet length. EXPECT_FALSE(pkt1->rawUnpack()); EXPECT_FALSE(pkt1->rawPack()); } TEST_F(PerfPkt6Test, PackTransactionId) { uint8_t data[100]; memset(&data, 0, sizeof(data)); const size_t offset_transid[] = { 50, 100 }; const uint32_t transid = 0x010203; // Create dummy packet that is simply filled with zeros. boost::scoped_ptr pkt1(new PerfPkt6(data, sizeof(data), offset_transid[0], transid)); // Reference data are non zero so we can detect them in dummy packet. uint8_t ref_data[3] = { 1, 2, 3 }; // This will store given transaction id in the packet data at // offset of 50. ASSERT_TRUE(pkt1->rawPack()); // Get the output buffer so we can validate it. util::OutputBuffer out_buf = pkt1->getBuffer(); ASSERT_EQ(sizeof(data), out_buf.getLength()); const uint8_t *out_buf_data = static_cast (out_buf.getData()); // Validate transaction id. EXPECT_EQ(0, memcmp(out_buf_data + offset_transid[0], ref_data, 3)); // Out of bounds transaction id offset. boost::scoped_ptr pkt2(new PerfPkt6(data, sizeof(data), offset_transid[1], transid)); cout << "Testing out of bounds offset. " "This may produce spurious errors ..." << endl; EXPECT_FALSE(pkt2->rawPack()); } TEST_F(PerfPkt6Test, UnpackTransactionId) { // Initialize data for dummy packet (zeros only). uint8_t data[100] = { 0 }; // Generate transaction id = 0x010203 and inject at offset = 50. for (uint8_t i = 50; i < 53; ++i) { data[i] = i - 49; } // Create packet and point out that transaction id is at offset 50. const size_t offset_transid[] = { 50, 300 }; boost::scoped_ptr pkt1(new PerfPkt6(data, sizeof(data), offset_transid[0])); // Get transaction id out of buffer and store in class member. ASSERT_TRUE(pkt1->rawUnpack()); // Test value of transaction id. EXPECT_EQ(0x010203, pkt1->getTransid()); // Out of bounds transaction id offset. boost::scoped_ptr pkt2(new PerfPkt6(data, sizeof(data), offset_transid[1])); cout << "Testing out of bounds offset. " "This may produce spurious errors ..." << endl; EXPECT_FALSE(pkt2->rawUnpack()); } }