From: Francis Dupont Date: Mon, 7 May 2018 13:08:14 +0000 (+0200) Subject: [5609] Added tests X-Git-Tag: trac5536_base~3^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=10393a449c8d65722735200937c742a68ddb3605;p=thirdparty%2Fkea.git [5609] Added tests --- diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc index 3b8d52d0dc..7a48996c59 100644 --- a/src/bin/dhcp6/tests/hooks_unittest.cc +++ b/src/bin/dhcp6/tests/hooks_unittest.cc @@ -1764,10 +1764,6 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedSolicit) { EXPECT_TRUE(callback_name_.empty()); } -//// Add a positive (vs this negative one) test with Solicit and rapid commit - -//// Looks for the same with park - // This test verifies that the leases6_committed hook point is not triggered // for the CONFIRM. TEST_F(HooksDhcpv6SrvTest, leases6CommittedConfirm) { @@ -1843,6 +1839,174 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedInfRequest) { EXPECT_TRUE(callback_name_.empty()); } +// This test verifies that the callout installed on the leases6_committed hook +// point is executed as a result of SOLICIT message sent to allocate new +// lease or renew an existing lease using Rapid Commit i.e. one exchange only. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRapidCommit) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"rapid-commit\": true, " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + Dhcp6Client client; + client.setInterface("eth1"); + client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + client.useRapidCommit(true); + + ASSERT_NO_THROW(configure(config, *client.getServer())); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); + + ASSERT_NO_THROW(client.doSolicit()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // Newly allocated lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); +} + +// This test verifies that it is possible to park a packet as a result of +// the leases6_committed callouts using Rapid Commit SOLICITs and prefixes. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRapidCommitPrefixes) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:1::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " + " \"subnet\": \"2001:db8:1::/48\", " + " \"rapid-commit\": true, " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + // Create first client and perform SARR. + Dhcp6Client client1; + client1.setInterface("eth1"); + client1.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); + client1.useRapidCommit(true); + + ASSERT_NO_THROW(configure(config, *client1.getServer())); + + // This callout uses provided IO service object to post a function + // that unparks the packet. The packet is parked and can be unparked + // by simply calling IOService::poll. + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_park_callout)); + + ASSERT_NO_THROW(client1.doSolicit()); + + // We should be offered an address but the REPLY should not arrive + // at this point, because the packet is parked. + ASSERT_FALSE(client1.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // Newly allocated lease should be passed to the callout. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + // Reset all indicators because we'll be now creating a second client. + resetCalloutBuffers(); + + // Create the second client to test that it may communicate with the + // server while the previous packet is parked. + Dhcp6Client client2; + client2.setInterface("eth1"); + client2.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:29::")); + client2.useRapidCommit(true); + ASSERT_NO_THROW(client2.doSolicit()); + + // The ADVERTISE should have been returned but not REPLAY, as this + // packet got parked too. + ASSERT_FALSE(client2.getContext().response_); + + // Check that the callback called is indeed the one we installed. + EXPECT_EQ("leases6_committed", callback_name_); + + // There should be now two actions scheduled on our IO service + // by the invoked callouts. They unpark both REPLY messages. + ASSERT_NO_THROW(io_service_->poll()); + + // Receive and check the first response. + ASSERT_NO_THROW(client1.receiveResponse()); + ASSERT_TRUE(client1.getContext().response_); + Pkt6Ptr rsp = client1.getContext().response_; + EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); + EXPECT_TRUE(client1.hasLeaseForPrefix(IOAddress("2001:db8:1:28::"), 64)); + + // Receive and check the second response. + ASSERT_NO_THROW(client2.receiveResponse()); + ASSERT_TRUE(client2.getContext().response_); + rsp = client2.getContext().response_; + EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); + EXPECT_TRUE(client2.hasLeaseForPrefix(IOAddress("2001:db8:1:29::"), 64)); +} + // This test verifies that the callout installed on the leases6_committed hook // point is executed as a result of REQUEST message sent to allocate new // lease or renew an existing lease. @@ -2002,11 +2166,10 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRequest) { EXPECT_TRUE(callback_deleted_leases6_->empty()); } -//// same with prefix - -// This test verifies that it is possible to park a packet as a result of -// the leases6_committed callouts. -TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) { +// This test verifies that the callout installed on the leases6_committed hook +// point is executed as a result of REQUEST message sent to allocate new +// lease or renew an existing lease. Prefix variant. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRequestPrefix) { IfaceMgrTestConfig test_config(true); string config = "{ \"interfaces-config\": {" @@ -2016,30 +2179,28 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) { "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " "\"subnet6\": [ { " - " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:1::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " " \"subnet\": \"2001:db8:1::/48\", " " \"interface\": \"eth1\" " " } ]," "\"valid-lifetime\": 4000 }"; - // Create first client and perform SARR. - Dhcp6Client client1; - client1.setInterface("eth1"); - client1.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + Dhcp6Client client; + client.setInterface("eth1"); + client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); - ASSERT_NO_THROW(configure(config, *client1.getServer())); + ASSERT_NO_THROW(configure(config, *client.getServer())); - // This callout uses provided IO service object to post a function - // that unparks the packet. The packet is parked and can be unparked - // by simply calling IOService::poll. ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "leases6_committed", leases6_committed_park_callout)); + "leases6_committed", leases6_committed_callout)); - ASSERT_NO_THROW(client1.doSARR()); + ASSERT_NO_THROW(client.doSARR()); - // We should be offered an address but the REPLY should not arrive - // at this point, because the packet is parked. - ASSERT_FALSE(client1.getContext().response_); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); // Check that the callback called is indeed the one we installed EXPECT_EQ("leases6_committed", callback_name_); @@ -2053,12 +2214,13 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) { sort(expected_argument_names.begin(), expected_argument_names.end()); EXPECT_TRUE(callback_argument_names_ == expected_argument_names); - // Newly allocated lease should be passed to the callout. + // Newly allocated lease should be returned. ASSERT_TRUE(callback_new_leases6_); ASSERT_EQ(1, callback_new_leases6_->size()); Lease6Ptr lease = callback_new_leases6_->at(0); ASSERT_TRUE(lease); - EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); // Deleted lease must not be present, because it is a new allocation. ASSERT_TRUE(callback_deleted_leases6_); @@ -2067,71 +2229,338 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) { // Pkt passed to a callout must be configured to copy retrieved options. EXPECT_TRUE(callback_qry_options_copy_); - // Reset all indicators because we'll be now creating a second client. resetCalloutBuffers(); - // Create the second client to test that it may communicate with the - // server while the previous packet is parked. - Dhcp6Client client2; - client2.setInterface("eth1"); - client2.requestAddress(0xabca, IOAddress("2001:db8:1::29")); - ASSERT_NO_THROW(client2.doSARR()); + // Request the lease and make sure that the callout has been executed. + ASSERT_NO_THROW(client.doRequest()); - // The ADVERTISE should have been returned but not REPLAY, as this - // packet got parked too. - ASSERT_FALSE(client2.getContext().response_); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // Check that the callback called is indeed the one we installed. + // Check that the callback called is indeed the one we installed EXPECT_EQ("leases6_committed", callback_name_); - // There should be now two actions scheduled on our IO service - // by the invoked callouts. They unpark both REPLY messages. - ASSERT_NO_THROW(io_service_->poll()); + // Requested lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); - // Receive and check the first response. - ASSERT_NO_THROW(client1.receiveResponse()); - ASSERT_TRUE(client1.getContext().response_); - Pkt6Ptr rsp = client1.getContext().response_; - EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); - EXPECT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::28"))); + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); - // Receive and check the second response. - ASSERT_NO_THROW(client2.receiveResponse()); - ASSERT_TRUE(client2.getContext().response_); - rsp = client2.getContext().response_; - EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); - EXPECT_TRUE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::29"))); -} + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); -//// same with prefix + resetCalloutBuffers(); -// This test verifies that incoming (positive) RENEW can be handled properly, -// and the lease6_renew callouts are triggered. -TEST_F(HooksDhcpv6SrvTest, basicLease6Renew) { - NakedDhcpv6Srv srv(0); + // Let's try to request again but force the client to request a different + // prefix with a different IAID. + client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::")); - // Install lease6_renew_callout - EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "lease6_renew", lease6_renew_callout)); + ASSERT_NO_THROW(client.doRequest()); - const IOAddress addr("2001:db8:1:1::cafe:babe"); - const uint32_t iaid = 234; + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // Generate client-id also duid_ - OptionPtr clientid = generateClientId(); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // Check that the address we are about to use is indeed in pool - ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr)); + // New lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(2, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(1); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); - // 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(Lease::TYPE_NA, addr, duid_, iaid, - 501, 502, 503, 504, subnet_->getID(), - HWAddrPtr(), 0)); - lease->cltt_ = 1234; - ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + // The old lease is kept. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_TRUE(callback_deleted_leases6_->empty()); - // Check that the lease is really in the database + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // The requested prefix is just a hint. + client.requestPrefix(0x5577, 64, IOAddress("4000::1")); + + ASSERT_NO_THROW(client.doRequest()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + ASSERT_TRUE(callback_new_leases6_); + EXPECT_EQ(3, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(2); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // Request an address: this should lead to an error as no address pool + // is configured. + client.requestAddress(0x1122, IOAddress("2001:db8:1::28")); + + ASSERT_NO_THROW(client.doRequest()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check the error. + EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122)); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + ASSERT_TRUE(callback_new_leases6_); + EXPECT_EQ(3, callback_new_leases6_->size()); + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); +} + +// This test verifies that it is possible to park a packet as a result of +// the leases6_committed callouts. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + // Create first client and perform SARR. + Dhcp6Client client1; + client1.setInterface("eth1"); + client1.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + + ASSERT_NO_THROW(configure(config, *client1.getServer())); + + // This callout uses provided IO service object to post a function + // that unparks the packet. The packet is parked and can be unparked + // by simply calling IOService::poll. + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_park_callout)); + + ASSERT_NO_THROW(client1.doSARR()); + + // We should be offered an address but the REPLY should not arrive + // at this point, because the packet is parked. + ASSERT_FALSE(client1.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // Newly allocated lease should be passed to the callout. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + // Reset all indicators because we'll be now creating a second client. + resetCalloutBuffers(); + + // Create the second client to test that it may communicate with the + // server while the previous packet is parked. + Dhcp6Client client2; + client2.setInterface("eth1"); + client2.requestAddress(0xabca, IOAddress("2001:db8:1::29")); + ASSERT_NO_THROW(client2.doSARR()); + + // The ADVERTISE should have been returned but not REPLAY, as this + // packet got parked too. + ASSERT_FALSE(client2.getContext().response_); + + // Check that the callback called is indeed the one we installed. + EXPECT_EQ("leases6_committed", callback_name_); + + // There should be now two actions scheduled on our IO service + // by the invoked callouts. They unpark both REPLY messages. + ASSERT_NO_THROW(io_service_->poll()); + + // Receive and check the first response. + ASSERT_NO_THROW(client1.receiveResponse()); + ASSERT_TRUE(client1.getContext().response_); + Pkt6Ptr rsp = client1.getContext().response_; + EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); + EXPECT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::28"))); + + // Receive and check the second response. + ASSERT_NO_THROW(client2.receiveResponse()); + ASSERT_TRUE(client2.getContext().response_); + rsp = client2.getContext().response_; + EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); + EXPECT_TRUE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::29"))); +} + +// This test verifies that it is possible to park a packet as a result of +// the leases6_committed callouts. Prefix variant. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:1::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + // Create first client and perform SARR. + Dhcp6Client client1; + client1.setInterface("eth1"); + client1.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); + + ASSERT_NO_THROW(configure(config, *client1.getServer())); + + // This callout uses provided IO service object to post a function + // that unparks the packet. The packet is parked and can be unparked + // by simply calling IOService::poll. + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_park_callout)); + + ASSERT_NO_THROW(client1.doSARR()); + + // We should be offered an address but the REPLY should not arrive + // at this point, because the packet is parked. + ASSERT_FALSE(client1.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // Newly allocated lease should be passed to the callout. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + // Reset all indicators because we'll be now creating a second client. + resetCalloutBuffers(); + + // Create the second client to test that it may communicate with the + // server while the previous packet is parked. + Dhcp6Client client2; + client2.setInterface("eth1"); + client2.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:29::")); + ASSERT_NO_THROW(client2.doSARR()); + + // The ADVERTISE should have been returned but not REPLAY, as this + // packet got parked too. + ASSERT_FALSE(client2.getContext().response_); + + // Check that the callback called is indeed the one we installed. + EXPECT_EQ("leases6_committed", callback_name_); + + // There should be now two actions scheduled on our IO service + // by the invoked callouts. They unpark both REPLY messages. + ASSERT_NO_THROW(io_service_->poll()); + + // Receive and check the first response. + ASSERT_NO_THROW(client1.receiveResponse()); + ASSERT_TRUE(client1.getContext().response_); + Pkt6Ptr rsp = client1.getContext().response_; + EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); + EXPECT_TRUE(client1.hasLeaseForPrefix(IOAddress("2001:db8:1:28::"), 64)); + + // Receive and check the second response. + ASSERT_NO_THROW(client2.receiveResponse()); + ASSERT_TRUE(client2.getContext().response_); + rsp = client2.getContext().response_; + EXPECT_EQ(DHCPV6_REPLY, rsp->getType()); + EXPECT_TRUE(client2.hasLeaseForPrefix(IOAddress("2001:db8:1:29::"), 64)); +} + +// This test verifies that incoming (positive) RENEW can be handled properly, +// and the lease6_renew callouts are triggered. +TEST_F(HooksDhcpv6SrvTest, basicLease6Renew) { + NakedDhcpv6Srv srv(0); + + // Install lease6_renew_callout + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "lease6_renew", lease6_renew_callout)); + + 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(Lease::TYPE_NA, 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(Lease::TYPE_NA, addr, duid_, iaid, + 501, 502, 503, 504, subnet_->getID(), + HWAddrPtr(), 0)); + lease->cltt_ = 1234; + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Check that the lease is really in the database Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr); ASSERT_TRUE(l); @@ -2273,108 +2702,267 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdateLease6Renew) { // Check that IA_NA was returned and that there's an address included boost::shared_ptr addr_opt = checkIA_NA(reply, 1000, 1001, 1002); - ASSERT_TRUE(addr_opt); - // Check that the lease is really in the database - l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt); - ASSERT_TRUE(l); + ASSERT_TRUE(addr_opt); + // Check that the lease is really in the database + l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt); + ASSERT_TRUE(l); + + // Check that we chose the distinct override values + ASSERT_NE(override_t1_, subnet_->getT1()); + ASSERT_NE(override_t2_, subnet_->getT2()); + ASSERT_NE(override_preferred_, subnet_->getPreferred()); + EXPECT_NE(override_valid_, subnet_->getValid()); + + // Check that T1, T2, preferred, valid were overridden the the callout + EXPECT_EQ(override_t1_, l->t1_); + EXPECT_EQ(override_t2_, l->t2_); + EXPECT_EQ(override_preferred_, l->preferred_lft_); + EXPECT_EQ(override_valid_, l->valid_lft_); + + // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors + int32_t cltt = static_cast(l->cltt_); + int32_t expected = static_cast(time(NULL)); + // Equality or difference by 1 between cltt and expected is ok. + EXPECT_GE(1, abs(cltt - expected)); + + EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr_opt->getAddress())); +} + +// This test verifies that incoming (positive) RENEW can be handled properly, +// and the lease6_renew callouts are able to set the skip flag that will +// reject the renewal +TEST_F(HooksDhcpv6SrvTest, skipLease6Renew) { + NakedDhcpv6Srv srv(0); + + // Install lease6_renew_skip_callout + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "lease6_renew", lease6_renew_skip_callout)); + + 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(Lease::TYPE_NA, 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(Lease::TYPE_NA, addr, duid_, iaid, + 501, 502, 503, 504, subnet_->getID(), + HWAddrPtr(), 0)); + lease->cltt_ = 1234; + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Check that the lease is really in the database + Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, + addr); + ASSERT_TRUE(l); + + // 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)); + + // Let's create a RENEW + Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234)); + req->setRemoteAddr(IOAddress("fe80::abcd")); + req->setIface("eth0"); + boost::shared_ptr ia = generateIA(D6O_IA_NA, 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()); + + // Pass it to the server and hope for a REPLY + Pkt6Ptr reply = srv.processRenew(req); + ASSERT_TRUE(reply); + + // Check that our callback was called + EXPECT_EQ("lease6_renew", callback_name_); + + l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr); + + // Check that the old values are still there and they were not + // updated by the renewal + 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)); +} + +// This test verifies that the callout installed on the leases6_committed hook +// point is executed as a result of RENEW message sent to allocate new +// lease or renew an existing lease. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + Dhcp6Client client; + client.setInterface("eth1"); + client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + + ASSERT_NO_THROW(configure(config, *client.getServer())); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); + + ASSERT_NO_THROW(client.doSARR()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // Newly allocated lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // Renew the lease and make sure that the callout has been executed. + ASSERT_NO_THROW(client.doRenew()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Renewed lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // Let's try to renew again but force the client to renew a different + // address with a different IAID. + client.requestAddress(0x2233, IOAddress("2001:db8:1::29")); + + ASSERT_NO_THROW(client.doRenew()); - // Check that we chose the distinct override values - ASSERT_NE(override_t1_, subnet_->getT1()); - ASSERT_NE(override_t2_, subnet_->getT2()); - ASSERT_NE(override_preferred_, subnet_->getPreferred()); - EXPECT_NE(override_valid_, subnet_->getValid()); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // Check that T1, T2, preferred, valid were overridden the the callout - EXPECT_EQ(override_t1_, l->t1_); - EXPECT_EQ(override_t2_, l->t2_); - EXPECT_EQ(override_preferred_, l->preferred_lft_); - EXPECT_EQ(override_valid_, l->valid_lft_); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors - int32_t cltt = static_cast(l->cltt_); - int32_t expected = static_cast(time(NULL)); - // Equality or difference by 1 between cltt and expected is ok. - EXPECT_GE(1, abs(cltt - expected)); + // New lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(2, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(1); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::29", lease->addr_.toText()); - EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr_opt->getAddress())); -} + // The old lease is kept. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_TRUE(callback_deleted_leases6_->empty()); -// This test verifies that incoming (positive) RENEW can be handled properly, -// and the lease6_renew callouts are able to set the skip flag that will -// reject the renewal -TEST_F(HooksDhcpv6SrvTest, skipLease6Renew) { - NakedDhcpv6Srv srv(0); + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); - // Install lease6_renew_skip_callout - EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( - "lease6_renew", lease6_renew_skip_callout)); + resetCalloutBuffers(); - const IOAddress addr("2001:db8:1:1::cafe:babe"); - const uint32_t iaid = 234; + // The renewed address is just a hint. + client.requestAddress(0x5577, IOAddress("4000::2")); - // Generate client-id also duid_ - OptionPtr clientid = generateClientId(); + ASSERT_NO_THROW(client.doRenew()); - // Check that the address we are about to use is indeed in pool - ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr)); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // 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(Lease::TYPE_NA, addr, duid_, iaid, - 501, 502, 503, 504, subnet_->getID(), - HWAddrPtr(), 0)); - lease->cltt_ = 1234; - ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // Check that the lease is really in the database - Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, - addr); - ASSERT_TRUE(l); + ASSERT_TRUE(callback_new_leases6_); + EXPECT_EQ(3, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(2); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::", lease->addr_.toText()); + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); - // 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)); + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); - // Let's create a RENEW - Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234)); - req->setRemoteAddr(IOAddress("fe80::abcd")); - req->setIface("eth0"); - boost::shared_ptr ia = generateIA(D6O_IA_NA, iaid, 1500, 3000); + resetCalloutBuffers(); - OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500)); - ia->addOption(renewed_addr_opt); - req->addOption(ia); - req->addOption(clientid); + // Renew a prefix: this should lead to an error as no prefix pool + // is configured. + client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::")); - // Server-id is mandatory in RENEW - req->addOption(srv.getServerID()); + ASSERT_NO_THROW(client.doRenew()); - // Pass it to the server and hope for a REPLY - Pkt6Ptr reply = srv.processRenew(req); - ASSERT_TRUE(reply); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); - // Check that our callback was called - EXPECT_EQ("lease6_renew", callback_name_); + // Check the error. + EXPECT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(0x1122)); - l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // Check that the old values are still there and they were not - // updated by the renewal - 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)); + ASSERT_TRUE(callback_new_leases6_); + EXPECT_EQ(3, callback_new_leases6_->size()); + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); } // This test verifies that the callout installed on the leases6_committed hook // point is executed as a result of RENEW message sent to allocate new -// lease or renew an existing lease. -TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { +// lease or renew an existing lease. Prefix variant. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) { IfaceMgrTestConfig test_config(true); string config = "{ \"interfaces-config\": {" @@ -2384,7 +2972,10 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " "\"subnet6\": [ { " - " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:1::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " " \"subnet\": \"2001:db8:1::/48\", " " \"interface\": \"eth1\" " " } ]," @@ -2392,7 +2983,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { Dhcp6Client client; client.setInterface("eth1"); - client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); ASSERT_NO_THROW(configure(config, *client.getServer())); @@ -2421,7 +3012,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { ASSERT_EQ(1, callback_new_leases6_->size()); Lease6Ptr lease = callback_new_leases6_->at(0); ASSERT_TRUE(lease); - EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); // Deleted lease must not be present, because it is a new allocation. ASSERT_TRUE(callback_deleted_leases6_); @@ -2446,7 +3038,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { ASSERT_EQ(1, callback_new_leases6_->size()); lease = callback_new_leases6_->at(0); ASSERT_TRUE(lease); - EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); // Deleted lease must not be present, because it is a new allocation. ASSERT_TRUE(callback_deleted_leases6_); @@ -2458,8 +3051,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { resetCalloutBuffers(); // Let's try to renew again but force the client to renew a different - // address with a different IAID. - client.requestAddress(0x2233, IOAddress("2001:db8:1::29")); + // prefix with a different IAID. + client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::")); ASSERT_NO_THROW(client.doRenew()); @@ -2474,7 +3067,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { ASSERT_EQ(2, callback_new_leases6_->size()); lease = callback_new_leases6_->at(1); ASSERT_TRUE(lease); - EXPECT_EQ("2001:db8:1::29", lease->addr_.toText()); + EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); // The old lease is kept. ASSERT_TRUE(callback_deleted_leases6_); @@ -2485,8 +3079,8 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { resetCalloutBuffers(); - // The renewed address is just a hint. - client.requestAddress(0x5577, IOAddress("4000::2")); + // The renewed prefix is just a hint. + client.requestPrefix(0x5577, 64, IOAddress("4000::1")); ASSERT_NO_THROW(client.doRenew()); @@ -2501,6 +3095,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { lease = callback_new_leases6_->at(2); ASSERT_TRUE(lease); EXPECT_EQ("2001:db8:1::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); ASSERT_TRUE(callback_deleted_leases6_); EXPECT_TRUE(callback_deleted_leases6_->empty()); @@ -2509,9 +3104,9 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { resetCalloutBuffers(); - // Renew a prefix: this should lead to an error as no prefix pool + // Renew an address: this should lead to an error as no address pool // is configured. - client.requestPrefix(0x1122, 64, IOAddress("2001:db8:1000::")); + client.requestAddress(0x1122, IOAddress("2001:db8:1::28")); ASSERT_NO_THROW(client.doRenew()); @@ -2519,7 +3114,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { ASSERT_TRUE(client.getContext().response_); // Check the error. - EXPECT_EQ(STATUS_NoPrefixAvail, client.getStatusCode(0x1122)); + EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122)); // Check that the callback called is indeed the one we installed EXPECT_EQ("leases6_committed", callback_name_); @@ -2530,8 +3125,6 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) { EXPECT_TRUE(callback_deleted_leases6_->empty()); } -//// same with prefix - // 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. @@ -2796,37 +3389,162 @@ TEST_F(HooksDhcpv6SrvTest, dropLease6Release) { req->setRemoteAddr(IOAddress("fe80::abcd")); boost::shared_ptr ia = generateIA(D6O_IA_NA, 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); + 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); + + ASSERT_TRUE(reply); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("lease6_release", callback_name_); + + // Check that the lease is still there + // get lease by address + l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, + addr); + ASSERT_TRUE(l); + + // Get lease by subnetid/duid/iaid combination + l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid, + subnet_->getID()); + ASSERT_TRUE(l); +} + +// This test verifies that the leases6_committed callout is executed +// with deleted leases as argument when RELEASE is processed. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + Dhcp6Client client; + client.setInterface("eth1"); + client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + + ASSERT_NO_THROW(configure(config, *client.getServer())); + + ASSERT_NO_THROW(client.doSARR()); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); + + ASSERT_NO_THROW(client.doRelease()); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // No new allocations. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_TRUE(callback_new_leases6_->empty()); + + // Released lease should be returned. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_EQ(1, callback_deleted_leases6_->size()); + Lease6Ptr lease = callback_deleted_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); +} + +// This test verifies that the leases6_committed callout is executed +// with deleted leases as argument when RELEASE is processed. Prefix variant. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleasePrefix) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:1::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + Dhcp6Client client; + client.setInterface("eth1"); + client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); + + ASSERT_NO_THROW(configure(config, *client.getServer())); + + ASSERT_NO_THROW(client.doSARR()); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); + + ASSERT_NO_THROW(client.doRelease()); - // Server-id is mandatory in RELEASE - req->addOption(srv.getServerID()); + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); - // Pass it to the server and hope for a REPLY - Pkt6Ptr reply = srv.processRelease(req); + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); - ASSERT_TRUE(reply); + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); - // Check that the callback called is indeed the one we installed - EXPECT_EQ("lease6_release", callback_name_); + // No new allocations. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_TRUE(callback_new_leases6_->empty()); - // Check that the lease is still there - // get lease by address - l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, - addr); - ASSERT_TRUE(l); + // Released lease should be returned. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_EQ(1, callback_deleted_leases6_->size()); + Lease6Ptr lease = callback_deleted_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); - // Get lease by subnetid/duid/iaid combination - l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, *duid_, iaid, - subnet_->getID()); - ASSERT_TRUE(l); + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); } // This test verifies that the leases6_committed callout is executed // with deleted leases as argument when RELEASE is processed. -TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) { +// Variant with two addresses and two prefixes. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleaseMultiple) { IfaceMgrTestConfig test_config(true); string config = "{ \"interfaces-config\": {" @@ -2837,14 +3555,23 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) { "\"renew-timer\": 1000, " "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," - " \"subnet\": \"2001:db8:1::/48\", " + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:2::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " + " \"subnet\": \"2001:db8::/32\", " " \"interface\": \"eth1\" " " } ]," "\"valid-lifetime\": 4000 }"; Dhcp6Client client; client.setInterface("eth1"); + // In theory we can reuse the IAID but copyIAsFromLeases() copies + // only one lease... client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + client.requestPrefix(0xabcb, 64, IOAddress("2001:db8:2:28::")); + client.requestAddress(0x2233, IOAddress("2001:db8:1::29")); + client.requestPrefix(0x2234, 64, IOAddress("2001:db8:2:29::")); ASSERT_NO_THROW(configure(config, *client.getServer())); @@ -2875,18 +3602,12 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) { // Released lease should be returned. ASSERT_TRUE(callback_deleted_leases6_); - EXPECT_EQ(1, callback_deleted_leases6_->size()); - Lease6Ptr lease = callback_deleted_leases6_->at(0); - ASSERT_TRUE(lease); - EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + EXPECT_EQ(4, callback_deleted_leases6_->size()); // Pkt passed to a callout must be configured to copy retrieved options. EXPECT_TRUE(callback_qry_options_copy_); } -//// same with prefix -//// same with addresses and prefixes (2+2) - // This test verifies that incoming (positive) REBIND can be handled properly, // and the lease6_rebind callouts are triggered. TEST_F(HooksDhcpv6SrvTest, basicLease6Rebind) { @@ -3300,7 +4021,171 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebind) { EXPECT_TRUE(callback_deleted_leases6_->empty()); } -//// same with prefix +// This test verifies that the callout installed on the leases6_committed hook +// point is executed as a result of REBIND message sent to allocate new +// lease or renew an existing lease. Prefix variant. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebindPrefix) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pd-pools\": [ {" + " \"prefix\": \"2001:db8:1::\", " + " \"prefix-len\": 56, " + " \"delegated-len\": 64 } ], " + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + Dhcp6Client client; + client.setInterface("eth1"); + client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::")); + + ASSERT_NO_THROW(configure(config, *client.getServer())); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); + + ASSERT_NO_THROW(client.doSARR()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // Newly allocated lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // Rebind the lease and make sure that the callout has been executed. + ASSERT_NO_THROW(client.doRebind()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Rebound lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(1, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + + // Deleted lease must not be present, because it is a new allocation. + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // Let's try to rebind again but force the client to rebind a different + // prefix with a different IAID. + client.requestPrefix(0x2233, 64, IOAddress("2001:db8:1:29::")); + + ASSERT_NO_THROW(client.doRebind()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // New lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(2, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(1); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1:29::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + + // The old lease is kept. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // The rebound prefix is just a hint. + client.requestPrefix(0x5577, 64, IOAddress("4000::1")); + + ASSERT_NO_THROW(client.doRebind()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + ASSERT_TRUE(callback_new_leases6_); + EXPECT_EQ(3, callback_new_leases6_->size()); + lease = callback_new_leases6_->at(2); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::", lease->addr_.toText()); + EXPECT_EQ(64, lease->prefixlen_); + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + resetCalloutBuffers(); + + // Rebind an address: this should lead to an error as no address pool + // is configured. + client.requestAddress(0x1122, IOAddress("2001:db8:1::28")); + + ASSERT_NO_THROW(client.doRebind()); + + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + // Check the error. + EXPECT_EQ(STATUS_NoAddrsAvail, client.getStatusCode(0x1122)); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + ASSERT_TRUE(callback_new_leases6_); + EXPECT_EQ(3, callback_new_leases6_->size()); + ASSERT_TRUE(callback_deleted_leases6_); + EXPECT_TRUE(callback_deleted_leases6_->empty()); +} // This test checks that the basic decline hook (lease6_decline) is // triggered properly. @@ -3496,17 +4381,81 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedDecline) { // Declined lease should be returned. ASSERT_TRUE(callback_new_leases6_); - EXPECT_EQ(1, callback_new_leases6_->size()); + ASSERT_EQ(1, callback_new_leases6_->size()); + Lease6Ptr lease = callback_new_leases6_->at(0); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); +} + +// This test verifies that the leases6_committed callout is executed +// with deleted leases as argument when DECLINE is processed. +// Variant with 2 IA_NAs. +TEST_F(HooksDhcpv6SrvTest, leases6CommittedDeclineTwoNAs) { + IfaceMgrTestConfig test_config(true); + + string config = "{ \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + "}," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"interface\": \"eth1\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + Dhcp6Client client; + client.setInterface("eth1"); + client.requestAddress(0xabca, IOAddress("2001:db8:1::28")); + client.requestAddress(0x2233, IOAddress("2001:db8:1::29")); + + ASSERT_NO_THROW(configure(config, *client.getServer())); + + ASSERT_NO_THROW(client.doSARR()); + // Make sure that we received a response + ASSERT_TRUE(client.getContext().response_); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases6_committed", leases6_committed_callout)); + + ASSERT_NO_THROW(client.doDecline()); + + // Check that the callback called is indeed the one we installed + EXPECT_EQ("leases6_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query6"); + expected_argument_names.push_back("deleted_leases6"); + expected_argument_names.push_back("leases6"); + + sort(expected_argument_names.begin(), expected_argument_names.end()); + EXPECT_TRUE(callback_argument_names_ == expected_argument_names); + + // No deleted leases. + ASSERT_TRUE(callback_deleted_leases6_); + ASSERT_TRUE(callback_deleted_leases6_->empty()); + + // Declined lease should be returned. + ASSERT_TRUE(callback_new_leases6_); + ASSERT_EQ(2, callback_new_leases6_->size()); Lease6Ptr lease = callback_new_leases6_->at(0); ASSERT_TRUE(lease); EXPECT_EQ("2001:db8:1::28", lease->addr_.toText()); + lease = callback_new_leases6_->at(1); + ASSERT_TRUE(lease); + EXPECT_EQ("2001:db8:1::29", lease->addr_.toText()); // Pkt passed to a callout must be configured to copy retrieved options. EXPECT_TRUE(callback_qry_options_copy_); } -//// same with 2 IA_NA -//// same with an IA_NA with 2 addresses (if I can get an example) +// Should test with one NA and two addresses but need an example first... // Checks if callout installed on host6_identifier can generate an // identifier and whether that identifier is actually used.