pkt4_unittest.cc 19.9 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
// Copyright (C) 2011  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 <boost/static_assert.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
23 24 25 26 27
#include <util/buffer.h>
#include <asiolink/io_address.h>
#include <dhcp/pkt4.h>
#include <dhcp/dhcp4.h>
#include <exceptions/exceptions.h>
28 29 30 31 32

using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
33
using namespace isc::util;
34 35 36 37 38 39
using namespace boost;

namespace {

TEST(Pkt4Test, constructor) {

40
    ASSERT_EQ(236U, static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) );
41
    Pkt4* pkt = 0;
42

43
    // Just some dummy payload.
44
    uint8_t testData[250];
45
    for (int i = 0; i < 250; i++) {
46
        testData[i]=i;
47
    }
48

49
    // Positive case1. Normal received packet.
50
    EXPECT_NO_THROW(
51
        pkt = new Pkt4(testData, Pkt4::DHCPV4_PKT_HDR_LEN);
52 53
    );

54
    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
55 56 57 58 59 60

    EXPECT_NO_THROW(
        delete pkt;
        pkt = 0;
    );

61
    // Positive case2. Normal outgoing packet.
62 63 64 65 66
    EXPECT_NO_THROW(
        pkt = new Pkt4(DHCPDISCOVER, 0xffffffff);
    );

    // DHCPv4 packet must be at least 236 bytes long
67
    EXPECT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
68 69 70 71 72 73 74
    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
    EXPECT_EQ(0xffffffff, pkt->getTransid());
    EXPECT_NO_THROW(
        delete pkt;
        pkt = 0;
    );

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

88
// a sample data
Tomek Mrugalski's avatar
Tomek Mrugalski committed
89 90 91 92 93 94 95 96 97 98 99 100
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");
101 102 103 104 105

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

// a dummy MAC address, padded with 0s
106
const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
107 108 109 110 111 112
                                 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.";
113

114 115 116 117 118 119 120
// 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);

121
/// @brief Generates test packet.
122 123 124 125 126 127 128 129 130 131 132 133 134
///
/// 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));
135 136 137 138

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

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

    return (pkt);
}

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

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

182 183
    // Initialize the vector with the header fields defined above.
    vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
184

185
    // Append the large header fields.
186
    copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
187 188
    copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
    copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
189

190 191 192 193
    // 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());
194 195 196 197 198 199

    return (buf);
}

TEST(Pkt4Test, fixedFields) {

200
    boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
201 202

    // ok, let's check packet values
203 204 205 206
    EXPECT_EQ(dummyOp, pkt->getOp());
    EXPECT_EQ(dummyHtype, pkt->getHtype());
    EXPECT_EQ(dummyHlen, pkt->getHlen());
    EXPECT_EQ(dummyHops, pkt->getHops());
207
    EXPECT_EQ(dummyTransid, pkt->getTransid());
208 209
    EXPECT_EQ(dummySecs, pkt->getSecs());
    EXPECT_EQ(dummyFlags, pkt->getFlags());
210

211 212 213 214
    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());
215 216

    // chaddr is always 16 bytes long and contains link-layer addr (MAC)
217
    EXPECT_EQ(0, memcmp(dummyChaddr, pkt->getChaddr(), 16));
218

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

221
    EXPECT_EQ(0, memcmp(dummyFile, &pkt->getFile()[0], 128));
222 223 224 225 226

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

TEST(Pkt4Test, fixedFieldsPack) {
227
    boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
228
    vector<uint8_t> expectedFormat = generateTestPacket2();
229 230 231 232 233

    EXPECT_NO_THROW(
        pkt->pack();
    );

234
    ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN), pkt->len());
235 236

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

240
    EXPECT_EQ(0, memcmp(exp, got, Pkt4::DHCPV4_PKT_HDR_LEN));
241 242 243 244
}

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
247 248 249 250 251 252 253 254 255
    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);

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

259

260 261 262 263
    EXPECT_NO_THROW(
        pkt->unpack()
    );

264
    // ok, let's check packet values
265 266 267 268 269 270 271 272 273 274 275 276
    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());
277 278

    // chaddr is always 16 bytes long and contains link-layer addr (MAC)
279
    EXPECT_EQ(0, memcmp(dummyChaddr, pkt->getChaddr(), Pkt4::MAX_CHADDR_LEN));
280

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

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

287
    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
288 289 290 291 292
}

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

293
    vector<uint8_t> mac;
294 295
    uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];

Tomek Mrugalski's avatar
Tomek Mrugalski committed
296 297 298
    // 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).
299 300
    mac.resize(Pkt4::MAX_CHADDR_LEN);

301 302
    Pkt4* pkt = 0;
    // let's test each hlen, from 0 till 16
Tomek Mrugalski's avatar
Tomek Mrugalski committed
303 304
    for (int macLen = 0; macLen < Pkt4::MAX_CHADDR_LEN; macLen++) {
        for (int i = 0; i < Pkt4::MAX_CHADDR_LEN; i++) {
305 306 307
            mac[i] = 0;
            expectedChaddr[i] = 0;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
308 309 310
        for (int i = 0; i < macLen; i++) {
            mac[i] = 128 + i;
            expectedChaddr[i] = 128 + i;
311
        }
312

313 314 315 316 317
        // type and transaction doesn't matter in this test
        pkt = new Pkt4(DHCPOFFER, 1234);
        pkt->setHWAddr(255-macLen*10, // just weird htype
                       macLen,
                       mac);
318
        EXPECT_EQ(0, memcmp(expectedChaddr, pkt->getChaddr(),
319 320
                            Pkt4::MAX_CHADDR_LEN));

321
        EXPECT_NO_THROW(
322 323 324 325
            pkt->pack();
        );

        // CHADDR starts at offset 28 in DHCP packet
326 327 328 329
        const uint8_t* ptr =
            static_cast<const uint8_t*>(pkt->getBuffer().getData())+28;

        EXPECT_EQ(0, memcmp(ptr, expectedChaddr, Pkt4::MAX_CHADDR_LEN));
330 331 332 333 334 335 336 337

        delete pkt;
    }

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

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
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
361
    for (int i = 0; i < sizeof(types) / sizeof(msgType); i++) {
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

        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;
    }
}

381 382 383 384 385 386 387 388
// 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
389
        for (int i = 0; i < Pkt4::MAX_SNAME_LEN; i++) {
390 391
            sname[i] = 0;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
392
        for (int i = 0; i < snameLen; i++) {
393 394
            sname[i] = i;
        }
395

396 397 398 399
        // type and transaction doesn't matter in this test
        pkt = new Pkt4(DHCPOFFER, 1234);
        pkt->setSname(sname, snameLen);

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

402
        EXPECT_NO_THROW(
403 404 405 406
            pkt->pack();
        );

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

        delete pkt;
    }
}

TEST(Pkt4Test, file) {

    uint8_t file[Pkt4::MAX_FILE_LEN];

    Pkt4* pkt = 0;
420
    // Let's test each file length, from 0 till 128.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
421 422
    for (int fileLen = 0; fileLen < Pkt4::MAX_FILE_LEN; fileLen++) {
        for (int i = 0; i < Pkt4::MAX_FILE_LEN; i++) {
423 424
            file[i] = 0;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
425
        for (int i = 0; i < fileLen; i++) {
426 427
            file[i] = i;
        }
428

429
        // Type and transaction doesn't matter in this test.
430 431 432
        pkt = new Pkt4(DHCPOFFER, 1234);
        pkt->setFile(file, fileLen);

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

435
        //
436
        EXPECT_NO_THROW(
437 438 439
            pkt->pack();
        );

440
        // FILE starts at offset 108 in DHCP packet.
441 442 443
        const uint8_t* ptr =
            static_cast<const uint8_t*>(pkt->getBuffer().getData())+108;
        EXPECT_EQ(0, memcmp(ptr, file, Pkt4::MAX_FILE_LEN));
444 445 446 447 448 449

        delete pkt;
    }

}

450 451 452 453
static uint8_t v4Opts[] = {
    12,  3, 0,   1,  2,
    13,  3, 10, 11, 12,
    14,  3, 20, 21, 22,
454
    53, 1, 1, // DHCP_MESSAGE_TYPE (required to not throw exception during unpack)
455
    128, 3, 30, 31, 32,
456
    254, 3, 40, 41, 42,
457 458
};

459
TEST(Pkt4Test, options) {
460 461 462 463
    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
464 465 466
        payload[i].push_back(i*10);
        payload[i].push_back(i*10+1);
        payload[i].push_back(i*10+2);
467 468 469 470 471
    }

    boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
    boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
    boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
472
    boost::shared_ptr<Option> optMsgType(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE));
473 474
    boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[3]));
    boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[4]));
475
    optMsgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
476 477 478 479 480 481

    pkt->addOption(opt1);
    pkt->addOption(opt2);
    pkt->addOption(opt3);
    pkt->addOption(opt4);
    pkt->addOption(opt5);
482
    pkt->addOption(optMsgType);
483 484 485 486 487 488 489 490 491 492 493 494 495 496

    EXPECT_TRUE(pkt->getOption(12));
    EXPECT_TRUE(pkt->getOption(13));
    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
    );
497

498 499 500 501
    EXPECT_NO_THROW(
        pkt->pack();
    );

Tomek Mrugalski's avatar
Tomek Mrugalski committed
502
    const OutputBuffer& buf = pkt->getBuffer();
503 504 505 506
    // 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());
507 508 509

    // that that this extra data actually contain our options
    const uint8_t* ptr = static_cast<const uint8_t*>(buf.getData());
510
    ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE); // rewind to end of fixed part
511
    EXPECT_EQ(0, memcmp(ptr, v4Opts, sizeof(v4Opts)));
512
    EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4Opts))));
513 514 515 516

    EXPECT_NO_THROW(
        delete pkt;
    );
517 518 519
}

TEST(Pkt4Test, unpackOptions) {
520 521 522

    vector<uint8_t> expectedFormat = generateTestPacket2();

523 524 525 526 527
    expectedFormat.push_back(0x63);
    expectedFormat.push_back(0x82);
    expectedFormat.push_back(0x53);
    expectedFormat.push_back(0x63);

528
    for (int i = 0; i < sizeof(v4Opts); i++) {
529 530 531 532 533
        expectedFormat.push_back(v4Opts[i]);
    }

    // now expectedFormat contains fixed format and 5 options

534 535
    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
                                expectedFormat.size()));
536 537 538 539 540 541 542 543 544 545 546

    EXPECT_NO_THROW(
        pkt->unpack()
    );

    EXPECT_TRUE(pkt->getOption(12));
    EXPECT_TRUE(pkt->getOption(13));
    EXPECT_TRUE(pkt->getOption(14));
    EXPECT_TRUE(pkt->getOption(128));
    EXPECT_TRUE(pkt->getOption(254));

547
    boost::shared_ptr<Option> x = pkt->getOption(12);
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 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

    x = pkt->getOption(13);
    ASSERT_TRUE(x); // option 13 should exist
    EXPECT_EQ(13, x->getType());  // this should be option 13
    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

    x = pkt->getOption(14);
    ASSERT_TRUE(x); // option 14 should exist
    EXPECT_EQ(14, x->getType());  // this should be option 14
    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+12, 3)); // data len=3

    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
573
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+20, 3)); // data len=3
574 575 576 577 578 579

    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
580
    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+25, 3)); // data len=3
581 582
}

583 584
// This test verifies methods that are used for manipulating meta fields
// i.e. fields that are not part of DHCPv4 (e.g. interface name).
585 586 587 588 589 590 591 592 593 594 595 596
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());
597 598

    delete pkt;
599 600
}

601 602
TEST(Pkt4Test, Timestamp) {
    Pkt4* pkt = new Pkt4(DHCPOFFER, 1234);
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

    // 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);
622 623 624 625 626

    delete pkt;
}


627

628
} // end of anonymous namespace