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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
// 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 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() {
    }

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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
        static std::vector<uint8_t> buf(hdr, hdr + sizeof(hdr));

        // If this is a first call to this function. Initialize
        // remaining data.
        if (buf.size() == 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));
        }
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
        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.
129 130
    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data),
                                                  10, 0x010203));
131 132
    EXPECT_EQ(0x010203, pkt2->getTransid());
    EXPECT_EQ(10, pkt2->getTransIdOffset());
133 134 135 136

    // 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());
137 138 139 140 141
}

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

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

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

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

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

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

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

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

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

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

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

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

238 239 240 241 242 243 244
    // 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));
245

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

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    // 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());

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

    // Create DHO_DHCP_MESSAGE_TYPE option that has wrong offset.
    // 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());
269 270
}

271 272 273 274 275 276 277 278 279 280 281 282 283 284
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));
285

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

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

295 296 297 298 299 300 301 302 303
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.
304 305
    ASSERT_TRUE(pkt1->rawPack());

306
    // Get packet's output buffer and make sure it has valid size.
307
    util::OutputBuffer out_buf = pkt1->getBuffer();
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    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));
327 328 329 330
    EXPECT_FALSE(pkt2->rawPack());
}

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

334 335 336 337
    // 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;
338 339
    }

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

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    // 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));

    cout << "Testing unpack of transaction id at invalid offset."
         << "This may produce spurious errors." << endl;

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

}