perf_pkt4_unittest.cc 14 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// 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 {

38
// A dummy MAC address, padded with 0s
39 40 41
const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
                                 0, 0, 0, 0, 0, 0, 0, 0 };

42
// Let's use some creative test content here (128 chars + \0)
43 44 45 46
const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
    "adipiscing elit. Proin mollis placerat metus, at "
    "lacinia orci ornare vitae. Mauris amet.";

47
// Yet another type of test content (64 chars + \0)
48 49 50 51 52 53 54 55
const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
    "adipiscing elit posuere.";

class PerfPkt4Test : public ::testing::Test {
public:
    PerfPkt4Test() {
    }

56
    /// \brief Returns buffer with sample DHCPDISCOVER message.
57
    ///
58 59 60
    /// This method creates buffer containing on-wire data of
    /// DHCPDICOSVER message. This buffer is used by tests below
    /// to create DHCPv4 test packets.
61
    ///
62 63
    /// \return vector containing on-wire data
    std::vector<uint8_t>& capture() {
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

        // 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
81 82
            53, 1, 1,           // DHCP message type.
            128, 3, 30, 31, 32,
83 84 85 86
            254, 3, 40, 41, 42,
        };

        // Initialize the vector with the header fields defined above.
87 88 89 90
        static std::vector<uint8_t> buf(hdr, hdr + sizeof(hdr));

        // If this is a first call to this function. Initialize
        // remaining data.
91
        if (buf.size() == sizeof(hdr)) {
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109

            // 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));
        }
110 111 112 113 114 115 116 117
        return buf;
    }
};

TEST_F(PerfPkt4Test, Constructor) {
    // Initialize some dummy payload.
    uint8_t data[250];
    for (int i = 0; i < 250; i++) {
118
        data[i] = i;
119 120 121 122 123 124 125 126 127
    }

    // 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.
128 129
    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data),
                                                  10, 0x010203));
130 131
    EXPECT_EQ(0x010203, pkt2->getTransid());
    EXPECT_EQ(10, pkt2->getTransIdOffset());
132 133 134 135

    // Test default constructor. Transaction id offset is expected to be 1.
    boost::scoped_ptr<PerfPkt4> pkt3(new PerfPkt4(data, sizeof(data)));
    EXPECT_EQ(1, pkt3->getTransIdOffset());
136 137 138 139 140
}

TEST_F(PerfPkt4Test, RawPack) {
    // Create new packet.
    std::vector<uint8_t> buf = capture();
141
    boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
142 143 144 145 146

    // 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);
147 148
    OptionBuffer vec_boot_filesize(buf_boot_filesize + 2,
                                   buf_boot_filesize + 5);
149 150

    // Create options objects.
151
    LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4,
152 153 154
                                                        DHO_HOST_NAME,
                                                        vec_hostname,
                                                        240));
155
    LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4,
156 157 158 159 160 161 162 163
                                                             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));

164
    // We have valid options addedwith valid offsets so
165 166 167 168 169 170 171
    // 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());
172 173
    const uint8_t* out_buf_data =
        static_cast<const uint8_t*>(pkt_output.getData());
174 175 176 177 178 179 180 181 182

    // 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();
183
    boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
184 185 186 187 188 189

    // Create options (existing in the packet) and specify their offsets.
    LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
                                                     DHO_MERIT_DUMP,
                                                     OptionBuffer(),
                                                     250));
190

191 192
    LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
                                                        DHO_DHCP_MESSAGE_TYPE,
193
                                                        OptionBuffer(),
194 195 196 197
                                                        255));
    // Addition should be successful
    ASSERT_NO_THROW(pkt->addOption(opt_merit));
    ASSERT_NO_THROW(pkt->addOption(opt_msg_type));
198

199 200 201 202 203 204
    // 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.
205 206 207 208
    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));
209 210 211 212 213 214
    ASSERT_TRUE(opt_merit);
    ASSERT_TRUE(opt_msg_type);

    // Get first option payload.
    OptionBuffer opt_merit_data = opt_merit->getData();

215 216
    // Define reference data.
    uint8_t buf_merit[] = { 20, 21, 22 };
217 218 219

    // Validate first option data.
    ASSERT_EQ(sizeof(buf_merit), opt_merit_data.size());
220 221 222 223
    EXPECT_TRUE(std::equal(opt_merit_data.begin(),
                           opt_merit_data.end(),
                           buf_merit));

224 225 226 227 228 229 230 231 232
    // 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) {
233 234 235
    // Create new packet.
    std::vector<uint8_t> buf = capture();
    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size()));
236

237 238 239 240 241 242 243
    // Create option with invalid offset.
    // This option is at offset 250 (not 251).
    LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
                                                     DHO_MERIT_DUMP,
                                                     OptionBuffer(),
                                                     251));
    ASSERT_NO_THROW(pkt1->addOption(opt_merit));
244

245 246
    cout << "Testing unpack of invalid options. "
         << "This may produce spurious errors." << endl;
247

248 249 250 251 252
    // Unpack is expected to fail because it is supposed to read
    // option type from buffer and match it with DHO_MERIT_DUMP.
    // It will not match because option is shifted by on byte.
    ASSERT_FALSE(pkt1->rawUnpack());

253
    // Create another packet.
254 255
    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size()));

256
    // Create DHO_DHCP_MESSAGE_TYPE option that has the wrong offset.
257 258 259 260 261 262 263 264 265 266 267
    // With this offset, option goes beyond packet size (268).
    LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
                                                        DHO_DHCP_MESSAGE_TYPE,
                                                        OptionBuffer(1, 2),
                                                        266));
    // Adding option is expected to be successful because no
    // offset validation takes place at this point.
    ASSERT_NO_THROW(pkt2->addOption(opt_msg_type));

    // This is expected to fail because option is out of bounds.
    ASSERT_FALSE(pkt2->rawPack());
268 269
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283
TEST_F(PerfPkt4Test, TruncatedPacket) {
    // Get the whole packet and truncate it to 249 bytes.
    std::vector<uint8_t> buf = capture();
    buf.resize(249);
    boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));

    // Option DHO_BOOT_SIZE is now truncated because whole packet
    // is truncated. This option ends at 249 while last index of
    // truncated packet is now 248.
    LocalizedOptionPtr opt_boot_filesize(new LocalizedOption(Option::V4,
                                                             DHO_BOOT_SIZE,
                                                             OptionBuffer(3, 1),
                                                             245));
    ASSERT_NO_THROW(pkt->addOption(opt_boot_filesize));
284

285 286
    cout << "Testing pack and unpack of options in truncated "
         << "packet. This may produce spurious errors." << endl;
287

288 289 290 291 292
    // Both pack and unpack are expected to fail because
    // added option is out of bounds.
    EXPECT_FALSE(pkt->rawUnpack());
    EXPECT_FALSE(pkt->rawPack());
}
293

294 295 296 297 298 299 300 301 302
TEST_F(PerfPkt4Test, PackTransactionId) {
    // Create dummy packet that consists of zeros.
    std::vector<uint8_t> buf(268, 0);
    // Initialize transaction id 0x00000102 at offset 10.
    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size(),
                                                  10, 0x0102));

    // Pack will inject transaction id at offset 10 into the
    // packet buffer.
303 304
    ASSERT_TRUE(pkt1->rawPack());

305
    // Get packet's output buffer and make sure it has valid size.
306
    util::OutputBuffer out_buf = pkt1->getBuffer();
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    ASSERT_EQ(buf.size(), out_buf.getLength());
    const uint8_t *out_buf_data =
        static_cast<const uint8_t*>(out_buf.getData());

    // Initialize reference data for transaction id.
    const uint8_t ref_data[] = { 0, 0, 1, 2 };

    // Expect that reference transaction id matches what we have
    // read from buffer.
    EXPECT_EQ(0, memcmp(ref_data, out_buf_data + 10, 4));

    cout << "Testing pack with invalid transaction id offset. "
         << "This may produce spurious errors" << endl;

    // Create packet with invalid transaction id offset.
    // Packet length is 268, transaction id is 4 bytes long so last byte of
    // transaction id is out of bounds.
    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size(),
                                                  265, 0x0102));
326 327 328 329
    EXPECT_FALSE(pkt2->rawPack());
}

TEST_F(PerfPkt4Test, UnpackTransactionId) {
330 331
    // Initialize packet data, lebgth 268, zeros only.
    std::vector<uint8_t> in_data(268, 0);
332

333 334 335 336
    // Assume that transaction id is at offset 100.
    // Fill 4 bytes at offset 100 with dummy transaction id.
    for (int i = 100; i < 104; ++i) {
        in_data[i] = i - 99;
337 338
    }

339 340 341 342
    // Create packet from initialized buffer.
    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
                                                  in_data.size(),
                                                  100));
343 344
    ASSERT_TRUE(pkt1->rawUnpack());

345 346 347 348 349 350 351 352
    // Get unpacked transaction id and compare with reference.
    EXPECT_EQ(0x01020304, pkt1->getTransid());

    // Create packet with transaction id at invalid offset.
    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&in_data[0],
                                                  in_data.size(),
                                                  270));

353
    cout << "Testing unpack of transaction id at invalid offset. "
354 355 356 357 358 359
         << "This may produce spurious errors." << endl;

    // Unpack is supposed to fail because transaction id is at
    // out of bounds offset.
    EXPECT_FALSE(pkt2->rawUnpack());
}
360 361

}