|
@@ -0,0 +1,344 @@
|
|
|
+// 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 <config.h>
|
|
|
+#include <iostream>
|
|
|
+#include <sstream>
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <gtest/gtest.h>
|
|
|
+
|
|
|
+#include <asiolink/io_address.h>
|
|
|
+#include <dhcp/option.h>
|
|
|
+#include <dhcp/dhcp4.h>
|
|
|
+
|
|
|
+#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<uint8_t> 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<uint8_t> 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<PerfPkt4> 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<PerfPkt4> 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<uint8_t> buf = capture();
|
|
|
+ boost::scoped_ptr<PerfPkt4> 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<const uint8_t*>(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<uint8_t> buf = capture();
|
|
|
+ boost::scoped_ptr<PerfPkt4> 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<LocalizedOption>(pkt->getOption(DHO_MERIT_DUMP));
|
|
|
+ opt_msg_type = boost::dynamic_pointer_cast<LocalizedOption>(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<PerfPkt4> 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<PerfPkt4> 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<PerfPkt4> 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<PerfPkt4> 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<const uint8_t*>
|
|
|
+ (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<PerfPkt4> 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<PerfPkt4> 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<PerfPkt4> 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());
|
|
|
+
|
|
|
+ } */
|
|
|
+
|
|
|
+}
|