// Copyright (C) 2011-2012 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_pkt4.h" using namespace std; using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::perfdhcp; typedef PerfPkt4::LocalizedOptionPtr LocalizedOptionPtr; namespace { // a sample data const uint8_t dummyOp = BOOTREQUEST; const uint8_t dummyHtype = 6; const uint8_t dummyHlen = 6; const uint8_t dummyHops = 13; const uint32_t dummyTransid = 0x12345678; const uint16_t dummySecs = 42; const uint16_t dummyFlags = BOOTP_BROADCAST; const IOAddress dummyCiaddr("192.0.2.1"); const IOAddress dummyYiaddr("1.2.3.4"); const IOAddress dummySiaddr("192.0.2.255"); const IOAddress dummyGiaddr("255.255.255.255"); // a dummy MAC address const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5}; // a dummy MAC address, padded with 0s const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // let's use some creative test content here (128 chars + \0) const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur " "adipiscing elit. Proin mollis placerat metus, at " "lacinia orci ornare vitae. Mauris amet."; // yet another type of test content (64 chars + \0) const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur " "adipiscing elit posuere."; class PerfPkt4Test : public ::testing::Test { public: PerfPkt4Test() { } /// \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 std::vector capture() { // That is only part of the header. It contains all "short" fields, // larger fields are constructed separately. uint8_t hdr[] = { 1, 6, 6, 13, // op, htype, hlen, hops, 0x12, 0x34, 0x56, 0x78, // transaction-id 0, 42, 0x80, 0x00, // 42 secs, BROADCAST flags 192, 0, 2, 1, // ciaddr 1, 2, 3, 4, // yiaddr 192, 0, 2, 255, // siaddr 255, 255, 255, 255, // giaddr }; uint8_t v4Opts[] = { 12, 3, 0, 1, 2, // Host name option. 13, 3, 10, 11, 12, // Boot file size option 14, 3, 20, 21, 22, // Merit dump file 53, 1, 1, // DHCP message type. 128, 3, 30, 31, 32, 254, 3, 40, 41, 42, }; // Initialize the vector with the header fields defined above. vector buf(hdr, hdr + sizeof(hdr)); // Append the large header fields. std::copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf)); std::copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf)); std::copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf)); // Append magic cookie. buf.push_back(0x63); buf.push_back(0x82); buf.push_back(0x53); buf.push_back(0x63); // Append options. std::copy(v4Opts, v4Opts + sizeof(v4Opts), back_inserter(buf)); return buf; } }; TEST_F(PerfPkt4Test, Constructor) { // Initialize some dummy payload. uint8_t data[250]; for (int i = 0; i < 250; i++) { data[i]=i; } // Test constructor to be used for incoming messages. // Use default (1) offset value and don't specify transaction id. boost::scoped_ptr pkt1(new PerfPkt4(data, sizeof(data), 1)); EXPECT_EQ(1, pkt1->getTransIdOffset()); // Test constructor to be used for outgoing messages. // Use non-zero offset and specify transaction id. boost::scoped_ptr pkt2(new PerfPkt4(data, sizeof(data), 10, 0x010203)); EXPECT_EQ(0x010203, pkt2->getTransid()); EXPECT_EQ(10, pkt2->getTransIdOffset()); } TEST_F(PerfPkt4Test, RawPack) { // Create new packet. std::vector buf = capture(); boost::scoped_ptr pkt(new PerfPkt4(&buf[0], buf.size(), 0x1, 1)); // Initialize options data. uint8_t buf_hostname[] = { 12, 3, 4, 5, 6 }; uint8_t buf_boot_filesize[] = { 13, 3, 1, 2, 3 }; OptionBuffer vec_hostname(buf_hostname + 2, buf_hostname + 5); OptionBuffer vec_boot_filesize(buf_boot_filesize + 2, buf_boot_filesize + 5); // Create options objects. LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4, DHO_HOST_NAME, vec_hostname, 240)); LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4, DHO_BOOT_SIZE, vec_boot_filesize, 245)); // Try to add options to packet. ASSERT_NO_THROW(pkt->addOption(pkt_boot_filesize)); ASSERT_NO_THROW(pkt->addOption(pkt_hostname)); // We have valid options addedwith valid offsets so // pack operation should succeed. ASSERT_TRUE(pkt->rawPack()); // Buffer should now contain new values of DHO_HOST_NAME and // DHO_BOOT_SIZE options. util::OutputBuffer pkt_output = pkt->getBuffer(); ASSERT_EQ(buf.size(), pkt_output.getLength()); const uint8_t* out_buf_data = static_cast(pkt_output.getData()); // Check if options we read from buffer is valid. EXPECT_EQ(0, memcmp(buf_hostname, out_buf_data + 240, 5)); EXPECT_EQ(0, memcmp(buf_boot_filesize, out_buf_data + 245, 5)); } TEST_F(PerfPkt4Test, RawUnpack) { // Create new packet. std::vector buf = capture(); boost::scoped_ptr pkt(new PerfPkt4(&buf[0], buf.size(), 0x1, 1)); // Create options (existing in the packet) and specify their offsets. LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4, DHO_MERIT_DUMP, OptionBuffer(), 250)); LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4, DHO_DHCP_MESSAGE_TYPE, OptionBuffer(), 255)); // Addition should be successful ASSERT_NO_THROW(pkt->addOption(opt_merit)); ASSERT_NO_THROW(pkt->addOption(opt_msg_type)); // Option fit to packet boundaries and offsets are valid, // so this should unpack successfully. ASSERT_TRUE(pkt->rawUnpack()); // At this point we should have updated options data (read from buffer). // Let's try to retrieve them. opt_merit = boost::dynamic_pointer_cast(pkt->getOption(DHO_MERIT_DUMP)); opt_msg_type = boost::dynamic_pointer_cast(pkt->getOption(DHO_DHCP_MESSAGE_TYPE)); ASSERT_TRUE(opt_merit); ASSERT_TRUE(opt_msg_type); // Get first option payload. OptionBuffer opt_merit_data = opt_merit->getData(); // Define reference data. uint8_t buf_merit[] = { 20, 21, 22 }; // Validate first option data. ASSERT_EQ(sizeof(buf_merit), opt_merit_data.size()); EXPECT_TRUE(std::equal(opt_merit_data.begin(), opt_merit_data.end(), buf_merit)); // Get second option payload. OptionBuffer opt_msg_type_data = opt_msg_type->getData(); // Expect one byte of message type payload. ASSERT_EQ(1, opt_msg_type_data.size()); EXPECT_EQ(1, opt_msg_type_data[0]); } /* TEST_F(PerfPkt4Test, 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) LocalizedOptionPtr pkt1_serverid(new LocalizedOption(Option::V6, D6O_SERVERID, vec_server_id, 150)); 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, 85)); pkt2->addOption(pkt2_serverid); // Pack must fail due to invalid offset. EXPECT_FALSE(pkt2->rawPack()); } TEST_F(PerfPkt4Test, 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); LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6, D6O_CLIENTID, vec_duid, 4)); 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(PerfPkt4Test, PackTransactionId) { uint8_t data[100] = { 0 }; // Create dummy packet that is simply filled with zeros. boost::scoped_ptr pkt1(new PerfPkt4(data, sizeof(data), 0x010203, PerfPkt4::Offset(50))); // 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 + 50, ref_data, 3)); // Out of bounds transaction id offset. boost::scoped_ptr pkt2(new PerfPkt4(data, sizeof(data), 0x010202, PerfPkt4::Offset(100))); cout << "Testing out of bounds offset. " "This may produce spurious errors ..." << endl; EXPECT_FALSE(pkt2->rawPack()); } TEST_F(PerfPkt4Test, UnpackTransactionId) { // Initialize data for dummy packet (zeros only). uint8_t data[100] = { 0 }; // Generate transaction id = 0x010203 and inject at offset = 50. for (int i = 50; i < 53; ++i) { data[i] = i - 49; } // Create packet and point out that transaction id is at offset 50. boost::scoped_ptr pkt1(new PerfPkt4(data, sizeof(data), PerfPkt4::Offset(50))); // 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 PerfPkt4(data, sizeof(data), PerfPkt4::Offset(300))); cout << "Testing out of bounds offset. " "This may produce spurious errors ..." << endl; EXPECT_FALSE(pkt2->rawUnpack()); } */ }