|
@@ -59,6 +59,7 @@ public:
|
|
|
using Dhcpv6Srv::processSolicit;
|
|
|
using Dhcpv6Srv::processRequest;
|
|
|
using Dhcpv6Srv::processRenew;
|
|
|
+ using Dhcpv6Srv::processRelease;
|
|
|
using Dhcpv6Srv::createStatusCode;
|
|
|
using Dhcpv6Srv::selectSubnet;
|
|
|
using Dhcpv6Srv::sanityCheck;
|
|
@@ -143,11 +144,14 @@ public:
|
|
|
}
|
|
|
|
|
|
// Checks that server rejected IA_NA, i.e. that it has no addresses and
|
|
|
- // that expected status code really appears there.
|
|
|
+ // that expected status code really appears there. In some limited cases
|
|
|
+ // (reply to RELEASE) it may be used to verify positive case, where
|
|
|
+ // IA_NA response is expected to not include address.
|
|
|
+ //
|
|
|
// Status code indicates type of error encountered (in theory it can also
|
|
|
// indicate success, but servers typically don't send success status
|
|
|
// as this is the default result and it saves bandwidth)
|
|
|
- void checkRejectedIA_NA(const boost::shared_ptr<Option6IA>& ia,
|
|
|
+ void checkIA_NAStatusCode(const boost::shared_ptr<Option6IA>& ia,
|
|
|
uint16_t expected_status_code) {
|
|
|
// Make sure there is no address assigned.
|
|
|
EXPECT_FALSE(ia->getOption(D6O_IAADDR));
|
|
@@ -158,6 +162,12 @@ public:
|
|
|
|
|
|
boost::shared_ptr<OptionCustom> status =
|
|
|
boost::dynamic_pointer_cast<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) {
|
|
@@ -169,6 +179,26 @@ public:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ void checkMsgStatusCode(const Pkt6Ptr& msg, uint16_t expected_status) {
|
|
|
+ boost::shared_ptr<OptionCustom> status =
|
|
|
+ boost::dynamic_pointer_cast<OptionCustom>(msg->getOption(D6O_STATUS_CODE));
|
|
|
+
|
|
|
+ // It is ok to not include status success as this is the default behavior
|
|
|
+ if (expected_status == 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),
|
|
|
+ status->readInteger<uint16_t>(0));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Check that generated IAADDR option contains expected address.
|
|
|
void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
|
|
|
const IOAddress& expected_addr,
|
|
@@ -989,6 +1019,197 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
|
|
|
EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
|
|
|
}
|
|
|
|
|
|
+// This test verifies that incoming (positive) RELEASE can be handled properly,
|
|
|
+// that a REPLY is generated, that the response has status code and that the
|
|
|
+// lease is indeed removed from the database.
|
|
|
+//
|
|
|
+// expected:
|
|
|
+// - returned REPLY message has copy of client-id
|
|
|
+// - returned REPLY message has server-id
|
|
|
+// - returned REPLY message has IA that does not include an IAADDR
|
|
|
+// - lease is actually removed from LeaseMgr
|
|
|
+TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
|
|
|
+ boost::scoped_ptr<NakedDhcpv6Srv> srv;
|
|
|
+ ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
|
|
|
+
|
|
|
+ const IOAddress addr("2001:db8:1:1::cafe:babe");
|
|
|
+ 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(addr));
|
|
|
+
|
|
|
+ // 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(Lease6::LEASE_IA_NA, addr, duid_, iaid,
|
|
|
+ 501, 502, 503, 504, subnet_->getID(), 0));
|
|
|
+ lease->cltt_ = 1234;
|
|
|
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
|
|
|
+
|
|
|
+ // Check that the lease is really in the database
|
|
|
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
|
|
|
+ ASSERT_TRUE(l);
|
|
|
+
|
|
|
+ // Let's create a RELEASE
|
|
|
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
|
|
|
+ req->setRemoteAddr(IOAddress("fe80::abcd"));
|
|
|
+ boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
|
|
|
+
|
|
|
+ OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
|
|
|
+ ia->addOption(released_addr_opt);
|
|
|
+ req->addOption(ia);
|
|
|
+ req->addOption(clientid);
|
|
|
+
|
|
|
+ // Server-id is mandatory in RELEASE
|
|
|
+ req->addOption(srv->getServerID());
|
|
|
+
|
|
|
+ // Pass it to the server and hope for a REPLY
|
|
|
+ Pkt6Ptr reply = srv->processRelease(req);
|
|
|
+
|
|
|
+ // Check if we get response at all
|
|
|
+ checkResponse(reply, DHCPV6_REPLY, 1234);
|
|
|
+
|
|
|
+ OptionPtr tmp = reply->getOption(D6O_IA_NA);
|
|
|
+ ASSERT_TRUE(tmp);
|
|
|
+
|
|
|
+ // Check that IA_NA was returned and that there's an address included
|
|
|
+ ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
|
|
|
+ checkIA_NAStatusCode(ia, STATUS_Success);
|
|
|
+ checkMsgStatusCode(reply, STATUS_Success);
|
|
|
+
|
|
|
+ // There should be no address returned in RELEASE (see RFC3315, 18.2.6)
|
|
|
+ EXPECT_FALSE(tmp->getOption(D6O_IAADDR));
|
|
|
+
|
|
|
+ // 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(addr);
|
|
|
+ ASSERT_FALSE(l);
|
|
|
+
|
|
|
+ // get lease by subnetid/duid/iaid combination
|
|
|
+ l = LeaseMgrFactory::instance().getLease6(*duid_, iaid, subnet_->getID());
|
|
|
+ ASSERT_FALSE(l);
|
|
|
+}
|
|
|
+
|
|
|
+// This test verifies that incoming (invalid) RELEASE can be handled properly.
|
|
|
+//
|
|
|
+// This test checks 3 scenarios:
|
|
|
+// 1. there is no such lease at all
|
|
|
+// 2. there is such a lease, but it is assigned to a different IAID
|
|
|
+// 3. there is such a lease, but it belongs to a different client
|
|
|
+//
|
|
|
+// expected:
|
|
|
+// - returned REPLY message has copy of client-id
|
|
|
+// - returned REPLY message has server-id
|
|
|
+// - returned REPLY message has IA that includes STATUS-CODE
|
|
|
+// - No lease in LeaseMgr
|
|
|
+TEST_F(Dhcpv6SrvTest, ReleaseReject) {
|
|
|
+
|
|
|
+ boost::scoped_ptr<NakedDhcpv6Srv> srv;
|
|
|
+ ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
|
|
|
+
|
|
|
+ const IOAddress addr("2001:db8:1:1::dead");
|
|
|
+ const uint32_t transid = 1234;
|
|
|
+ const uint32_t valid_iaid = 234;
|
|
|
+ const uint32_t bogus_iaid = 456;
|
|
|
+
|
|
|
+ // Quick sanity check that the address we're about to use is ok
|
|
|
+ ASSERT_TRUE(subnet_->inPool(addr));
|
|
|
+
|
|
|
+ // GenerateClientId() also sets duid_
|
|
|
+ OptionPtr clientid = generateClientId();
|
|
|
+
|
|
|
+ // Check that the lease is NOT in the database
|
|
|
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
|
|
|
+ ASSERT_FALSE(l);
|
|
|
+
|
|
|
+ // Let's create a RENEW
|
|
|
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, transid));
|
|
|
+ req->setRemoteAddr(IOAddress("fe80::abcd"));
|
|
|
+ boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
|
|
|
+
|
|
|
+ OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
|
|
|
+ ia->addOption(renewed_addr_opt);
|
|
|
+ req->addOption(ia);
|
|
|
+ req->addOption(clientid);
|
|
|
+
|
|
|
+ // Server-id is mandatory in RENEW
|
|
|
+ req->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(req);
|
|
|
+
|
|
|
+ // Check if we get response at all
|
|
|
+ checkResponse(reply, DHCPV6_REPLY, transid);
|
|
|
+ OptionPtr tmp = reply->getOption(D6O_IA_NA);
|
|
|
+ ASSERT_TRUE(tmp);
|
|
|
+ // Check that IA_NA was returned and that there's an address included
|
|
|
+ ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
|
|
|
+ ASSERT_TRUE(ia);
|
|
|
+ checkIA_NAStatusCode(ia, STATUS_NoBinding);
|
|
|
+ checkMsgStatusCode(reply, STATUS_NoBinding);
|
|
|
+
|
|
|
+ // Check that the lease is not there
|
|
|
+ l = LeaseMgrFactory::instance().getLease6(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(Lease6::LEASE_IA_NA, addr, duid_, valid_iaid,
|
|
|
+ 501, 502, 503, 504, subnet_->getID(), 0));
|
|
|
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
|
|
|
+
|
|
|
+ // Pass it to the server and hope for a REPLY
|
|
|
+ reply = srv->processRelease(req);
|
|
|
+ checkResponse(reply, DHCPV6_REPLY, transid);
|
|
|
+ tmp = reply->getOption(D6O_IA_NA);
|
|
|
+ ASSERT_TRUE(tmp);
|
|
|
+ // Check that IA_NA was returned and that there's an address included
|
|
|
+ ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
|
|
|
+ ASSERT_TRUE(ia);
|
|
|
+ checkIA_NAStatusCode(ia, STATUS_NoBinding);
|
|
|
+ checkMsgStatusCode(reply, STATUS_NoBinding);
|
|
|
+
|
|
|
+ // Check that the lease is still there
|
|
|
+ l = LeaseMgrFactory::instance().getLease6(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");
|
|
|
+
|
|
|
+ req->delOption(D6O_CLIENTID);
|
|
|
+ ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(D6O_IA_NA));
|
|
|
+ ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
|
|
|
+ req->addOption(generateClientId(13)); // generate different DUID
|
|
|
+ // (with length 13)
|
|
|
+
|
|
|
+ reply = srv->processRelease(req);
|
|
|
+ checkResponse(reply, DHCPV6_REPLY, transid);
|
|
|
+ tmp = reply->getOption(D6O_IA_NA);
|
|
|
+ ASSERT_TRUE(tmp);
|
|
|
+ // Check that IA_NA was returned and that there's an address included
|
|
|
+ ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
|
|
|
+ ASSERT_TRUE(ia);
|
|
|
+ checkIA_NAStatusCode(ia, STATUS_NoBinding);
|
|
|
+ checkMsgStatusCode(reply, STATUS_NoBinding);
|
|
|
+
|
|
|
+ // Check that the lease is still there
|
|
|
+ l = LeaseMgrFactory::instance().getLease6(addr);
|
|
|
+ ASSERT_TRUE(l);
|
|
|
+
|
|
|
+ // Finally, let's cleanup the database
|
|
|
+ EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
|
|
|
+}
|
|
|
+
|
|
|
// This test verifies if the status code option is generated properly.
|
|
|
TEST_F(Dhcpv6SrvTest, StatusCode) {
|
|
|
boost::scoped_ptr<NakedDhcpv6Srv> srv;
|