pkt6_unittest.cc 67.3 KB
Newer Older
1
// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7
#include <config.h>
8

9
#include <asiolink/io_address.h>
10
#include <dhcp/dhcp6.h>
11
#include <dhcp/option.h>
12 13 14 15
#include <dhcp/option_custom.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
16
#include <dhcp/option_string.h>
17
#include <dhcp/option_vendor.h>
18
#include <dhcp/iface_mgr.h>
19
#include <dhcp/pkt6.h>
20
#include <dhcp/hwaddr.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
21
#include <dhcp/docsis3_option_defs.h>
22
#include <dhcp/tests/pkt_captures.h>
23
#include <testutils/gtest_utils.h>
24
#include <util/range_utilities.h>
25
#include <boost/bind.hpp>
26
#include <boost/date_time/posix_time/posix_time.hpp>
27
#include <boost/scoped_ptr.hpp>
28
#include <boost/pointer_cast.hpp>
29
#include <util/encode/hex.h>
30 31
#include <gtest/gtest.h>

32
#include <algorithm>
33 34
#include <iostream>
#include <sstream>
35
#include <utility>
36 37

#include <arpa/inet.h>
38 39 40

using namespace std;
using namespace isc;
41
using namespace isc::asiolink;
42
using namespace isc::dhcp;
43
using namespace isc::dhcp::test;
44
using boost::scoped_ptr;
45

46
namespace {
47

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
class NakedPkt6 : public Pkt6 {
public:

    /// @brief Constructor, used in replying to a message
    ///
    /// @param msg_type type of message (SOLICIT=1, ADVERTISE=2, ...)
    /// @param transid transaction-id
    /// @param proto protocol (TCP or UDP)
    NakedPkt6(const uint8_t msg_type, const uint32_t transid,
              const DHCPv6Proto& proto = UDP)
        : Pkt6(msg_type, transid, proto) {
    }

    /// @brief Constructor, used in message transmission
    ///
    /// Creates new message. Transaction-id will randomized.
    ///
    /// @param buf pointer to a buffer of received packet content
    /// @param len size of buffer of received packet content
    /// @param proto protocol (usually UDP, but TCP will be supported eventually)
    NakedPkt6(const uint8_t* buf, const uint32_t len,
              const DHCPv6Proto& proto = UDP)
        : Pkt6(buf, len, proto) {
    }

    using Pkt6::getNonCopiedOptions;
    using Pkt6::getNonCopiedRelayOption;
    using Pkt6::getNonCopiedAnyRelayOption;
};

typedef boost::shared_ptr<NakedPkt6> NakedPkt6Ptr;

80 81 82 83
class Pkt6Test : public ::testing::Test {
public:
    Pkt6Test() {
    }
84

Francis Dupont's avatar
Francis Dupont committed
85 86
    /// @brief generates an option with given code (and length) and
    /// random content
87 88 89 90 91 92 93 94 95 96
    ///
    /// @param code option code
    /// @param len data length (data will be randomized)
    ///
    /// @return pointer to the new option
    OptionPtr generateRandomOption(uint16_t code, size_t len = 10) {
        OptionBuffer data(len);
        util::fillRandom(data.begin(), data.end());
        return OptionPtr(new Option(Option::V6, code, data));
    }
97 98

    /// @brief Create a wire representation of the test packet and clone it.
99 100 101 102 103 104 105 106 107 108 109 110 111
    ///
    /// The purpose of this function is to create a packet to be used to
    /// check that packet parsing works correctly. The unpack() function
    /// requires that the data_ field of the object holds the data to be
    /// parsed. This function creates an on-wire representation of the
    /// packet by calling pack(). But, the pack() function stores the
    /// on-wire representation into the output buffer (not the data_ field).
    /// For this reason, it is not enough to return the packet on which
    /// pack() is called. This function returns a clone of this packet
    /// which is created using a constructor taking a buffer and buffer
    /// length as an input. This constructor is normally used to parse
    /// received packets. It stores the packet in a data_ field and
    /// therefore unpack() can be called to parse it.
112 113 114
    ///
    /// @param parent Packet from which the new packet should be created.
    Pkt6Ptr packAndClone(Pkt6Ptr& parent) {
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
        OptionPtr opt1(new Option(Option::V6, 1));
        OptionPtr opt2(new Option(Option::V6, 2));
        OptionPtr opt3(new Option(Option::V6, 100));
        // Let's not use zero-length option type 3 as it is IA_NA

        parent->addOption(opt1);
        parent->addOption(opt2);
        parent->addOption(opt3);

        EXPECT_NO_THROW(parent->pack());

        // Create second packet,based on assembled data from the first one
        Pkt6Ptr clone(new Pkt6(static_cast<const uint8_t*>
                               (parent->getBuffer().getData()),
                               parent->getBuffer().getLength()));
        return (clone);

    }
133 134 135
};

TEST_F(Pkt6Test, constructor) {
136
    uint8_t data[] = { 0, 1, 2, 3, 4, 5 };
137
    scoped_ptr<Pkt6> pkt1(new Pkt6(data, sizeof(data)));
138

139 140
    EXPECT_EQ(6, pkt1->data_.size());
    EXPECT_EQ(0, memcmp( &pkt1->data_[0], data, sizeof(data)));
141
}
142

143 144 145 146 147 148 149 150
/// @brief returns captured actual 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
151
Pkt6Ptr capture1() {
152
    uint8_t data[98];
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    data[0]  = 1;
    data[1]  = 1;       data[2]  = 2;     data[3] = 3;      data[4]  = 0;
    data[5]  = 1;       data[6]  = 0;     data[7] = 14;     data[8]  = 0;
    data[9]  = 1;       data[10] = 0;     data[11] = 1;     data[12] = 21;
    data[13] = 158;     data[14] = 60;    data[15] = 22;    data[16] = 0;
    data[17] = 30;      data[18] = 140;   data[19] = 155;   data[20] = 115;
    data[21] = 73;      data[22] = 0;     data[23] = 3;     data[24] = 0;
    data[25] = 40;      data[26] = 0;     data[27] = 0;     data[28] = 0;
    data[29] = 1;       data[30] = 255;   data[31] = 255;   data[32] = 255;
    data[33] = 255;     data[34] = 255;   data[35] = 255;   data[36] = 255;
    data[37] = 255;     data[38] = 0;     data[39] = 5;     data[40] = 0;
    data[41] = 24;      data[42] = 32;    data[43] = 1;     data[44] = 13;
    data[45] = 184;     data[46] = 0;     data[47] = 1;     data[48] = 0;
    data[49] = 0;       data[50] = 0;     data[51] = 0;     data[52] = 0;
    data[53] = 0;       data[54] = 0;     data[55] = 0;     data[56] = 18;
    data[57] = 52;      data[58] = 255;   data[59] = 255;   data[60] = 255;
    data[61] = 255;     data[62] = 255;   data[63] = 255;   data[64] = 255;
    data[65] = 255;     data[66] = 0;     data[67] = 23;    data[68] = 0;
    data[69] = 16;      data[70] = 32;    data[71] = 1;     data[72] = 13;
    data[73] = 184;     data[74] = 0;     data[75] = 1;     data[76] = 0;
    data[77] = 0;       data[78] = 0;     data[79] = 0;     data[80] = 0;
    data[81] = 0;       data[82] = 0;     data[83] = 0;     data[84] = 221;
    data[85] = 221;     data[86] = 0;     data[87] = 8;     data[88] = 0;
    data[89] = 2;       data[90] = 0;     data[91] = 100;   data[92] = 0;
    data[93] = 6;       data[94] = 0;     data[95] = 2;     data[96] = 0;
    data[97] = 23;
179

180
    Pkt6Ptr pkt(new Pkt6(data, sizeof(data)));
181 182 183 184 185 186 187
    pkt->setRemotePort(546);
    pkt->setRemoteAddr(IOAddress("fe80::21e:8cff:fe9b:7349"));
    pkt->setLocalPort(0);
    pkt->setLocalAddr(IOAddress("ff02::1:2"));
    pkt->setIndex(2);
    pkt->setIface("eth0");

188 189 190
    return (pkt);
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
191
/// @brief creates doubly relayed solicit message
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
///
/// This is a traffic capture exported from wireshark. It includes a SOLICIT
/// message that passed through two relays. Each relay include interface-id,
/// remote-id and relay-forw encapsulation. It is especially interesting,
/// because of the following properties:
/// - double encapsulation
/// - first relay inserts relay-msg before extra options
/// - second relay inserts relay-msg after extra options
/// - both relays are from different vendors
/// - interface-id are different for each relay
/// - first relay inserts valid remote-id
/// - second relay inserts remote-id with empty vendor data
/// - the solicit message requests for custom options in ORO
/// - there are option types in RELAY-FORW that do not appear in SOLICIT
/// - there are option types in SOLICT that do not appear in RELAY-FORW
///
/// RELAY-FORW
///  - relay message option
///      - RELAY-FORW
///          - interface-id option
///          - remote-id option
///          - RELAY-FORW
///              SOLICIT
///                  - client-id option
///                  - ia_na option
///                  - elapsed time
///                  - ORO
///  - interface-id option
///  - remote-id option
///
/// The original capture was posted to dibbler users mailing list.
///
/// @return created double relayed SOLICIT message
225
Pkt6Ptr capture2() {
226 227 228

    // string exported from Wireshark
    string hex_string =
Francis Dupont's avatar
Francis Dupont committed
229 230 231 232 233 234 235
        "0c01200108880db800010000000000000000fe80000000000000020021fffe5c"
        "18a90009007d0c0000000000000000000000000000000000fe80000000000000"
        "020021fffe5c18a9001200154953414d3134342065746820312f312f30352f30"
        "310025000400000de900090036016b4fe20001000e0001000118b03341000021"
        "5c18a90003000c00000001ffffffffffffffff00080002000000060006001700"
        "f200f30012001c4953414d3134347c3239397c697076367c6e743a76703a313a"
        "313130002500120000197f0001000118b033410000215c18a9";
236 237 238 239 240 241 242

    std::vector<uint8_t> bin;

    // Decode the hex string and store it in bin (which happens
    // to be OptionBuffer format)
    isc::util::encode::decodeHex(hex_string, bin);

243
    NakedPkt6Ptr pkt(new NakedPkt6(&bin[0], bin.size()));
244 245 246 247 248 249
    pkt->setRemotePort(547);
    pkt->setRemoteAddr(IOAddress("fe80::1234"));
    pkt->setLocalPort(547);
    pkt->setLocalAddr(IOAddress("ff05::1:3"));
    pkt->setIndex(2);
    pkt->setIface("eth0");
250
    return (boost::dynamic_pointer_cast<Pkt6>(pkt));
251
}
252

253
TEST_F(Pkt6Test, unpack_solicit1) {
254
    Pkt6Ptr sol(capture1());
255

256
    ASSERT_NO_THROW(sol->unpack());
257

258
    // Check for length
259 260
    EXPECT_EQ(98, sol->len() );

261
    // Check for type
262
    EXPECT_EQ(DHCPV6_SOLICIT, sol->getType() );
263

264
    // Check that all present options are returned
265 266 267 268 269
    EXPECT_TRUE(sol->getOption(D6O_CLIENTID)); // client-id is present
    EXPECT_TRUE(sol->getOption(D6O_IA_NA));    // IA_NA is present
    EXPECT_TRUE(sol->getOption(D6O_ELAPSED_TIME));  // elapsed is present
    EXPECT_TRUE(sol->getOption(D6O_NAME_SERVERS));
    EXPECT_TRUE(sol->getOption(D6O_ORO));
270

271
    // Let's check that non-present options are not returned
272 273 274
    EXPECT_FALSE(sol->getOption(D6O_SERVERID)); // server-id is missing
    EXPECT_FALSE(sol->getOption(D6O_IA_TA));
    EXPECT_FALSE(sol->getOption(D6O_IAADDR));
275 276
}

277
TEST_F(Pkt6Test, packUnpack) {
278
    // Create an on-wire representation of the test packet and clone it.
279 280
    Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 0x020304));
    Pkt6Ptr clone = packAndClone(pkt);
281

282
    // Now recreate options list
283
    ASSERT_NO_THROW(clone->unpack());
284

285 286 287
    // transid, message-type should be the same as before
    EXPECT_EQ(0x020304, clone->getTransid());
    EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
288

289 290 291 292 293
    EXPECT_TRUE(clone->getOption(1));
    EXPECT_TRUE(clone->getOption(2));
    EXPECT_TRUE(clone->getOption(100));
    EXPECT_FALSE(clone->getOption(4));
}
294

295 296
// Checks if the code is able to handle malformed packet
TEST_F(Pkt6Test, unpackMalformed) {
Francis Dupont's avatar
Francis Dupont committed
297 298
    // Get a packet. We're really interested in its on-wire
    // representation only.
299
    Pkt6Ptr donor(capture1());
300 301 302 303 304 305 306 307 308 309 310 311 312 313

    // That's our original content. It should be sane.
    OptionBuffer orig = donor->data_;

    Pkt6Ptr success(new Pkt6(&orig[0], orig.size()));
    EXPECT_NO_THROW(success->unpack());

    // Insert trailing garbage.
    OptionBuffer malform1 = orig;
    malform1.push_back(123);

    // Let's check a truncated packet. Moderately sane DHCPv6 packet should at
    // least have four bytes header. Zero bytes is definitely not a valid one.
    OptionBuffer empty(1); // Let's allocate one byte, so we won't be
Francis Dupont's avatar
Francis Dupont committed
314
                           // dereferencing an empty buffer.
315 316 317 318 319 320 321 322 323 324 325 326

    Pkt6Ptr empty_pkt(new Pkt6(&empty[0], 0));
    EXPECT_THROW(empty_pkt->unpack(), isc::BadValue);

    // Neither is 3 bytes long.
    OptionBuffer shorty;
    shorty.push_back(DHCPV6_SOLICIT);
    shorty.push_back(1);
    shorty.push_back(2);
    Pkt6Ptr too_short_pkt(new Pkt6(&shorty[0], shorty.size()));
    EXPECT_THROW(too_short_pkt->unpack(), isc::BadValue);

327 328
    // The code should complain about remaining bytes that can't be parsed
    // but doesn't do so yet.
329 330 331 332
    Pkt6Ptr trailing_garbage(new Pkt6(&malform1[0], malform1.size()));
    EXPECT_NO_THROW(trailing_garbage->unpack());

    // A strict approach would assume the code will reject the whole packet,
333 334
    // but we decided to follow Jon Postel's law and be silent about
    // received malformed or truncated options.
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

    // Add an option that is truncated
    OptionBuffer malform2 = orig;
    malform2.push_back(0);
    malform2.push_back(123); // 0, 123 - option code = 123
    malform2.push_back(0);
    malform2.push_back(1);   // 0, 1 - option length = 1
    // Option content would go here, but it's missing

    Pkt6Ptr trunc_option(new Pkt6(&malform2[0], malform2.size()));

    // The unpack() operation should succeed...
    EXPECT_NO_THROW(trunc_option->unpack());

    // ... but there should be no option 123 as it was malformed.
    EXPECT_FALSE(trunc_option->getOption(123));
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

    // Check with truncated length field
    Pkt6Ptr trunc_length(new Pkt6(&malform2[0], malform2.size() - 1));
    EXPECT_NO_THROW(trunc_length->unpack());
    EXPECT_FALSE(trunc_length->getOption(123));

    // Check with missing length field
    Pkt6Ptr no_length(new Pkt6(&malform2[0], malform2.size() - 2));
    EXPECT_NO_THROW(no_length->unpack());
    EXPECT_FALSE(no_length->getOption(123));

    // Check with truncated type field
    Pkt6Ptr trunc_type(new Pkt6(&malform2[0], malform2.size() - 3));
    EXPECT_NO_THROW(trunc_type->unpack());
    EXPECT_FALSE(trunc_type->getOption(123));
366 367
}

368 369 370 371
// Checks if the code is able to handle a malformed vendor option
TEST_F(Pkt6Test, unpackVendorMalformed) {
    // Get a packet. We're really interested in its on-wire
    // representation only.
372
    Pkt6Ptr donor(capture1());
373 374 375 376 377 378 379 380

    // Add a vendor option
    OptionBuffer orig = donor->data_;

    orig.push_back(0); // vendor options
    orig.push_back(17);
    orig.push_back(0);
    size_t len_index = orig.size();
381
    orig.push_back(18); // length=18
382 383 384 385 386
    orig.push_back(1); // vendor_id=0x1020304
    orig.push_back(2);
    orig.push_back(3);
    orig.push_back(4);
    orig.push_back(1); // suboption type=0x101
387
    orig.push_back(1);
388 389 390 391 392 393 394
    orig.push_back(0); // suboption length=3
    orig.push_back(3);
    orig.push_back(102); // data="foo"
    orig.push_back(111);
    orig.push_back(111);
    orig.push_back(1); // suboption type=0x102
    orig.push_back(2);
395
    orig.push_back(0); // suboption length=3
396 397 398 399 400 401 402 403 404 405
    orig.push_back(3);
    orig.push_back(99); // data="bar'
    orig.push_back(98);
    orig.push_back(114);

    Pkt6Ptr success(new Pkt6(&orig[0], orig.size()));
    EXPECT_NO_THROW(success->unpack());

    // Truncated vendor option is not accepted but doesn't throw
    vector<uint8_t> shortv = orig;
406
    shortv[len_index] = 20;
407 408
    Pkt6Ptr too_short_vendor_pkt(new Pkt6(&shortv[0], shortv.size()));
    EXPECT_NO_THROW(too_short_vendor_pkt->unpack());
409

410 411 412 413 414
    // Truncated option header is not accepted
    vector<uint8_t> shorth = orig;
    shorth.resize(orig.size() - 4);
    shorth[len_index] = 12;
    Pkt6Ptr too_short_header_pkt(new Pkt6(&shorth[0], shorth.size()));
415
    EXPECT_THROW(too_short_header_pkt->unpack(), SkipRemainingOptionsError);
416 417

    // Truncated option data is not accepted
418 419
    vector<uint8_t> shorto = orig;
    shorto.resize(orig.size() - 2);
420
    shorto[len_index] = 16;
421
    Pkt6Ptr too_short_option_pkt(new Pkt6(&shorto[0], shorto.size()));
422
    EXPECT_THROW(too_short_option_pkt->unpack(), SkipRemainingOptionsError);
423 424
}

425 426
// This test verifies that options can be added (addOption()), retrieved
// (getOption(), getOptions()) and deleted (delOption()).
427
TEST_F(Pkt6Test, addGetDelOptions) {
428
    scoped_ptr<Pkt6> parent(new Pkt6(DHCPV6_SOLICIT, random()));
429

430 431 432
    OptionPtr opt1(new Option(Option::V6, 1));
    OptionPtr opt2(new Option(Option::V6, 2));
    OptionPtr opt3(new Option(Option::V6, 2));
433 434 435 436 437 438 439 440

    parent->addOption(opt1);
    parent->addOption(opt2);

    // getOption() test
    EXPECT_EQ(opt1, parent->getOption(1));
    EXPECT_EQ(opt2, parent->getOption(2));

441
    // Expect NULL
442
    EXPECT_EQ(OptionPtr(), parent->getOption(4));
443

444
    // Now there are 2 options of type 2
445 446
    parent->addOption(opt3);

447
    OptionCollection options = parent->getOptions(2);
448 449
    EXPECT_EQ(2, options.size()); // there should be 2 instances

450
    // Both options must be of type 2 and there must not be
451
    // any other type returned
452
    for (OptionCollection::const_iterator x= options.begin();
453 454 455 456 457 458 459 460 461 462 463 464 465
         x != options.end(); ++x) {
        EXPECT_EQ(2, x->second->getType());
    }

    // Try to get a single option. Normally for singular options
    // it is better to use getOption(), but getOptions() must work
    // as well
    options = parent->getOptions(1);
    ASSERT_EQ(1, options.size());

    EXPECT_EQ(1, (*options.begin()).second->getType());
    EXPECT_EQ(opt1, options.begin()->second);

466
    // Let's delete one of them
467 468
    EXPECT_EQ(true, parent->delOption(2));

469
    // There still should be the other option 2
470
    EXPECT_NE(OptionPtr(), parent->getOption(2));
471

472
    // Let's delete the other option 2
473 474
    EXPECT_EQ(true, parent->delOption(2));

475
    // No more options with type=2
476
    EXPECT_EQ(OptionPtr(), parent->getOption(2));
477

478
    // Let's try to delete - should fail
479 480
    EXPECT_TRUE(false ==  parent->delOption(2));

481 482 483
    // Finally try to get a non-existent option
    options = parent->getOptions(1234);
    EXPECT_EQ(0, options.size());
484 485
}

486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
// Check that multiple options of the same type may be retrieved by using
// Pkt6::getOptions or Pkt6::getNonCopiedOptions. In the former case, also
// check that retrieved options are copied when Pkt6::setCopyRetrievedOptions
// is enabled.
TEST_F(Pkt6Test, getOptions) {
    NakedPkt6 pkt(DHCPV6_SOLICIT, 1234);
    OptionPtr opt1(new Option(Option::V6, 1));
    OptionPtr opt2(new Option(Option::V6, 1));
    OptionPtr opt3(new Option(Option::V6, 2));
    OptionPtr opt4(new Option(Option::V6, 2));

    pkt.addOption(opt1);
    pkt.addOption(opt2);
    pkt.addOption(opt3);
    pkt.addOption(opt4);

    // Retrieve options with option code 1.
    OptionCollection options = pkt.getOptions(1);
    ASSERT_EQ(2, options.size());

    OptionCollection::const_iterator opt_it;

    // Make sure that the first option is returned. We're using the pointer
    // to opt1 to find the option.
    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(1, opt1));
    EXPECT_TRUE(opt_it != options.end());

    // Make sure that the second option is returned.
    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(1, opt2));
    EXPECT_TRUE(opt_it != options.end());

    // Retrieve options with option code 2.
    options = pkt.getOptions(2);

    // opt3 and opt4 should exist.
    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(2, opt3));
    EXPECT_TRUE(opt_it != options.end());

    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(2, opt4));
    EXPECT_TRUE(opt_it != options.end());

    // Enable copying options when they are retrieved.
    pkt.setCopyRetrievedOptions(true);

    options = pkt.getOptions(1);
    ASSERT_EQ(2, options.size());

    // Both retrieved options should be copied so an attempt to find them
    // using option pointer should fail. Original pointers should have
    // been replaced with new instances.
    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(1, opt1));
    EXPECT_TRUE(opt_it == options.end());

    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(1, opt2));
    EXPECT_TRUE(opt_it == options.end());

    // Return instances of options with the option code 1 and make sure
    // that copies of the options were used to replace original options
    // in the packet.
    OptionCollection options_modified = pkt.getNonCopiedOptions(1);
    for (OptionCollection::const_iterator opt_it_modified = options_modified.begin();
         opt_it_modified != options_modified.end(); ++opt_it_modified) {
        opt_it = std::find(options.begin(), options.end(), *opt_it_modified);
        ASSERT_TRUE(opt_it != options.end());
    }

    // Let's check that remaining two options haven't been affected by
    // retrieving the options with option code 1.
    options = pkt.getNonCopiedOptions(2);
    ASSERT_EQ(2, options.size());

    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(2, opt3));
    EXPECT_TRUE(opt_it != options.end());

    opt_it = std::find(options.begin(), options.end(),
                       std::pair<const unsigned int, OptionPtr>(2, opt4));
    EXPECT_TRUE(opt_it != options.end());
}

572
TEST_F(Pkt6Test, Timestamp) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
573
    boost::scoped_ptr<Pkt6> pkt(new Pkt6(DHCPV6_SOLICIT, 0x020304));
574 575 576 577

    // Just after construction timestamp is invalid
    ASSERT_TRUE(pkt->getTimestamp().is_not_a_date_time());

578 579 580 581
    // Update packet time.
    pkt->updateTimestamp();

    // Get updated packet time.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
582
    boost::posix_time::ptime ts_packet = pkt->getTimestamp();
583 584 585 586 587

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

    // Check current time.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
588 589
    boost::posix_time::ptime ts_now =
        boost::posix_time::microsec_clock::universal_time();
590 591

    // Calculate period between packet time and now.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
592
    boost::posix_time::time_period ts_period(ts_packet, ts_now);
593 594 595

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

598 599 600 601
// This test verifies that getName() method returns proper
// packet type names.
TEST_F(Pkt6Test, getName) {
    // Check all possible packet types
602
    for (unsigned itype = 0; itype < 256; ++itype) {
603 604 605
        uint8_t type = itype;

        switch (type) {
606 607 608 609
        case DHCPV6_ADVERTISE:
            EXPECT_STREQ("ADVERTISE", Pkt6::getName(type));
            break;

610 611 612 613 614 615 616 617
        case DHCPV6_CONFIRM:
            EXPECT_STREQ("CONFIRM", Pkt6::getName(type));
            break;

        case DHCPV6_DECLINE:
            EXPECT_STREQ("DECLINE", Pkt6::getName(type));
            break;

618 619 620 621 622 623 624 625
        case DHCPV6_DHCPV4_QUERY:
            EXPECT_STREQ("DHCPV4_QUERY", Pkt6::getName(type));
            break;

        case DHCPV6_DHCPV4_RESPONSE:
            EXPECT_STREQ("DHCPV4_RESPONSE", Pkt6::getName(type));
            break;

626 627 628 629 630
        case DHCPV6_INFORMATION_REQUEST:
            EXPECT_STREQ("INFORMATION_REQUEST",
                         Pkt6::getName(type));
            break;

631 632 633 634 635 636 637 638
        case DHCPV6_LEASEQUERY:
            EXPECT_STREQ("LEASEQUERY", Pkt6::getName(type));
            break;

        case DHCPV6_LEASEQUERY_REPLY:
            EXPECT_STREQ("LEASEQUERY_REPLY", Pkt6::getName(type));
            break;

639 640 641 642
        case DHCPV6_REBIND:
            EXPECT_STREQ("REBIND", Pkt6::getName(type));
            break;

643 644 645 646 647 648 649 650 651 652 653 654
        case DHCPV6_RECONFIGURE:
            EXPECT_STREQ("RECONFIGURE", Pkt6::getName(type));
            break;

        case DHCPV6_RELAY_FORW:
            EXPECT_STREQ("RELAY_FORWARD", Pkt6::getName(type));
            break;

        case DHCPV6_RELAY_REPL:
            EXPECT_STREQ("RELAY_REPLY", Pkt6::getName(type));
            break;

655 656 657 658 659 660 661 662
        case DHCPV6_RELEASE:
            EXPECT_STREQ("RELEASE", Pkt6::getName(type));
            break;

        case DHCPV6_RENEW:
            EXPECT_STREQ("RENEW", Pkt6::getName(type));
            break;

663 664 665 666
        case DHCPV6_REPLY:
            EXPECT_STREQ("REPLY", Pkt6::getName(type));
            break;

667 668 669 670 671 672 673 674 675 676 677 678 679 680
        case DHCPV6_REQUEST:
            EXPECT_STREQ("REQUEST", Pkt6::getName(type));
            break;

        case DHCPV6_SOLICIT:
            EXPECT_STREQ("SOLICIT", Pkt6::getName(type));
            break;

        default:
            EXPECT_STREQ("UNKNOWN", Pkt6::getName(type));
        }
    }
}

681 682 683 684
// This test verifies that a fancy solicit that passed through two
// relays can be parsed properly. See capture2() method description
// for details regarding the packet.
TEST_F(Pkt6Test, relayUnpack) {
685
    Pkt6Ptr msg(capture2());
686 687 688 689 690 691 692 693 694 695

    EXPECT_NO_THROW(msg->unpack());

    EXPECT_EQ(DHCPV6_SOLICIT, msg->getType());
    EXPECT_EQ(217, msg->len());

    ASSERT_EQ(2, msg->relay_info_.size());

    OptionPtr opt;

696
    // Part 1: Check options inserted by the first relay
697 698 699 700 701 702 703 704 705 706 707 708

    // There should be 2 options in first relay
    EXPECT_EQ(2, msg->relay_info_[0].options_.size());

    // There should be interface-id option
    ASSERT_TRUE(opt = msg->getRelayOption(D6O_INTERFACE_ID, 0));
    OptionBuffer data = opt->getData();
    EXPECT_EQ(32, opt->len()); // 28 bytes of data + 4 bytes header
    EXPECT_EQ(data.size(), 28);
    // That's a strange interface-id, but this is a real life example
    EXPECT_TRUE(0 == memcmp("ISAM144|299|ipv6|nt:vp:1:110", &data[0], 28));

709
    // Get the remote-id option
710 711
    ASSERT_TRUE(opt = msg->getRelayOption(D6O_REMOTE_ID, 0));
    EXPECT_EQ(22, opt->len()); // 18 bytes of data + 4 bytes header
Tomek Mrugalski's avatar
Tomek Mrugalski committed
712
    boost::shared_ptr<OptionCustom> custom = boost::dynamic_pointer_cast<OptionCustom>(opt);
713 714 715 716

    uint32_t vendor_id = custom->readInteger<uint32_t>(0);
    EXPECT_EQ(6527, vendor_id); // 6527 = Panthera Networks

Francis Dupont's avatar
Francis Dupont committed
717 718 719
    uint8_t expected_remote_id[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0xb0,
                                     0x33, 0x41, 0x00, 0x00, 0x21, 0x5c,
                                     0x18, 0xa9 };
720 721 722 723
    OptionBuffer remote_id = custom->readBinary(1);
    ASSERT_EQ(sizeof(expected_remote_id), remote_id.size());
    ASSERT_EQ(0, memcmp(expected_remote_id, &remote_id[0], remote_id.size()));

724
    // Part 2: Check options inserted by the second relay
725

726
    // Get the interface-id from the second relay
727 728 729 730 731 732
    ASSERT_TRUE(opt = msg->getRelayOption(D6O_INTERFACE_ID, 1));
    data = opt->getData();
    EXPECT_EQ(25, opt->len()); // 21 bytes + 4 bytes header
    EXPECT_EQ(data.size(), 21);
    EXPECT_TRUE(0 == memcmp("ISAM144 eth 1/1/05/01", &data[0], 21));

733
    // Get the remote-id option
734 735
    ASSERT_TRUE(opt = msg->getRelayOption(D6O_REMOTE_ID, 1));
    EXPECT_EQ(8, opt->len());
Tomek Mrugalski's avatar
Tomek Mrugalski committed
736
    custom = boost::dynamic_pointer_cast<OptionCustom>(opt);
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754

    vendor_id = custom->readInteger<uint32_t>(0);
    EXPECT_EQ(3561, vendor_id); // 3561 = Broadband Forum
    // @todo: See if we can validate empty remote-id field

    // Let's check if there is no leak between options stored in
    // the SOLICIT message and the relay.
    EXPECT_FALSE(opt = msg->getRelayOption(D6O_IA_NA, 1));


    // Part 3: Let's check options in the message itself
    // This is not redundant compared to other direct messages tests,
    // as we parsed it differently
    EXPECT_EQ(DHCPV6_SOLICIT, msg->getType());
    EXPECT_EQ(0x6b4fe2, msg->getTransid());

    ASSERT_TRUE(opt = msg->getOption(D6O_CLIENTID));
    EXPECT_EQ(18, opt->len()); // 14 bytes of data + 4 bytes of header
Francis Dupont's avatar
Francis Dupont committed
755 756 757
    uint8_t expected_client_id[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0xb0,
                                     0x33, 0x41, 0x00, 0x00, 0x21, 0x5c,
                                     0x18, 0xa9 };
758 759 760 761 762
    data = opt->getData();
    ASSERT_EQ(data.size(), sizeof(expected_client_id));
    ASSERT_EQ(0, memcmp(&data[0], expected_client_id, data.size()));

    ASSERT_TRUE(opt = msg->getOption(D6O_IA_NA));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
763 764
    boost::shared_ptr<Option6IA> ia =
        boost::dynamic_pointer_cast<Option6IA>(opt);
765 766 767 768 769 770 771
    ASSERT_TRUE(ia);
    EXPECT_EQ(1, ia->getIAID());
    EXPECT_EQ(0xffffffff, ia->getT1());
    EXPECT_EQ(0xffffffff, ia->getT2());

    ASSERT_TRUE(opt = msg->getOption(D6O_ELAPSED_TIME));
    EXPECT_EQ(6, opt->len()); // 2 bytes of data + 4 bytes of header
Tomek Mrugalski's avatar
Tomek Mrugalski committed
772 773
    boost::shared_ptr<OptionInt<uint16_t> > elapsed =
        boost::dynamic_pointer_cast<OptionInt<uint16_t> > (opt);
774 775 776 777
    ASSERT_TRUE(elapsed);
    EXPECT_EQ(0, elapsed->getValue());

    ASSERT_TRUE(opt = msg->getOption(D6O_ORO));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
778 779
    boost::shared_ptr<OptionIntArray<uint16_t> > oro =
        boost::dynamic_pointer_cast<OptionIntArray<uint16_t> > (opt);
780 781 782 783 784 785 786 787 788 789 790
    const std::vector<uint16_t> oro_list = oro->getValues();
    EXPECT_EQ(3, oro_list.size());
    EXPECT_EQ(23, oro_list[0]);
    EXPECT_EQ(242, oro_list[1]);
    EXPECT_EQ(243, oro_list[2]);
}

// This test verified that message with relay information can be
// packed and then unpacked.
TEST_F(Pkt6Test, relayPack) {

791
    scoped_ptr<Pkt6> parent(new Pkt6(DHCPV6_ADVERTISE, 0x020304));
792 793 794

    Pkt6::RelayInfo relay1;
    relay1.msg_type_ = DHCPV6_RELAY_REPL;
795
    relay1.hop_count_ = 17; // not very meaningful, but useful for testing
796 797 798 799
    relay1.linkaddr_ = IOAddress("2001:db8::1");
    relay1.peeraddr_ = IOAddress("fe80::abcd");

    uint8_t relay_opt_data[] = { 1, 2, 3, 4, 5, 6, 7, 8};
800 801
    vector<uint8_t> relay_data(relay_opt_data,
                               relay_opt_data + sizeof(relay_opt_data));
802 803 804

    OptionPtr optRelay1(new Option(Option::V6, 200, relay_data));

805
    relay1.options_.insert(make_pair(optRelay1->getType(), optRelay1));
806 807 808 809

    OptionPtr opt1(new Option(Option::V6, 100));
    OptionPtr opt2(new Option(Option::V6, 101));
    OptionPtr opt3(new Option(Option::V6, 102));
810
    // Let's not use zero-length option type 3 as it is IA_NA
811 812 813 814 815 816 817 818 819

    parent->addRelayInfo(relay1);

    parent->addOption(opt1);
    parent->addOption(opt2);
    parent->addOption(opt3);

    EXPECT_EQ(DHCPV6_ADVERTISE, parent->getType());

820
    EXPECT_NO_THROW(parent->pack());
821

Francis Dupont's avatar
Francis Dupont committed
822 823
    EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN
              + 3 * Option::OPTION6_HDR_LEN // ADVERTISE
824 825
              + Pkt6::DHCPV6_RELAY_HDR_LEN // Relay header
              + Option::OPTION6_HDR_LEN // Relay-msg
826 827 828
              + optRelay1->len(),
              parent->len());

829 830 831 832
    // Create second packet,based on assembled data from the first one
    scoped_ptr<Pkt6> clone(new Pkt6(static_cast<const uint8_t*>(
                                    parent->getBuffer().getData()),
                                    parent->getBuffer().getLength()));
833

834
    // Now recreate options list
835
    EXPECT_NO_THROW( clone->unpack() );
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

    // transid, message-type should be the same as before
    EXPECT_EQ(parent->getTransid(), parent->getTransid());
    EXPECT_EQ(DHCPV6_ADVERTISE, clone->getType());

    EXPECT_TRUE( clone->getOption(100));
    EXPECT_TRUE( clone->getOption(101));
    EXPECT_TRUE( clone->getOption(102));
    EXPECT_FALSE(clone->getOption(103));

    // Now check relay info
    ASSERT_EQ(1, clone->relay_info_.size());
    EXPECT_EQ(DHCPV6_RELAY_REPL, clone->relay_info_[0].msg_type_);
    EXPECT_EQ(17, clone->relay_info_[0].hop_count_);
    EXPECT_EQ("2001:db8::1", clone->relay_info_[0].linkaddr_.toText());
    EXPECT_EQ("fe80::abcd", clone->relay_info_[0].peeraddr_.toText());

    // There should be exactly one option
    EXPECT_EQ(1, clone->relay_info_[0].options_.size());
    OptionPtr opt = clone->getRelayOption(200, 0);
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->getType() , optRelay1->getType());
    EXPECT_EQ(opt->len(), optRelay1->len());
    OptionBuffer data = opt->getData();
    ASSERT_EQ(data.size(), sizeof(relay_opt_data));
861
    EXPECT_EQ(0, memcmp(&data[0], relay_opt_data, sizeof(relay_opt_data)));
862 863 864 865 866 867 868 869 870 871

    // As we have a nicely built relay packet we can check
    // that the functions to get the peer and link addreses work
    EXPECT_EQ("2001:db8::1", clone->getRelay6LinkAddress(0).toText());
    EXPECT_EQ("fe80::abcd", clone->getRelay6PeerAddress(0).toText());

    vector<uint8_t>binary = clone->getRelay6LinkAddress(0).toBytes();
    uint8_t expected0[] = {0x20, 1, 0x0d, 0xb8, 0, 0, 0, 0,
                           0, 0, 0, 0, 0, 0, 0, 1};
    EXPECT_EQ(0, memcmp(expected0, &binary[0], 16));
872
}
873

874
TEST_F(Pkt6Test, getRelayOption) {
875
    NakedPkt6Ptr msg(boost::dynamic_pointer_cast<NakedPkt6>(capture2()));
876 877 878 879 880 881 882
    ASSERT_TRUE(msg);

    ASSERT_NO_THROW(msg->unpack());
    ASSERT_EQ(2, msg->relay_info_.size());

    OptionPtr opt_iface_id = msg->getNonCopiedRelayOption(D6O_INTERFACE_ID, 0);
    ASSERT_TRUE(opt_iface_id);
883

884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
    OptionPtr opt_iface_id_returned = msg->getRelayOption(D6O_INTERFACE_ID, 0);
    ASSERT_TRUE(opt_iface_id_returned);

    EXPECT_TRUE(opt_iface_id == opt_iface_id_returned);

    msg->setCopyRetrievedOptions(true);

    opt_iface_id_returned = msg->getRelayOption(D6O_INTERFACE_ID, 0);
    EXPECT_FALSE(opt_iface_id == opt_iface_id_returned);

    opt_iface_id = msg->getNonCopiedRelayOption(D6O_INTERFACE_ID, 0);
    EXPECT_TRUE(opt_iface_id == opt_iface_id_returned);
}

// This test verifies that options added by relays to the message can be
899 900 901
// accessed and retrieved properly
TEST_F(Pkt6Test, getAnyRelayOption) {

902
    boost::scoped_ptr<NakedPkt6> msg(new NakedPkt6(DHCPV6_ADVERTISE, 0x020304));
903 904 905 906 907 908 909 910 911 912
    msg->addOption(generateRandomOption(300));

    // generate options for relay1
    Pkt6::RelayInfo relay1;

    // generate 3 options with code 200,201,202 and random content
    OptionPtr relay1_opt1(generateRandomOption(200));
    OptionPtr relay1_opt2(generateRandomOption(201));
    OptionPtr relay1_opt3(generateRandomOption(202));

913 914 915
    relay1.options_.insert(make_pair(200, relay1_opt1));
    relay1.options_.insert(make_pair(201, relay1_opt2));
    relay1.options_.insert(make_pair(202, relay1_opt3));
916 917 918 919 920 921 922
    msg->addRelayInfo(relay1);

    // generate options for relay2
    Pkt6::RelayInfo relay2;
    OptionPtr relay2_opt1(new Option(Option::V6, 100));
    OptionPtr relay2_opt2(new Option(Option::V6, 101));
    OptionPtr relay2_opt3(new Option(Option::V6, 102));
Francis Dupont's avatar
Francis Dupont committed
923 924
    OptionPtr relay2_opt4(new Option(Option::V6, 200));
    // the same code as relay1_opt3
925 926 927 928
    relay2.options_.insert(make_pair(100, relay2_opt1));
    relay2.options_.insert(make_pair(101, relay2_opt2));
    relay2.options_.insert(make_pair(102, relay2_opt3));
    relay2.options_.insert(make_pair(200, relay2_opt4));
929 930 931 932 933
    msg->addRelayInfo(relay2);

    // generate options for relay3
    Pkt6::RelayInfo relay3;
    OptionPtr relay3_opt1(generateRandomOption(200, 7));
934
    relay3.options_.insert(make_pair(200, relay3_opt1));
935 936 937 938 939 940 941 942
    msg->addRelayInfo(relay3);

    // Ok, so we now have a packet that traversed the following network:
    // client---relay3---relay2---relay1---server

    // First check that the getAnyRelayOption does not confuse client options
    // and relay options
    // 300 is a client option, present in the message itself.
Francis Dupont's avatar
Francis Dupont committed
943 944
    OptionPtr opt =
        msg->getAnyRelayOption(300, Pkt6::RELAY_SEARCH_FROM_CLIENT);
945 946 947 948 949 950
    EXPECT_FALSE(opt);
    opt = msg->getAnyRelayOption(300, Pkt6::RELAY_SEARCH_FROM_SERVER);
    EXPECT_FALSE(opt);
    opt = msg->getAnyRelayOption(300, Pkt6::RELAY_GET_FIRST);
    EXPECT_FALSE(opt);
    opt = msg->getAnyRelayOption(300, Pkt6::RELAY_GET_LAST);
951 952 953 954 955 956 957 958
    EXPECT_FALSE(opt);

    // Option 200 is added in every relay.

    // We want to get that one inserted by relay3 (first match, starting from
    // closest to the client.
    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_SEARCH_FROM_CLIENT);
    ASSERT_TRUE(opt);
959
    EXPECT_TRUE(opt->equals(relay3_opt1));
960
    EXPECT_TRUE(opt == relay3_opt1);
961 962 963 964 965

    // We want to ge that one inserted by relay1 (first match, starting from
    // closest to the server.
    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_SEARCH_FROM_SERVER);
    ASSERT_TRUE(opt);
966
    EXPECT_TRUE(opt->equals(relay1_opt1));
967
    EXPECT_TRUE(opt == relay1_opt1);
968 969

    // We just want option from the first relay (closest to the client)
970
    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_GET_FIRST);
971
    ASSERT_TRUE(opt);
972
    EXPECT_TRUE(opt->equals(relay3_opt1));
973
    EXPECT_TRUE(opt == relay3_opt1);
974 975

    // We just want option from the last relay (closest to the server)
976
    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_GET_LAST);
977
    ASSERT_TRUE(opt);
978
    EXPECT_TRUE(opt->equals(relay1_opt1));
979 980 981 982 983 984 985 986 987 988 989 990
    EXPECT_TRUE(opt == relay1_opt1);

    // Enable copying options when they are retrieved and redo the tests
    // but expect that options are still equal but different pointers
    // are returned.
    msg->setCopyRetrievedOptions(true);

    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_SEARCH_FROM_CLIENT);
    ASSERT_TRUE(opt);
    EXPECT_TRUE(opt->equals(relay3_opt1));
    EXPECT_FALSE(opt == relay3_opt1);
    // Test that option copy has replaced the original option within the
Andrei Pavel's avatar
Andrei Pavel committed
991
    // packet. We achieve that by calling a variant of the method which
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
    // retrieved non-copied option.
    relay3_opt1 = msg->getNonCopiedAnyRelayOption(200, Pkt6::RELAY_SEARCH_FROM_CLIENT);
    ASSERT_TRUE(relay3_opt1);
    EXPECT_TRUE(opt == relay3_opt1);

    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_SEARCH_FROM_SERVER);
    ASSERT_TRUE(opt);
    EXPECT_TRUE(opt->equals(relay1_opt1));
    EXPECT_FALSE(opt == relay1_opt1);
    relay1_opt1 = msg->getNonCopiedAnyRelayOption(200, Pkt6::RELAY_SEARCH_FROM_SERVER);
    ASSERT_TRUE(relay1_opt1);
    EXPECT_TRUE(opt == relay1_opt1);

    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_GET_FIRST);
    ASSERT_TRUE(opt);
    EXPECT_TRUE(opt->equals(relay3_opt1));
    EXPECT_FALSE(opt == relay3_opt1);
    relay3_opt1 = msg->getNonCopiedAnyRelayOption(200, Pkt6::RELAY_GET_FIRST);
    ASSERT_TRUE(relay3_opt1);
    EXPECT_TRUE(opt == relay3_opt1);

    opt = msg->getAnyRelayOption(200, Pkt6::RELAY_GET_LAST);
    ASSERT_TRUE(opt);
    EXPECT_TRUE(opt->equals(relay1_opt1));
    EXPECT_FALSE(opt == relay1_opt1);
    relay1_opt1 = msg->getNonCopiedAnyRelayOption(200, Pkt6::RELAY_GET_LAST);
    ASSERT_TRUE(relay1_opt1);
    EXPECT_TRUE(opt == relay1_opt1);

    // Disable copying options and continue with other tests.
    msg->setCopyRetrievedOptions(false);
1023 1024 1025 1026 1027

    // Let's try to ask for something that is inserted by the middle relay
    // only.
    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_SEARCH_FROM_SERVER);
    ASSERT_TRUE(opt);
1028
    EXPECT_TRUE(opt->equals(relay2_opt1));
1029 1030 1031

    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_SEARCH_FROM_CLIENT);
    ASSERT_TRUE(opt);
1032
    EXPECT_TRUE(opt->equals(relay2_opt1));
1033

1034 1035 1036 1037 1038 1039 1040 1041
    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_GET_FIRST);
    EXPECT_FALSE(opt);

    opt = msg->getAnyRelayOption(100, Pkt6::RELAY_GET_LAST);
    EXPECT_FALSE(opt);

    // Finally, try to get an option that does not exist
    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_GET_FIRST);
1042 1043
    EXPECT_FALSE(opt);

1044
    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_GET_LAST);
1045 1046
    EXPECT_FALSE(opt);

1047 1048 1049 1050 1051
    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_SEARCH_FROM_SERVER);
    EXPECT_FALSE(opt);

    opt = msg->getAnyRelayOption(500, Pkt6::RELAY_SEARCH_FROM_CLIENT);
    EXPECT_FALSE(opt);
1052 1053
}

1054 1055 1056 1057 1058
// Tests whether Pkt6::toText() properly prints out all parameters, including
// relay options: remote-id, interface-id.
TEST_F(Pkt6Test, toText) {

    // This packet contains doubly relayed solicit. The inner-most
Josh Soref's avatar
Josh Soref committed
1059
    // relay-forward contains interface-id and remote-id. We will
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
    // check that these are printed correctly.
    Pkt6Ptr msg(capture2());
    EXPECT_NO_THROW(msg->unpack());

    ASSERT_EQ(2, msg->relay_info_.size());

    string expected =
        "localAddr=[ff05::1:3]:547 remoteAddr=[fe80::1234]:547\n"
        "msgtype=1(SOLICIT), transid=0x6b4fe2\n"
        "type=00001, len=00014: 00:01:00:01:18:b0:33:41:00:00:21:5c:18:a9\n"
        "type=00003(IA_NA), len=00012: iaid=1, t1=4294967295, t2=4294967295\n"
        "type=00006, len=00006: 23(uint16) 242(uint16) 243(uint16)\n"
        "type=00008, len=00002: 0 (uint16)\n"
        "2 relay(s):\n"
        "relay[0]: msg-type=12(RELAY_FORWARD), hop-count=1,\n"
        "link-address=2001:888:db8:1::, peer-address=fe80::200:21ff:fe5c:18a9, 2 option(s)\n"
        "type=00018, len=00028: 49:53:41:4d:31:34:34:7c:32:39:39:7c:69:70:76:36:7c:6e:74:3a:76:70:3a:31:3a:31:31:30\n"
        "type=00037, len=00018: 6527 (uint32) 0001000118B033410000215C18A9 (binary)\n"
        "relay[1]: msg-type=12(RELAY_FORWARD), hop-count=0,\n"
        "link-address=::, peer-address=fe80::200:21ff:fe5c:18a9, 2 option(s)\n"
        "type=00018, len=00021: 49:53:41:4d:31:34:34:20:65:74:68:20:31:2f:31:2f:30:35:2f:30:31\n"
        "type=00037, len=00004: 3561 (uint32)  (binary)\n";

    EXPECT_EQ(expected, msg->toText());
}

1086 1087 1088 1089 1090 1091
// Tests whether a packet can be assigned to a class and later
// checked if it belongs to a given class
TEST_F(Pkt6Test, clientClasses) {
    Pkt6 pkt(DHCPV6_ADVERTISE, 1234);

    // Default values (do not belong to any class)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1092 1093
    EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
    EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_MODEM));
1094
    EXPECT_TRUE(pkt.getClasses().empty());
1095 1096

    // Add to the first class
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1097 1098 1099
    pkt.addClass(DOCSIS3_CLASS_EROUTER);
    EXPECT_TRUE(pkt.inClass(DOCSIS3_CLASS_EROUTER));
    EXPECT_FALSE(pkt.inClass(DOCSIS3_CLASS_MODEM));
1100
    ASSERT_FALSE(pkt.getClasses().empty());
1101 1102

    // Add to a second class
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1103 1104