dhcp6_test_utils.cc 30.4 KB
Newer Older
1
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// 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.

15
#include <config.h>
16 17
#include <gtest/gtest.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
18
#include <dhcp6/json_config_parser.h>
19
#include <cc/command_interpreter.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
20
#include <string.h>
21

22
using namespace isc::data;
23 24 25
using namespace isc::dhcp;
using namespace isc::asiolink;

26 27 28
namespace isc {
namespace test {

29 30 31 32 33 34 35 36 37 38 39 40 41
Dhcpv6SrvTest::Dhcpv6SrvTest()
:srv_(0) {
    subnet_ = isc::dhcp::Subnet6Ptr
        (new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:1::"),
                                48, 1000, 2000, 3000, 4000));
    subnet_->setIface("eth0");

    pool_ = isc::dhcp::Pool6Ptr
        (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_NA,
                              isc::asiolink::IOAddress("2001:db8:1:1::"),
                              64));
    subnet_->addPool(pool_);

42 43 44
    isc::dhcp::CfgMgr::instance().clear();
    isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_);
    isc::dhcp::CfgMgr::instance().commit();
45 46 47 48 49 50 51 52 53

    // configure PD pool
    pd_pool_ = isc::dhcp::Pool6Ptr
        (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_PD,
                              isc::asiolink::IOAddress("2001:db8:1:2::"),
                              64, 80));
    subnet_->addPool(pd_pool_);
}

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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
// Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
// It returns IAADDR option for each chaining with checkIAAddr method.
boost::shared_ptr<Option6IAAddr>
Dhcpv6SrvTest::checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
                          uint32_t expected_t1, uint32_t expected_t2) {
    OptionPtr tmp = rsp->getOption(D6O_IA_NA);
    // Can't use ASSERT_TRUE() in method that returns something
    if (!tmp) {
        ADD_FAILURE() << "IA_NA option not present in response";
        return (boost::shared_ptr<Option6IAAddr>());
    }

    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    if (!ia) {
        ADD_FAILURE() << "IA_NA cannot convert option ptr to Option6";
        return (boost::shared_ptr<Option6IAAddr>());
    }

    EXPECT_EQ(expected_iaid, ia->getIAID());
    EXPECT_EQ(expected_t1, ia->getT1());
    EXPECT_EQ(expected_t2, ia->getT2());

    tmp = ia->getOption(D6O_IAADDR);
    boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
    return (addr);
}

boost::shared_ptr<Option6IAPrefix>
Dhcpv6SrvTest::checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
                          uint32_t expected_t1, uint32_t expected_t2) {
    OptionPtr tmp = rsp->getOption(D6O_IA_PD);
    // Can't use ASSERT_TRUE() in method that returns something
    if (!tmp) {
        ADD_FAILURE() << "IA_PD option not present in response";
        return (boost::shared_ptr<Option6IAPrefix>());
    }

    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    if (!ia) {
        ADD_FAILURE() << "IA_PD cannot convert option ptr to Option6";
        return (boost::shared_ptr<Option6IAPrefix>());
    }

    EXPECT_EQ(expected_iaid, ia->getIAID());
    EXPECT_EQ(expected_t1, ia->getT1());
    EXPECT_EQ(expected_t2, ia->getT2());

    tmp = ia->getOption(D6O_IAPREFIX);
    boost::shared_ptr<Option6IAPrefix> addr = boost::dynamic_pointer_cast<Option6IAPrefix>(tmp);
    return (addr);
}

// Checks if the lease sent to client is present in the database
// and is valid when checked agasint the configured subnet
Lease6Ptr
Dhcpv6SrvTest::checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
                          boost::shared_ptr<Option6IAAddr> addr) {
    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);

    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
                                                            addr->getAddress());
    if (!lease) {
116
        std::cout << "Lease for " << addr->getAddress()
117 118 119 120
                  << " not found in the database backend.";
        return (Lease6Ptr());
    }

121
    EXPECT_EQ(addr->getAddress(), lease->addr_);
122 123 124 125 126 127 128
    EXPECT_TRUE(*lease->duid_ == *duid);
    EXPECT_EQ(ia->getIAID(), lease->iaid_);
    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);

    return (lease);
}

129 130 131 132 133 134 135 136 137 138 139 140
isc::dhcp::Lease6Ptr
Dhcpv6SrvTest::checkLease(const isc::dhcp::Lease6& lease) {
    Lease6Ptr lease_db = LeaseMgrFactory::instance().getLease6(lease.type_,
                                                               lease.addr_);
    if (!lease_db) {
        return (Lease6Ptr());
    }

    EXPECT_TRUE(lease_db->matches(lease));
    return (lease_db);
}

141 142 143 144 145 146 147 148
Lease6Ptr
Dhcpv6SrvTest::checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
                            boost::shared_ptr<Option6IAPrefix> prefix){
    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_pd);

    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
                                                            prefix->getAddress());
    if (!lease) {
149
        std::cout << "PD lease for " << prefix->getAddress()
150 151 152 153
                  << " not found in the database backend.";
        return (Lease6Ptr());
    }

154
    EXPECT_EQ(prefix->getAddress(), lease->addr_);
155 156 157 158 159 160 161 162
    EXPECT_TRUE(*lease->duid_ == *duid);
    EXPECT_EQ(ia->getIAID(), lease->iaid_);
    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);

    return (lease);
}


163 164 165
Pkt6Ptr
Dhcpv6SrvTest::createMessage(uint8_t message_type, Lease::Type lease_type,
                             const IOAddress& addr, const uint8_t prefix_len,
166
                             const uint32_t iaid) {
167 168
    Pkt6Ptr msg = Pkt6Ptr(new Pkt6(message_type, 1234));
    msg->setRemoteAddr(IOAddress("fe80::abcd"));
169
    msg->setIface("eth0");
170 171 172
    msg->addOption(createIA(lease_type, addr, prefix_len, iaid));
    return (msg);
}
173

174 175 176 177
Option6IAPtr
Dhcpv6SrvTest::createIA(isc::dhcp::Lease::Type lease_type,
                        const isc::asiolink::IOAddress& addr,
                        const uint8_t prefix_len, const uint32_t iaid) {
178 179 180 181 182 183 184 185 186 187 188
    uint16_t code;
    OptionPtr subopt;
    switch (lease_type) {
    case Lease::TYPE_NA:
        code = D6O_IA_NA;
        subopt.reset(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
        break;
    case Lease::TYPE_PD:
        code = D6O_IA_PD;
        subopt.reset(new Option6IAPrefix(D6O_IAPREFIX, addr, prefix_len,
                                         300, 500));
189
        break;
190
    default:
191 192
        isc_throw(BadValue, "Invalid lease type specified "
                  << static_cast<int>(lease_type));
193 194
    }

195
    Option6IAPtr ia = generateIA(code, iaid, 1500, 3000);
196 197
    ia->addOption(subopt);

198
    return (ia);
199 200
}

201 202
void
Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr,
203
                              const std::string& renew_addr,
204
                              const uint8_t prefix_len, bool insert_before_renew) {
205 206 207 208 209 210 211 212 213 214 215 216
    NakedDhcpv6Srv srv(0);

    const IOAddress existing(existing_addr);
    const IOAddress renew(renew_addr);
    const uint32_t iaid = 234;

    // Generate client-id also duid_
    OptionPtr clientid = generateClientId();

    // Check that the address we are about to use is indeed in pool
    ASSERT_TRUE(subnet_->inPool(type, existing));

217 218 219 220 221 222 223 224
    Lease6Ptr l;
    if (insert_before_renew) {
        // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
        // value on purpose. They should be updated during RENEW.
        Lease6Ptr lease(new Lease6(type, existing, duid_, iaid, 501, 502, 503, 504,
                                   subnet_->getID(), HWAddrPtr(), prefix_len));
        lease->cltt_ = 1234;
        ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
225

226 227 228
        // Check that the lease is really in the database
        l = LeaseMgrFactory::instance().getLease6(type, existing);
        ASSERT_TRUE(l);
229

230 231 232 233 234 235 236 237
        // Check that T1, T2, preferred, valid and cltt really set and not using
        // previous (500, 501, etc.) values
        EXPECT_NE(l->t1_, subnet_->getT1());
        EXPECT_NE(l->t2_, subnet_->getT2());
        EXPECT_NE(l->preferred_lft_, subnet_->getPreferred());
        EXPECT_NE(l->valid_lft_, subnet_->getValid());
        EXPECT_NE(l->cltt_, time(NULL));
    }
238

239
    Pkt6Ptr req = createMessage(DHCPV6_RENEW, type, IOAddress(renew_addr),
240
                                prefix_len, iaid);
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    req->addOption(clientid);
    req->addOption(srv.getServerID());

    // Pass it to the server and hope for a REPLY
    Pkt6Ptr reply = srv.processRenew(req);

    // Check if we get response at all
    checkResponse(reply, DHCPV6_REPLY, 1234);

    // Check DUIDs
    checkServerId(reply, srv.getServerID());
    checkClientId(reply, clientid);

    switch (type) {
    case Lease::TYPE_NA: {
        // Check that IA_NA was returned and that there's an address included
257 258
        boost::shared_ptr<Option6IAAddr>
          addr_opt = checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
259 260 261 262 263 264 265

        ASSERT_TRUE(addr_opt);

        // Check that we've got the address we requested
        checkIAAddr(addr_opt, renew, Lease::TYPE_NA);

        // Check that the lease is really in the database
266
        l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
        ASSERT_TRUE(l);
        break;
    }

    case Lease::TYPE_PD: {
        // Check that IA_NA was returned and that there's an address included
        boost::shared_ptr<Option6IAPrefix> prefix_opt
            = checkIA_PD(reply, 234, subnet_->getT1(), subnet_->getT2());

        ASSERT_TRUE(prefix_opt);

        // Check that we've got the address we requested
        checkIAAddr(prefix_opt, renew, Lease::TYPE_PD);
        EXPECT_EQ(pd_pool_->getLength(), prefix_opt->getLength());

        // Check that the lease is really in the database
283
        l = checkPdLease(duid_, reply->getOption(D6O_IA_PD), prefix_opt);
284 285 286 287 288 289 290 291
        ASSERT_TRUE(l);
        break;
    }
    default:
        isc_throw(BadValue, "Invalid lease type");
    }

    // Check that T1, T2, preferred, valid and cltt were really updated
292 293 294 295
    EXPECT_EQ(subnet_->getT1(), l->t1_);
    EXPECT_EQ(subnet_->getT2(), l->t2_);
    EXPECT_EQ(subnet_->getPreferred(), l->preferred_lft_);
    EXPECT_EQ(subnet_->getValid(), l->valid_lft_);
296 297 298 299 300 301 302 303 304 305

    // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
    int32_t cltt = static_cast<int32_t>(l->cltt_);
    int32_t expected = static_cast<int32_t>(time(NULL));
    // equality or difference by 1 between cltt and expected is ok.
    EXPECT_GE(1, abs(cltt - expected));

    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(renew_addr));
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
void
Dhcpv6SrvTest::testRenewWrongIAID(Lease::Type type, const IOAddress& addr) {

    NakedDhcpv6Srv srv(0);

    const uint32_t transid = 1234;
    const uint32_t valid_iaid = 234;
    const uint32_t bogus_iaid = 456;

    uint8_t prefix_len = (type == Lease::TYPE_PD) ? 128 : pd_pool_->getLength();

    // Quick sanity check that the address we're about to use is ok
    ASSERT_TRUE(subnet_->inPool(type, addr));

    // Check that the lease is NOT in the database
    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_FALSE(l);

    // GenerateClientId() also sets duid_
    OptionPtr clientid = generateClientId();

    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
    // value on purpose. They should be updated during RENEW.
    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
                               501, 502, 503, 504, subnet_->getID(),
                               HWAddrPtr(), prefix_len));
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));

    // Pass it to the server and hope for a REPLY
    // Let's create a RENEW
    Pkt6Ptr renew = createMessage(DHCPV6_RENEW, type, IOAddress(addr), prefix_len,
                                  bogus_iaid);
    renew->addOption(clientid);
    renew->addOption(srv.getServerID());

    // The duid and address matches, but the iaid is different. The server could
    // respond with NoBinding. However, according to
    // draft-ietf-dhc-dhcpv6-stateful-issues-10, the server can also assign a
    // new address. And that's what we expect here.
    Pkt6Ptr reply = srv.processRenew(renew);
    checkResponse(reply, DHCPV6_REPLY, transid);

    // Check that IA_NA was returned and that there's an address included
    boost::shared_ptr<Option6IAAddr>
        addr_opt = checkIA_NA(reply, bogus_iaid, subnet_->getT1(), subnet_->getT2());

    ASSERT_TRUE(addr_opt);

    // Check that we've got the an address
    checkIAAddr(addr_opt, addr_opt->getAddress(), Lease::TYPE_NA);

    // Check that we got a different address than was in the database.
    EXPECT_NE(addr_opt->getAddress().toText(), addr.toText());

    // Check that the lease is really in the database
    l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
    ASSERT_TRUE(l);
}

void
Dhcpv6SrvTest::testRenewSomeoneElsesLease(Lease::Type type, const IOAddress& addr) {

    NakedDhcpv6Srv srv(0);
    const uint32_t valid_iaid = 234;
    const uint32_t transid = 1234;

    uint8_t prefix_len = (type == Lease::TYPE_PD) ? 128 : pd_pool_->getLength();

    // GenerateClientId() also sets duid_
    OptionPtr clientid = generateClientId();

    // Let's create a lease.
    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
                               501, 502, 503, 504, subnet_->getID(),
                               HWAddrPtr(), prefix_len));
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));

    // CASE 3: Lease belongs to a client with different client-id
    Pkt6Ptr renew = createMessage(DHCPV6_RENEW, type, IOAddress(addr), prefix_len,
                                  valid_iaid);
    renew->addOption(generateClientId(13)); // generate different DUID (length 13)
    renew->addOption(srv.getServerID());

    // The iaid and address matches, but the duid is different.
    // The server should not renew it, but assign something else.
    Pkt6Ptr reply = srv.processRenew(renew);
    checkResponse(reply, DHCPV6_REPLY, transid);
    OptionPtr tmp = reply->getOption(D6O_IA_NA);
    ASSERT_TRUE(tmp);

    // Check that IA_?? was returned and that there's proper status code
    // Check that IA_NA was returned and that there's an address included
    boost::shared_ptr<Option6IAAddr>
        addr_opt = checkIA_NA(reply, valid_iaid, subnet_->getT1(), subnet_->getT2());

    ASSERT_TRUE(addr_opt);

    // Check that we've got the an address
    checkIAAddr(addr_opt, addr_opt->getAddress(), Lease::TYPE_NA);

    // Check that we got a different address than was in the database.
    EXPECT_NE(addr_opt->getAddress().toText(), addr.toText());

    // Check that the lease is really in the database
    Lease6Ptr l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
    ASSERT_TRUE(l);
}

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
void
Dhcpv6SrvTest::testRenewReject(Lease::Type type, const IOAddress& addr) {

    NakedDhcpv6Srv srv(0);

    const uint32_t transid = 1234;
    const uint32_t valid_iaid = 234;
    const uint32_t bogus_iaid = 456;

    uint32_t code;
    uint8_t prefix_len;
    if (type == Lease::TYPE_NA) {
        code = D6O_IA_NA;
        prefix_len = 128;
    } else if (type == Lease::TYPE_PD) {
        code = D6O_IA_PD;
        prefix_len = pd_pool_->getLength();
    } else {
        isc_throw(BadValue, "Invalid lease type");
    }

    // Quick sanity check that the address we're about to use is ok
    ASSERT_TRUE(subnet_->inPool(type, addr));

    // GenerateClientId() also sets duid_
    OptionPtr clientid = generateClientId();

    // Check that the lease is NOT in the database
    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_FALSE(l);

    // Let's create a RENEW
    Pkt6Ptr req = createMessage(DHCPV6_RENEW, type, IOAddress(addr), prefix_len,
                                bogus_iaid);
    req->addOption(clientid);
    req->addOption(srv.getServerID());

    // Case 1: No lease known to server

    // Pass it to the server and hope for a REPLY
    Pkt6Ptr reply = srv.processRenew(req);

    // Check if we get response at all
    checkResponse(reply, DHCPV6_REPLY, transid);
    OptionPtr tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
460 461

    // Check that IA_?? was returned and that there's proper status code
462 463
    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    ASSERT_TRUE(ia);
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498

    if (type == Lease::TYPE_PD) {
        // For PD, the check is easy. NoBinding and no prefixes
        checkIA_NAStatusCode(ia, STATUS_NoBinding, subnet_->getT1(), subnet_->getT2());
    } else {
        // For IA, it's more involved, as the server will reject the address
        // (and send it with 0 lifetimes), but will also assign a new address.

        // First, check that the requested address is rejected.
        bool found = false;

        dhcp::OptionCollection options = ia->getOptions();
        for (isc::dhcp::OptionCollection::iterator opt = options.begin();
             opt != options.end(); ++opt) {
            if (opt->second->getType() != D6O_IAADDR) {
                continue;
            }

            dhcp::Option6IAAddrPtr opt_addr =
                boost::dynamic_pointer_cast<isc::dhcp::Option6IAAddr>(opt->second);
            ASSERT_TRUE(opt_addr);

            if (opt_addr->getAddress() != addr) {
                // There may be other addresses, e.g. the newly assigned one
                continue;
            }

            found = true;
            EXPECT_NE(0, opt_addr->getPreferred());
            EXPECT_NE(0, opt_addr->getValid());
        }

        EXPECT_TRUE(found) << "Expected address " << addr.toText()
                           << " with zero lifetimes not found.";
    }
499 500 501 502 503 504 505 506 507 508 509

    // Check that there is no lease added
    l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_FALSE(l);

    // CASE 2: Lease is known and belongs to this client, but to a different IAID

    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
    // value on purpose. They should be updated during RENEW.
    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
                               501, 502, 503, 504, subnet_->getID(),
510
                               HWAddrPtr(), prefix_len));
511 512 513 514 515 516 517 518 519
    lease->cltt_ = 123; // Let's use it as an indicator that the lease
                        // was NOT updated.
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));

    // Pass it to the server and hope for a REPLY
    reply = srv.processRenew(req);
    checkResponse(reply, DHCPV6_REPLY, transid);
    tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
520 521

    // Check that IA_?? was returned and that there's proper status code
522 523
    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    ASSERT_TRUE(ia);
524
    checkIA_NAStatusCode(ia, STATUS_NoBinding, subnet_->getT1(), subnet_->getT2());
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

    // There is a iaid mis-match, so server should respond that there is
    // no such address to renew.

    // CASE 3: Lease belongs to a client with different client-id
    req->delOption(D6O_CLIENTID);
    ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(code));
    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
    req->addOption(generateClientId(13)); // generate different DUID
                                          // (with length 13)

    reply = srv.processRenew(req);
    checkResponse(reply, DHCPV6_REPLY, transid);
    tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
540 541

    // Check that IA_?? was returned and that there's proper status code
542 543
    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    ASSERT_TRUE(ia);
544
    checkIA_NAStatusCode(ia, STATUS_NoBinding, subnet_->getT1(), subnet_->getT2());
545 546 547 548 549 550 551 552 553

    lease = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_TRUE(lease);
    // Verify that the lease was not updated.
    EXPECT_EQ(123, lease->cltt_);

    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
}

554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
void
Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
                                const IOAddress& release_addr) {
    NakedDhcpv6Srv srv(0);

    const uint32_t iaid = 234;

    uint32_t code; // option code of the container (IA_NA or IA_PD)
    uint8_t prefix_len;
    if (type == Lease::TYPE_NA) {
        code = D6O_IA_NA;
        prefix_len = 128;
    } else if (type == Lease::TYPE_PD) {
        code = D6O_IA_PD;
        prefix_len = pd_pool_->getLength();
    } else {
        isc_throw(BadValue, "Invalid lease type");
    }

    // Generate client-id also duid_
    OptionPtr clientid = generateClientId();

    // Check that the address we are about to use is indeed in pool
    ASSERT_TRUE(subnet_->inPool(type, existing));

    // Let's prepopulate the database
580
    Lease6Ptr lease(new Lease6(type, existing, duid_, iaid,
581
                               501, 502, 503, 504, subnet_->getID(),
582
                               HWAddrPtr(), prefix_len));
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));

    // Check that the lease is really in the database
    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, existing);
    ASSERT_TRUE(l);

    // Let's create a RELEASE
    Pkt6Ptr rel = createMessage(DHCPV6_RELEASE, type, release_addr, prefix_len,
                                iaid);
    rel->addOption(clientid);
    rel->addOption(srv.getServerID());

    // Pass it to the server and hope for a REPLY
    Pkt6Ptr reply = srv.processRelease(rel);

    // Check if we get response at all
    checkResponse(reply, DHCPV6_REPLY, 1234);

    OptionPtr tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);

    // Check that IA_NA was returned and that there's an address included
    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
606
    checkIA_NAStatusCode(ia, STATUS_Success, 0, 0);
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
    checkMsgStatusCode(reply, STATUS_Success);

    // There should be no address returned in RELEASE (see RFC3315, 18.2.6)
    // There should be no prefix
    EXPECT_FALSE(tmp->getOption(D6O_IAADDR));
    EXPECT_FALSE(tmp->getOption(D6O_IAPREFIX));

    // Check DUIDs
    checkServerId(reply, srv.getServerID());
    checkClientId(reply, clientid);

    // Check that the lease is really gone in the database
    // get lease by address
    l = LeaseMgrFactory::instance().getLease6(type, release_addr);
    ASSERT_FALSE(l);

    // get lease by subnetid/duid/iaid combination
    l = LeaseMgrFactory::instance().getLease6(type, *duid_, iaid,
                                              subnet_->getID());
    ASSERT_FALSE(l);
}
628

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
void
Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
    NakedDhcpv6Srv srv(0);

    const uint32_t transid = 1234;
    const uint32_t valid_iaid = 234;
    const uint32_t bogus_iaid = 456;

    uint32_t code; // option code of the container (IA_NA or IA_PD)
    uint8_t prefix_len;
    if (type == Lease::TYPE_NA) {
        code = D6O_IA_NA;
        prefix_len = 128;
    } else if (type == Lease::TYPE_PD) {
        code = D6O_IA_PD;
        prefix_len = pd_pool_->getLength();
    } else {
        isc_throw(BadValue, "Invalid lease type");
    }

    // Quick sanity check that the address we're about to use is ok
    ASSERT_TRUE(subnet_->inPool(type, addr));

    // GenerateClientId() also sets duid_
    OptionPtr clientid = generateClientId();

    // Check that the lease is NOT in the database
    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_FALSE(l);

    // Let's create a RELEASE
    Pkt6Ptr rel = createMessage(DHCPV6_RELEASE, type, addr, prefix_len, valid_iaid);
    rel->addOption(clientid);
    rel->addOption(srv.getServerID());

    // Case 1: No lease known to server
    SCOPED_TRACE("CASE 1: No lease known to server");

    // Pass it to the server and hope for a REPLY
    Pkt6Ptr reply = srv.processRelease(rel);

    // Check if we get response at all
    checkResponse(reply, DHCPV6_REPLY, transid);
    OptionPtr tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);
    // Check that IA_NA/IA_PD was returned and that there's status code in it
    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    ASSERT_TRUE(ia);
677
    checkIA_NAStatusCode(ia, STATUS_NoBinding, 0, 0);
678 679 680 681 682 683 684 685 686 687
    checkMsgStatusCode(reply, STATUS_NoBinding);

    // Check that the lease is not there
    l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_FALSE(l);

    // CASE 2: Lease is known and belongs to this client, but to a different IAID
    SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");

    Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid, 501, 502, 503,
688
                               504, subnet_->getID(), HWAddrPtr(), prefix_len));
689 690 691 692 693 694 695 696 697 698 699 700
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));

    // Let's create a different RELEASE, with a bogus iaid
    rel = createMessage(DHCPV6_RELEASE, type, addr, prefix_len, bogus_iaid);
    rel->addOption(clientid);
    rel->addOption(srv.getServerID());

    // Pass it to the server and hope for a REPLY
    reply = srv.processRelease(rel);
    checkResponse(reply, DHCPV6_REPLY, transid);
    tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
701 702

    // Check that IA_?? was returned and that there's proper status code
703 704
    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    ASSERT_TRUE(ia);
705
    checkIA_NAStatusCode(ia, STATUS_NoBinding, 0, 0);
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
    checkMsgStatusCode(reply, STATUS_NoBinding);

    // Check that the lease is still there
    l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_TRUE(l);

    // CASE 3: Lease belongs to a client with different client-id
    SCOPED_TRACE("CASE 3: Lease belongs to a client with different client-id");

    rel->delOption(D6O_CLIENTID);
    ia = boost::dynamic_pointer_cast<Option6IA>(rel->getOption(code));
    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
    rel->addOption(generateClientId(13)); // generate different DUID
                                          // (with length 13)

    reply = srv.processRelease(rel);
    checkResponse(reply, DHCPV6_REPLY, transid);
    tmp = reply->getOption(code);
    ASSERT_TRUE(tmp);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
725 726

    // Check that IA_?? was returned and that there's proper status code
727 728
    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
    ASSERT_TRUE(ia);
729
    checkIA_NAStatusCode(ia, STATUS_NoBinding, 0, 0);
730 731 732 733 734 735 736 737 738 739
    checkMsgStatusCode(reply, STATUS_NoBinding);

    // Check that the lease is still there
    l = LeaseMgrFactory::instance().getLease6(type, addr);
    ASSERT_TRUE(l);

    // Finally, let's cleanup the database
    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
}

740 741
void
Dhcpv6SrvTest::configure(const std::string& config) {
742 743 744 745 746
    configure(config, srv_);
}

void
Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) {
747 748 749 750
    ElementPtr json = data::Element::fromJSON(config);
    ConstElementPtr status;

    // Configure the server and make sure the config is accepted
751
    EXPECT_NO_THROW(status = configureDhcp6Server(srv, json));
752 753
    ASSERT_TRUE(status);
    int rcode;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
754
    ConstElementPtr comment = isc::config::parseAnswer(rcode, status);
755
    ASSERT_EQ(0, rcode);
756 757

    CfgMgr::instance().commit();
758 759
}

760 761 762 763 764 765 766 767 768 769 770
// Generate IA_NA option with specified parameters
boost::shared_ptr<Option6IA>
NakedDhcpv6SrvTest::generateIA(uint16_t type, uint32_t iaid, uint32_t t1,
                               uint32_t t2) {
    boost::shared_ptr<Option6IA> ia =
        boost::shared_ptr<Option6IA>(new Option6IA(type, iaid));
    ia->setT1(t1);
    ia->setT2(t2);
    return (ia);
}

771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
bool
Dhcpv6SrvTest::compareOptions(const isc::dhcp::OptionPtr& option1,
                              const isc::dhcp::OptionPtr& option2) {
    if ((!option1 && option2) || (option1 && !option2)) {
        return (false);
    }
    if (!option1 && !option2) {
        return (true);
    }

    // We could start by comparing option codes and option lengths
    // here, but it's just a waste of time. These are tests, so they
    // don't have to be super performant. The pack+memcmp approach
    // verifies all in one go.

    isc::util::OutputBuffer buf1(0);
    isc::util::OutputBuffer buf2(0);

    option1->pack(buf1);
    option2->pack(buf2);

    if (buf1.getLength() != buf2.getLength()) {
        return (false);
    }

    // memcmp returns 0 when equal.
    return (!memcmp(buf1.getData(), buf2.getData(), buf1.getLength()));
}

800 801 802
void
NakedDhcpv6SrvTest::checkIA_NAStatusCode(
    const boost::shared_ptr<isc::dhcp::Option6IA>& ia,
803 804
    uint16_t expected_status_code, uint32_t expected_t1, uint32_t expected_t2,
    bool check_addr)
805 806 807 808
{
    // Make sure there is no address assigned. Depending on the situation,
    // the server will either not return the address at all and sometimes
    // it will return it with zeroed lifetimes.
809 810 811 812 813 814 815 816 817 818 819 820 821 822
    if (check_addr) {
        dhcp::OptionCollection options = ia->getOptions();
        for (isc::dhcp::OptionCollection::iterator opt = options.begin();
             opt != options.end(); ++opt) {
            if (opt->second->getType() != D6O_IAADDR) {
                continue;
            }

            dhcp::Option6IAAddrPtr addr =
                boost::dynamic_pointer_cast<isc::dhcp::Option6IAAddr>(opt->second);
            ASSERT_TRUE(addr);

            EXPECT_EQ(0, addr->getPreferred());
            EXPECT_EQ(0, addr->getValid());
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
        }
    }

    // T1, T2 should NOT be zeroed. draft-ietf-dhc-dhcpv6-stateful-issues-10,
    // section 4.4.6 says says that T1,T2 should be consistent along all
    // provided IA options.
    EXPECT_EQ(expected_t1, ia->getT1());
    EXPECT_EQ(expected_t2, ia->getT2());

    isc::dhcp::OptionCustomPtr status =
        boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
        (ia->getOption(D6O_STATUS_CODE));

    // It is ok to not include status success as this is the default
    // behavior
    if (expected_status_code == STATUS_Success && !status) {
        return;
    }

    EXPECT_TRUE(status);

    if (status) {
        // We don't have dedicated class for status code, so let's
        // just interpret first 2 bytes as status. Remainder of the
        // status code option content is just a text explanation
        // what went wrong.
        EXPECT_EQ(static_cast<uint16_t>(expected_status_code),
                  status->readInteger<uint16_t>(0));
    }
}
853

854 855
}; // end of isc::test namespace
}; // end of isc namespace