pkt4_unittest.cc 21.3 KB
Newer Older
1
// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15
//
// 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>
16

17 18
#include <asiolink/io_address.h>
#include <dhcp/dhcp4.h>
19
#include <dhcp/pkt4.h>
20
#include <exceptions/exceptions.h>
21 22 23 24 25 26 27 28 29 30 31
#include <util/buffer.h>

#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/static_assert.hpp>
#include <gtest/gtest.h>

#include <iostream>
#include <sstream>

#include <arpa/inet.h>
32 33 34 35 36

using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
37
using namespace isc::util;
38 39 40
// don't import the entire boost namespace.  It will unexpectedly hide uint8_t
// for some systems.
using boost::scoped_ptr;
41 42 43 44 45

namespace {

TEST(Pkt4Test, constructor) {

46
    ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
47
    Pkt4* pkt = 0;
48

49
    // Just some dummy payload.
50
    uint8_t testData[250];
51
    for (int i = 0; i < 250; i++) {
52
        testData[i]=i;
53
    }
54

55
    // Positive case1. Normal received packet.
56
    EXPECT_NO_THROW(
57
        pkt = new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN);
58 59
    );

60
    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
61 62 63 64 65 66

    EXPECT_NO_THROW(
        delete pkt;
        pkt = 0;
    );

67
    // Positive case2. Normal outgoing packet.
68 69 70 71 72
    EXPECT_NO_THROW(
        pkt = new Pkt4(DHCPDISCOVER, 0xffffffff);
    );

    // DHCPv4 packet must be at least 236 bytes long
73
    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
74 75 76 77 78 79 80
    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
    EXPECT_EQ(0xffffffff, pkt->getTransid());
    EXPECT_NO_THROW(
        delete pkt;
        pkt = 0;
    );

81
    // Negative case. Should drop truncated messages.
82
    EXPECT_THROW(
83
        pkt = new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN-1),
84 85 86
        OutOfRange
    );
    if (pkt) {
87 88
        // Test failed. Exception should have been thrown, but
        // object was created instead. Let's clean this up.
89
        delete pkt;
90
        pkt = 0;
91 92 93
    }
}

94
// a sample data
Tomek Mrugalski's avatar
Tomek Mrugalski committed
95 96 97 98 99 100 101 102 103 104 105 106
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");
107 108 109 110 111

// a dummy MAC address
const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5};

// a dummy MAC address, padded with 0s
112
const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
113 114 115 116 117 118
                                 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.";
119

120 121 122 123 124 125 126
// yet another type of test content (64 chars + \0)
const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
    "adipiscing elit posuere.";

BOOST_STATIC_ASSERT(sizeof(dummyFile)  == Pkt4::MAX_FILE_LEN + 1);
BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);

127
/// @brief Generates test packet.
128 129 130 131 132 133 134 135 136 137 138 139 140
///
/// Allocates and generates test packet, with all fixed
/// fields set to non-zero values. Content is not always
/// reasonable.
///
/// See generateTestPacket2() function that returns
/// exactly the same packet in on-wire format.
///
/// @return pointer to allocated Pkt4 object.
boost::shared_ptr<Pkt4>
generateTestPacket1() {

    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
141 142 143 144

    vector<uint8_t> vectorMacAddr(dummyMacAddr, dummyMacAddr
                                  +sizeof(dummyMacAddr));

145
    // hwType = 6(ETHERNET), hlen = 6(MAC address len)
146 147
    pkt->setHWAddr(dummyHtype, dummyHlen, vectorMacAddr);
    pkt->setHops(dummyHops); // 13 relays. Wow!
148
    // Transaction-id is already set.
149 150 151 152 153 154
    pkt->setSecs(dummySecs);
    pkt->setFlags(dummyFlags); // all flags set
    pkt->setCiaddr(dummyCiaddr);
    pkt->setYiaddr(dummyYiaddr);
    pkt->setSiaddr(dummySiaddr);
    pkt->setGiaddr(dummyGiaddr);
155
    // Chaddr already set with setHWAddr().
156
    pkt->setSname(dummySname, 64);
157 158 159 160 161
    pkt->setFile(dummyFile, 128);

    return (pkt);
}

162
/// @brief Generates test packet.
163 164
///
/// Allocates and generates on-wire buffer that represents
165
/// test packet, with all fixed fields set to non-zero values.
166 167 168 169 170 171
/// Content is not always reasonable.
///
/// See generateTestPacket1() function that returns
/// exactly the same packet as Pkt4 object.
///
/// @return pointer to allocated Pkt4 object
172 173
// Returns a vector containing a DHCPv4 packet header.
vector<uint8_t>
174 175
generateTestPacket2() {

176 177
    // That is only part of the header. It contains all "short" fields,
    // larger fields are constructed separately.
178
    uint8_t hdr[] = {
179
        1, 6, 6, 13,            // op, htype, hlen, hops,
180
        0x12, 0x34, 0x56, 0x78, // transaction-id
181
        0, 42, 0x80, 0x00,      // 42 secs, BROADCAST flags
182 183 184 185
        192, 0, 2, 1,           // ciaddr
        1, 2, 3, 4,             // yiaddr
        192, 0, 2, 255,         // siaddr
        255, 255, 255, 255,     // giaddr
186 187
    };

188 189
    // Initialize the vector with the header fields defined above.
    vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
190

191
    // Append the large header fields.
192
    copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
193 194
    copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
    copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
195

196 197 198 199
    // Should now have all the header, so check.  The "static_cast" is used
    // to get round an odd bug whereby the linker appears not to find the
    // definition of DHCPV4_PKT_HDR_LEN if it appears within an EXPECT_EQ().
    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), buf.size());
200 201 202 203 204 205

    return (buf);
}

TEST(Pkt4Test, fixedFields) {

206
    boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
207 208

    // ok, let's check packet values
209 210 211 212
    EXPECT_EQ(dummyOp, pkt->getOp());
    EXPECT_EQ(dummyHtype, pkt->getHtype());
    EXPECT_EQ(dummyHlen, pkt->getHlen());
    EXPECT_EQ(dummyHops, pkt->getHops());
213
    EXPECT_EQ(dummyTransid, pkt->getTransid());
214 215
    EXPECT_EQ(dummySecs, pkt->getSecs());
    EXPECT_EQ(dummyFlags, pkt->getFlags());
216

217 218 219 220
    EXPECT_EQ(dummyCiaddr.toText(), pkt->getCiaddr().toText());
    EXPECT_EQ(dummyYiaddr.toText(), pkt->getYiaddr().toText());
    EXPECT_EQ(dummySiaddr.toText(), pkt->getSiaddr().toText());
    EXPECT_EQ(dummyGiaddr.toText(), pkt->getGiaddr().toText());
221 222

    // chaddr is always 16 bytes long and contains link-layer addr (MAC)
223
    EXPECT_EQ(0, memcmp(dummyChaddr, &pkt->getHWAddr()->hwaddr_[0], 16));
224

225
    EXPECT_EQ(0, memcmp(dummySname, &pkt->getSname()[0], 64));
226

227
    EXPECT_EQ(0, memcmp(dummyFile, &pkt->getFile()[0], 128));
228 229 230 231 232

    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
}

TEST(Pkt4Test, fixedFieldsPack) {
233
    boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
234
    vector<uint8_t> expectedFormat = generateTestPacket2();
235 236 237 238 239

    EXPECT_NO_THROW(
        pkt->pack();
    );

240
    ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
241 242

    // redundant but MUCH easier for debug in gdb
Tomek Mrugalski's avatar
Tomek Mrugalski committed
243 244
    const uint8_t* exp = &expectedFormat[0];
    const uint8_t* got = static_cast<const uint8_t*>(pkt->getBuffer().getData());
245

246
    EXPECT_EQ(0, memcmp(exp, got, Pkt4::DHCPV4_PKT_HDR_LEN));
247 248 249 250
}

/// TODO Uncomment when ticket #1226 is implemented
TEST(Pkt4Test, fixedFieldsUnpack) {
251
    vector<uint8_t> expectedFormat = generateTestPacket2();
252

Tomek Mrugalski's avatar
Tomek Mrugalski committed
253 254 255 256 257 258 259 260 261
    expectedFormat.push_back(0x63); // magic cookie
    expectedFormat.push_back(0x82);
    expectedFormat.push_back(0x53);
    expectedFormat.push_back(0x63);

    expectedFormat.push_back(0x35); // message-type
    expectedFormat.push_back(0x1);
    expectedFormat.push_back(0x1);

262
    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
Tomek Mrugalski's avatar
Tomek Mrugalski committed
263 264
                                         expectedFormat.size()));;

265

266 267 268 269
    EXPECT_NO_THROW(
        pkt->unpack()
    );

270
    // ok, let's check packet values
271 272 273 274 275 276 277 278 279 280 281 282
    EXPECT_EQ(dummyOp, pkt->getOp());
    EXPECT_EQ(dummyHtype, pkt->getHtype());
    EXPECT_EQ(dummyHlen, pkt->getHlen());
    EXPECT_EQ(dummyHops, pkt->getHops());
    EXPECT_EQ(dummyTransid, pkt->getTransid());
    EXPECT_EQ(dummySecs, pkt->getSecs());
    EXPECT_EQ(dummyFlags, pkt->getFlags());

    EXPECT_EQ(dummyCiaddr.toText(), pkt->getCiaddr().toText());
    EXPECT_EQ(string("1.2.3.4"), pkt->getYiaddr().toText());
    EXPECT_EQ(string("192.0.2.255"), pkt->getSiaddr().toText());
    EXPECT_EQ(string("255.255.255.255"), pkt->getGiaddr().toText());
283 284

    // chaddr is always 16 bytes long and contains link-layer addr (MAC)
285
    EXPECT_EQ(0, memcmp(dummyChaddr, &pkt->getHWAddr()->hwaddr_[0], dummyHlen));
286

287
    ASSERT_EQ(static_cast<size_t>(Pkt4::MAX_SNAME_LEN), pkt->getSname().size());
288
    EXPECT_EQ(0, memcmp(dummySname, &pkt->getSname()[0], Pkt4::MAX_SNAME_LEN));
289

290
    ASSERT_EQ(static_cast<size_t>(Pkt4::MAX_FILE_LEN), pkt->getFile().size());
291
    EXPECT_EQ(0, memcmp(dummyFile, &pkt->getFile()[0], Pkt4::MAX_FILE_LEN));
292

293
    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
294 295 296 297 298
}

// this test is for hardware addresses (htype, hlen and chaddr fields)
TEST(Pkt4Test, hwAddr) {

299
    vector<uint8_t> mac;
300 301
    uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];

Tomek Mrugalski's avatar
Tomek Mrugalski committed
302 303 304
    // We resize vector to specified length. It is more natural for fixed-length
    // field, than clear it (shrink size to 0) and push_back each element
    // (growing length back to MAX_CHADDR_LEN).
305 306
    mac.resize(Pkt4::MAX_CHADDR_LEN);

307 308
    Pkt4* pkt = 0;
    // let's test each hlen, from 0 till 16
Tomek Mrugalski's avatar
Tomek Mrugalski committed
309 310
    for (int macLen = 0; macLen < Pkt4::MAX_CHADDR_LEN; macLen++) {
        for (int i = 0; i < Pkt4::MAX_CHADDR_LEN; i++) {
311 312 313
            mac[i] = 0;
            expectedChaddr[i] = 0;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
314 315 316
        for (int i = 0; i < macLen; i++) {
            mac[i] = 128 + i;
            expectedChaddr[i] = 128 + i;
317
        }
318

319 320 321 322 323
        // type and transaction doesn't matter in this test
        pkt = new Pkt4(DHCPOFFER, 1234);
        pkt->setHWAddr(255-macLen*10, // just weird htype
                       macLen,
                       mac);
324
        EXPECT_EQ(0, memcmp(expectedChaddr, &pkt->getHWAddr()->hwaddr_[0],
325 326
                            Pkt4::MAX_CHADDR_LEN));

327
        EXPECT_NO_THROW(
328 329 330 331
            pkt->pack();
        );

        // CHADDR starts at offset 28 in DHCP packet
332 333 334 335
        const uint8_t* ptr =
            static_cast<const uint8_t*>(pkt->getBuffer().getData())+28;

        EXPECT_EQ(0, memcmp(ptr, expectedChaddr, Pkt4::MAX_CHADDR_LEN));
336 337 338 339 340 341 342 343

        delete pkt;
    }

    /// TODO: extend this test once options support is implemented. HW address
    /// longer than 16 bytes should be stored in client-identifier option
}

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
TEST(Pkt4Test, msgTypes) {

    struct msgType {
        uint8_t dhcp;
        uint8_t bootp;
    };

    msgType types[] = {
        {DHCPDISCOVER, BOOTREQUEST},
        {DHCPOFFER, BOOTREPLY},
        {DHCPREQUEST, BOOTREQUEST},
        {DHCPDECLINE, BOOTREQUEST},
        {DHCPACK, BOOTREPLY},
        {DHCPNAK, BOOTREPLY},
        {DHCPRELEASE, BOOTREQUEST},
        {DHCPINFORM, BOOTREQUEST},
        {DHCPLEASEQUERY, BOOTREQUEST},
        {DHCPLEASEUNASSIGNED, BOOTREPLY},
        {DHCPLEASEUNKNOWN, BOOTREPLY},
        {DHCPLEASEACTIVE, BOOTREPLY}
    };

    Pkt4* pkt = 0;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
367
    for (int i = 0; i < sizeof(types) / sizeof(msgType); i++) {
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

        pkt = new Pkt4(types[i].dhcp, 0);
        EXPECT_EQ(types[i].dhcp, pkt->getType());

        EXPECT_EQ(types[i].bootp, pkt->getOp());

        delete pkt;
        pkt = 0;
    }

    EXPECT_THROW(
        pkt = new Pkt4(100, 0), // there's no message type 100
        OutOfRange
    );
    if (pkt) {
        delete pkt;
    }
}

387 388 389 390 391 392 393 394
// this test verifies handling of sname field
TEST(Pkt4Test, sname) {

    uint8_t sname[Pkt4::MAX_SNAME_LEN];

    Pkt4* pkt = 0;
    // let's test each sname length, from 0 till 64
    for (int snameLen=0; snameLen < Pkt4::MAX_SNAME_LEN; snameLen++) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
395
        for (int i = 0; i < Pkt4::MAX_SNAME_LEN; i++) {
396 397
            sname[i] = 0;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
398
        for (int i = 0; i < snameLen; i++) {
399 400
            sname[i] = i;
        }
401

402 403 404 405
        // type and transaction doesn't matter in this test
        pkt = new Pkt4(DHCPOFFER, 1234);
        pkt->setSname(sname, snameLen);

406
        EXPECT_EQ(0, memcmp(sname, &pkt->getSname()[0], Pkt4::MAX_SNAME_LEN));
407

408
        EXPECT_NO_THROW(
409 410 411 412
            pkt->pack();
        );

        // SNAME starts at offset 44 in DHCP packet
413 414 415
        const uint8_t* ptr =
            static_cast<const uint8_t*>(pkt->getBuffer().getData())+44;
        EXPECT_EQ(0, memcmp(ptr, sname, Pkt4::MAX_SNAME_LEN));
416 417 418

        delete pkt;
    }
419 420 421 422 423

    // Check that a null argument generates an exception.
    Pkt4 pkt4(DHCPOFFER, 1234);
    EXPECT_THROW(pkt4.setSname(NULL, Pkt4::MAX_SNAME_LEN), InvalidParameter);
    EXPECT_THROW(pkt4.setSname(NULL, 0), InvalidParameter);
424 425 426 427 428 429 430
}

TEST(Pkt4Test, file) {

    uint8_t file[Pkt4::MAX_FILE_LEN];

    Pkt4* pkt = 0;
431
    // Let's test each file length, from 0 till 128.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
432 433
    for (int fileLen = 0; fileLen < Pkt4::MAX_FILE_LEN; fileLen++) {
        for (int i = 0; i < Pkt4::MAX_FILE_LEN; i++) {
434 435
            file[i] = 0;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
436
        for (int i = 0; i < fileLen; i++) {
437 438
            file[i] = i;
        }
439

440
        // Type and transaction doesn't matter in this test.
441 442 443
        pkt = new Pkt4(DHCPOFFER, 1234);
        pkt->setFile(file, fileLen);

444
        EXPECT_EQ(0, memcmp(file, &pkt->getFile()[0], Pkt4::MAX_FILE_LEN));
445

446
        //
447
        EXPECT_NO_THROW(
448 449 450
            pkt->pack();
        );

451
        // FILE starts at offset 108 in DHCP packet.
452 453 454
        const uint8_t* ptr =
            static_cast<const uint8_t*>(pkt->getBuffer().getData())+108;
        EXPECT_EQ(0, memcmp(ptr, file, Pkt4::MAX_FILE_LEN));
455 456 457 458

        delete pkt;
    }

459 460 461 462
    // Check that a null argument generates an exception.
    Pkt4 pkt4(DHCPOFFER, 1234);
    EXPECT_THROW(pkt4.setFile(NULL, Pkt4::MAX_FILE_LEN), InvalidParameter);
    EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
463 464
}

465 466 467 468
/// V4 Options being used for pack/unpack testing.
/// For test simplicity, all selected options have
/// variable length data so as there are no restrictions
/// on a length of their data.
469
static uint8_t v4Opts[] = {
470 471 472 473 474 475
    12,  3, 0,   1,  2, // Hostname
    14,  3, 10, 11, 12, // Merit Dump File
    53, 1, 1, // Message Type (required to not throw exception during unpack)
    60,  3, 20, 21, 22, // Class Id
    128, 3, 30, 31, 32, // Vendor specific
    254, 3, 40, 41, 42, // Reserved
476 477
};

478
TEST(Pkt4Test, options) {
479 480 481 482
    Pkt4* pkt = new Pkt4(DHCPOFFER, 0);

    vector<uint8_t> payload[5];
    for (int i = 0; i < 5; i++) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
483 484 485
        payload[i].push_back(i*10);
        payload[i].push_back(i*10+1);
        payload[i].push_back(i*10+2);
486 487 488
    }

    boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
489
    boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[1]));
490
    boost::shared_ptr<Option> optMsgType(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE));
491
    boost::shared_ptr<Option> opt2(new Option(Option::V4, 60, payload[2]));
492 493
    boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[3]));
    boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[4]));
494
    optMsgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
495 496 497 498 499 500

    pkt->addOption(opt1);
    pkt->addOption(opt2);
    pkt->addOption(opt3);
    pkt->addOption(opt4);
    pkt->addOption(opt5);
501
    pkt->addOption(optMsgType);
502 503

    EXPECT_TRUE(pkt->getOption(12));
504
    EXPECT_TRUE(pkt->getOption(60));
505 506 507 508 509 510 511 512 513 514 515
    EXPECT_TRUE(pkt->getOption(14));
    EXPECT_TRUE(pkt->getOption(128));
    EXPECT_TRUE(pkt->getOption(254));
    EXPECT_FALSE(pkt->getOption(127)); //  no such option

    // options are unique in DHCPv4. It should not be possible
    // to add more than one option of the same type.
    EXPECT_THROW(
        pkt->addOption(opt1),
        BadValue
    );
516

517 518 519 520
    EXPECT_NO_THROW(
        pkt->pack();
    );

Tomek Mrugalski's avatar
Tomek Mrugalski committed
521
    const OutputBuffer& buf = pkt->getBuffer();
522 523 524 525
    // check that all options are stored, they should take sizeof(v4Opts),
    // DHCP magic cookie (4 bytes), and OPTION_END added (just one byte)
    ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + sizeof(DHCP_OPTIONS_COOKIE)
              + sizeof(v4Opts) + 1, buf.getLength());
526 527 528

    // that that this extra data actually contain our options
    const uint8_t* ptr = static_cast<const uint8_t*>(buf.getData());
529
    ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE); // rewind to end of fixed part
530
    EXPECT_EQ(0, memcmp(ptr, v4Opts, sizeof(v4Opts)));
531
    EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4Opts))));
532 533 534 535

    EXPECT_NO_THROW(
        delete pkt;
    );
536 537 538
}

TEST(Pkt4Test, unpackOptions) {
539 540 541

    vector<uint8_t> expectedFormat = generateTestPacket2();

542 543 544 545 546
    expectedFormat.push_back(0x63);
    expectedFormat.push_back(0x82);
    expectedFormat.push_back(0x53);
    expectedFormat.push_back(0x63);

547
    for (int i = 0; i < sizeof(v4Opts); i++) {
548 549 550 551 552
        expectedFormat.push_back(v4Opts[i]);
    }

    // now expectedFormat contains fixed format and 5 options

553 554
    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
                                expectedFormat.size()));
555 556 557 558 559 560

    EXPECT_NO_THROW(
        pkt->unpack()
    );

    EXPECT_TRUE(pkt->getOption(12));
561
    EXPECT_TRUE(pkt->getOption(60));
562 563 564 565
    EXPECT_TRUE(pkt->getOption(14));
    EXPECT_TRUE(pkt->getOption(128));
    EXPECT_TRUE(pkt->getOption(254));

566
    boost::shared_ptr<Option> x = pkt->getOption(12);
567 568 569 570 571 572
    ASSERT_TRUE(x); // option 1 should exist
    EXPECT_EQ(12, x->getType());  // this should be option 12
    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
    EXPECT_EQ(5, x->len()); // total option length 5
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+2, 3)); // data len=3

573
    x = pkt->getOption(14);
574
    ASSERT_TRUE(x); // option 13 should exist
575
    EXPECT_EQ(14, x->getType());  // this should be option 13
576 577 578 579
    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
    EXPECT_EQ(5, x->len()); // total option length 5
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+7, 3)); // data len=3

580 581 582
    x = pkt->getOption(60);
    ASSERT_TRUE(x); // option 60 should exist
    EXPECT_EQ(60, x->getType());  // this should be option 60
583 584
    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
    EXPECT_EQ(5, x->len()); // total option length 5
585
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+15, 3)); // data len=3
586 587 588 589 590 591

    x = pkt->getOption(128);
    ASSERT_TRUE(x); // option 3 should exist
    EXPECT_EQ(128, x->getType());  // this should be option 254
    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
    EXPECT_EQ(5, x->len()); // total option length 5
592
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+20, 3)); // data len=3
593 594 595 596 597 598

    x = pkt->getOption(254);
    ASSERT_TRUE(x); // option 3 should exist
    EXPECT_EQ(254, x->getType());  // this should be option 254
    ASSERT_EQ(3, x->getData().size()); // it should be of length 3
    EXPECT_EQ(5, x->len()); // total option length 5
599
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+25, 3)); // data len=3
600 601
}

602 603
// This test verifies methods that are used for manipulating meta fields
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
604 605 606 607 608 609 610 611 612 613 614 615
TEST(Pkt4Test, metaFields) {

    Pkt4* pkt = new Pkt4(DHCPOFFER, 1234);
    pkt->setIface("loooopback");
    pkt->setIndex(42);
    pkt->setRemoteAddr(IOAddress("1.2.3.4"));
    pkt->setLocalAddr(IOAddress("4.3.2.1"));

    EXPECT_EQ("loooopback", pkt->getIface());
    EXPECT_EQ(42, pkt->getIndex());
    EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
    EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
616 617

    delete pkt;
618 619
}

620
TEST(Pkt4Test, Timestamp) {
621 622 623 624
    scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));

    // Just after construction timestamp is invalid
    ASSERT_TRUE(pkt->getTimestamp().is_not_a_date_time());
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

    // Update packet time.
    pkt->updateTimestamp();

    // Get updated packet time.
    boost::posix_time::ptime ts_packet = pkt->getTimestamp();

    // After timestamp is updated it should be date-time.
    ASSERT_FALSE(ts_packet.is_not_a_date_time());

    // Check current time.
    boost::posix_time::ptime ts_now =
        boost::posix_time::microsec_clock::universal_time();

    // Calculate period between packet time and now.
    boost::posix_time::time_period ts_period(ts_packet, ts_now);

    // Duration should be positive or zero.
    EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
644 645
}

646 647 648 649 650 651 652 653 654 655 656
TEST(Pkt4Test, hwaddr) {
    scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
    const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
    const uint8_t hw_type = 123; // hardware type

    HWAddrPtr hwaddr(new HWAddr(hw, sizeof(hw), hw_type));

    // setting NULL hardware address is not allowed
    EXPECT_THROW(pkt->setHWAddr(HWAddrPtr()), BadValue);

    pkt->setHWAddr(hwaddr);
657

658 659 660 661 662 663
    EXPECT_EQ(hw_type, pkt->getHtype());

    EXPECT_EQ(sizeof(hw), pkt->getHlen());

    EXPECT_TRUE(hwaddr == pkt->getHWAddr());
}
664

665
} // end of anonymous namespace