From: Francis Dupont Date: Thu, 12 Nov 2020 18:26:10 +0000 (+0100) Subject: [#1418] Checkpoint: finished v4 part, v6 and doc to do X-Git-Tag: Kea-1.9.4~98 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1644513e8fe7fcf07a8497ce429e80df3efb118d;p=thirdparty%2Fkea.git [#1418] Checkpoint: finished v4 part, v6 and doc to do --- diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 07b55de6bf..46d55bf9b4 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -1731,6 +1731,86 @@ TEST_F(Dhcpv4SrvTest, discoverEchoClientId) { checkClientId(offer, clientid); } +// This test verifies that incoming DISCOVER can reuse an existing lease. +TEST_F(Dhcpv4SrvTest, DiscoverReuse) { + IfaceMgrTestConfig test_config(true); + IfaceMgr::instance().openSockets4(); + + boost::scoped_ptr srv; + ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0))); + + // Enable lease reuse. + subnet_->setCacheThreshold(.1); + + const IOAddress addr("192.0.2.106"); + const uint32_t temp_valid = subnet_->getValid(); + const int delta = 100; + const time_t temp_timestamp = time(NULL) - delta; + + // Generate client-id also sets client_id_ member + OptionPtr clientid = generateClientId(); + + // Check that the address we are about to use is indeed in pool + ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr)); + + // let's create a lease and put it in the LeaseMgr + uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe}; + HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER)); + Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, + &client_id_->getDuid()[0], client_id_->getDuid().size(), + temp_valid, temp_timestamp, subnet_->getID())); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used)); + + // Check that the lease is really in the database + Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(l); + + // Check that preferred, valid and cltt really set. + // Constructed lease looks as if it was assigned 10 seconds ago + EXPECT_EQ(l->valid_lft_, temp_valid); + EXPECT_EQ(l->cltt_, temp_timestamp); + + // Let's create a DISCOVER + Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234)); + dis->setRemoteAddr(IOAddress(addr)); + dis->addOption(clientid); + dis->setIface("eth0"); + dis->setIndex(ETH0_INDEX); + dis->setHWAddr(hwaddr2); + + // Pass it to the server and get an offer + Pkt4Ptr offer = srv->processDiscover(dis); + + // Check if we get response at all + checkResponse(offer, DHCPOFFER, 1234); + + // Check valid lifetime (temp_valid - age) + OptionUint32Ptr opt = boost::dynamic_pointer_cast< + OptionUint32>(offer->getOption(DHO_DHCP_LEASE_TIME)); + ASSERT_TRUE(opt); + EXPECT_GE(subnet_->getValid() - delta, opt->getValue()); + EXPECT_LE(subnet_->getValid() - delta - 10, opt->getValue()); + + // Check address + EXPECT_EQ(addr, offer->getYiaddr()); + + // Check T1 + opt = boost::dynamic_pointer_cast< + OptionUint32>(offer->getOption(DHO_DHCP_RENEWAL_TIME)); + ASSERT_TRUE(opt); + EXPECT_EQ(opt->getValue(), subnet_->getT1()); + + // Check T2 + opt = boost::dynamic_pointer_cast< + OptionUint32>(offer->getOption(DHO_DHCP_REBINDING_TIME)); + ASSERT_TRUE(opt); + EXPECT_EQ(opt->getValue(), subnet_->getT2()); + + // Check identifiers + checkServerId(offer, srv->getServerID()); + checkClientId(offer, clientid); +} + // Check that option 58 and 59 are not included if they are not specified. TEST_F(Dhcpv4SrvTest, RequestNoTimers) { IfaceMgrTestConfig test_config(true); @@ -2158,6 +2238,96 @@ TEST_F(Dhcpv4SrvTest, RenewMaxLifetime) { } // end of Renew*Lifetime +// This test verifies that incoming RENEW can reuse an existing lease. +TEST_F(Dhcpv4SrvTest, RenewReuse) { + IfaceMgrTestConfig test_config(true); + IfaceMgr::instance().openSockets4(); + + boost::scoped_ptr srv; + ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0))); + + // Enable lease reuse. + subnet_->setCacheThreshold(.1); + + const IOAddress addr("192.0.2.106"); + const uint32_t temp_valid = subnet_->getValid(); + const int delta = 100; + const time_t temp_timestamp = time(NULL) - delta; + + // Generate client-id also sets client_id_ member + OptionPtr clientid = generateClientId(); + + // Check that the address we are about to use is indeed in pool + ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr)); + + // let's create a lease and put it in the LeaseMgr + uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe}; + HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER)); + Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, + &client_id_->getDuid()[0], client_id_->getDuid().size(), + temp_valid, temp_timestamp, subnet_->getID())); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used)); + + // Check that the lease is really in the database + Lease4Ptr l = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(l); + + // Check that preferred, valid and cltt really set. + // Constructed lease looks as if it was assigned 10 seconds ago + EXPECT_EQ(l->valid_lft_, temp_valid); + EXPECT_EQ(l->cltt_, temp_timestamp); + + // Let's create a RENEW + Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234)); + req->setRemoteAddr(IOAddress(addr)); + req->setYiaddr(addr); + req->setCiaddr(addr); // client's address + req->setIface("eth0"); + req->setIndex(ETH0_INDEX); + + req->addOption(clientid); + req->setHWAddr(hwaddr2); + + // Pass it to the server and hope for a REPLY + Pkt4Ptr ack = srv->processRequest(req); + + // Check if we get response at all + checkResponse(ack, DHCPACK, 1234); + + // Check valid lifetime (temp_valid - age) + OptionUint32Ptr opt = boost::dynamic_pointer_cast< + OptionUint32>(ack->getOption(DHO_DHCP_LEASE_TIME)); + ASSERT_TRUE(opt); + EXPECT_GE(subnet_->getValid() - delta, opt->getValue()); + EXPECT_LE(subnet_->getValid() - delta - 10, opt->getValue()); + + // Check address + EXPECT_EQ(addr, ack->getYiaddr()); + + // Check T1 + opt = boost::dynamic_pointer_cast< + OptionUint32>(ack->getOption(DHO_DHCP_RENEWAL_TIME)); + ASSERT_TRUE(opt); + EXPECT_EQ(opt->getValue(), subnet_->getT1()); + + // Check T2 + opt = boost::dynamic_pointer_cast< + OptionUint32>(ack->getOption(DHO_DHCP_REBINDING_TIME)); + ASSERT_TRUE(opt); + EXPECT_EQ(opt->getValue(), subnet_->getT2()); + + // Check identifiers + checkServerId(ack, srv->getServerID()); + checkClientId(ack, clientid); + + // Check that the lease is really in the database + Lease4Ptr lease = checkLease(ack, clientid, req->getHWAddr(), addr); + ASSERT_TRUE(lease); + + // Check that the lease was not updated + EXPECT_EQ(temp_timestamp, lease->cltt_); +} + // This test verifies that the logic which matches server identifier in the // received message with server identifiers used by a server works correctly: // - a message with no server identifier is accepted, diff --git a/src/bin/dhcp4/tests/hooks_unittest.cc b/src/bin/dhcp4/tests/hooks_unittest.cc index ed51ae9784..991cf301ac 100644 --- a/src/bin/dhcp4/tests/hooks_unittest.cc +++ b/src/bin/dhcp4/tests/hooks_unittest.cc @@ -2039,6 +2039,78 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedRequest) { checkCalloutHandleReset(client.getContext().query_); } +// This test verifies that the callout installed on the leases4_committed hook +// point is executed as a result of DHCPREQUEST message sent to reuse an +// an existing lease. +TEST_F(HooksDhcpv4SrvTest, leases4CommittedReuse) { + IfaceMgrTestConfig test_config(true); + IfaceMgr::instance().openSockets4(); + + ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "leases4_committed", leases4_committed_callout)); + + + // Modify the subnet to reuse leases. + subnet_->setCacheThreshold(.25); + + Dhcp4Client client(Dhcp4Client::SELECTING); + client.setIfaceName("eth1"); + client.setIfaceIndex(ETH1_INDEX); + ASSERT_NO_THROW(client.doDORA(boost::shared_ptr(new IOAddress("192.0.2.100")))); + + // 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("leases4_committed", callback_name_); + + // Check if all expected parameters were really received + vector expected_argument_names; + expected_argument_names.push_back("query4"); + expected_argument_names.push_back("deleted_leases4"); + expected_argument_names.push_back("leases4"); + + 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_lease4_); + EXPECT_EQ("192.0.2.100", callback_lease4_->addr_.toText()); + + // Deleted lease must not be present, because it is a new allocation. + EXPECT_FALSE(callback_deleted_lease4_); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + // Check if the callout handle state was reset after the callout. + checkCalloutHandleReset(client.getContext().query_); + + resetCalloutBuffers(); + + // Renew the lease and make sure that the callout has been executed. + client.setState(Dhcp4Client::RENEWING); + 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("leases4_committed", callback_name_); + + // Renewed lease should not be present because it was renewed. + EXPECT_FALSE(callback_lease4_); + + // Deleted lease must not be present, because it renews the same address. + EXPECT_FALSE(callback_deleted_lease4_); + + // Pkt passed to a callout must be configured to copy retrieved options. + EXPECT_TRUE(callback_qry_options_copy_); + + // Check if the callout handle state was reset after the callout. + checkCalloutHandleReset(client.getContext().query_); +} + // This test verifies that it is possible to park a packet as a result of // the leases4_committed callouts. TEST_F(HooksDhcpv4SrvTest, leases4CommittedParkRequests) { diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 53b5456047..a268e19d7c 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -3770,7 +3770,7 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr, lease->hostname_ = ctx.hostname_; // Add(update) the extended information on the lease. - updateLease4ExtendedInfo(lease, ctx); + static_cast(updateLease4ExtendedInfo(lease, ctx)); // Let's execute all callouts registered for lease4_select if (ctx.callout_handle_ && @@ -3869,7 +3869,10 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, ctx.old_lease_.reset(new Lease4(*old_values)); // Update the lease with the information from the context. - updateLease4Information(lease, ctx); + // If there was no significant changes, try reuse. + if (!updateLease4Information(lease, ctx)) { + setLeaseRemainingLife(lease, ctx); + } if (!ctx.fake_allocation_) { // If the lease is expired we have to reclaim it before @@ -3931,7 +3934,7 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, /// DROP status does not make sense here. } - if (!ctx.fake_allocation_ && !skip) { + if (!ctx.fake_allocation_ && !skip && (lease->remaining_valid_lft_ == 0)) { // for REQUEST we do update the lease LeaseMgrFactory::instance().updateLease4(lease); @@ -3978,7 +3981,7 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired, expired->state_ = Lease::STATE_DEFAULT; } - updateLease4Information(expired, ctx); + static_cast(updateLease4Information(expired, ctx)); LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA, ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA) @@ -4221,12 +4224,29 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) { return (new_lease); } -void +bool AllocEngine::updateLease4Information(const Lease4Ptr& lease, AllocEngine::ClientContext4& ctx) const { - lease->subnet_id_ = ctx.subnet_->getID(); - lease->hwaddr_ = ctx.hwaddr_; - lease->client_id_ = ctx.subnet_->getMatchClientId() ? ctx.clientid_ : ClientIdPtr(); + bool changed = false; + if (lease->subnet_id_ != ctx.subnet_->getID()) { + changed = true; + lease->subnet_id_ = ctx.subnet_->getID(); + } + if ((!ctx.hwaddr_ && lease->hwaddr_) || + (ctx.hwaddr_ && + (!lease->hwaddr_ || (*ctx.hwaddr_ != *lease->hwaddr_)))) { + changed = true; + lease->hwaddr_ = ctx.hwaddr_; + } + if (ctx.subnet_->getMatchClientId() && ctx.clientid_) { + if (!lease->client_id_ || (*ctx.clientid_ != *lease->client_id_)) { + changed = true; + lease->client_id_ = ctx.clientid_; + } + } else if (lease->client_id_) { + changed = true; + lease->client_id_ = ClientIdPtr(); + } lease->cltt_ = time(NULL); if (ctx.query_->inClass("BOOTP")) { // BOOTP uses infinite valid lifetime. @@ -4244,28 +4264,43 @@ AllocEngine::updateLease4Information(const Lease4Ptr& lease, lease->valid_lft_ = ctx.subnet_->getValid(); } } + // Reduced valid lifetime is a significant change. + if (lease->valid_lft_ < lease->current_valid_lft_) { + changed = true; + } - lease->fqdn_fwd_ = ctx.fwd_dns_update_; - lease->fqdn_rev_ = ctx.rev_dns_update_; - lease->hostname_ = ctx.hostname_; + if ((lease->fqdn_fwd_ != ctx.fwd_dns_update_) || + (lease->fqdn_rev_ != ctx.rev_dns_update_) || + (lease->hostname_ != ctx.hostname_)) { + changed = true; + lease->fqdn_fwd_ = ctx.fwd_dns_update_; + lease->fqdn_rev_ = ctx.rev_dns_update_; + lease->hostname_ = ctx.hostname_; + } // Add(update) the extended information on the lease. - updateLease4ExtendedInfo(lease, ctx); + if (updateLease4ExtendedInfo(lease, ctx)) { + changed = true; + } + + return (changed); } -void +bool AllocEngine::updateLease4ExtendedInfo(const Lease4Ptr& lease, const AllocEngine::ClientContext4& ctx) const { + bool changed = false; + // If storage is not enabled then punt. if (!ctx.subnet_->getStoreExtendedInfo()) { - return; + return (changed); } // Look for relay agent information option (option 82) OptionPtr rai = ctx.query_->getOption(DHO_DHCP_AGENT_OPTIONS); if (!rai) { // Pkt4 doesn't have it, so nothing to store (or update). - return; + return (changed); } // Create a StringElement with the hex string for relay-agent-info. @@ -4284,24 +4319,31 @@ AllocEngine::updateLease4ExtendedInfo(const Lease4Ptr& lease, } // Add/replace the extended info entry. - user_context->set("ISC", extended_info); + ConstElementPtr old_extended_info = user_context->get("ISC"); + if (!old_extended_info || (*old_extended_info != *extended_info)) { + changed = true; + user_context->set("ISC", extended_info); + } // Update the lease's user_context. lease->setContext(user_context); + + return (changed); } -void +bool AllocEngine::updateLease6ExtendedInfo(const Lease6Ptr& lease, const AllocEngine::ClientContext6& ctx) const { + bool changed = false; // If storage is not enabled then punt. if (!ctx.subnet_->getStoreExtendedInfo()) { - return; + return (changed); } // If we do not have relay information, then punt. if (ctx.query_->relay_info_.empty()) { - return; + return (changed); } // We need to convert the vector of RelayInfo instances in @@ -4355,10 +4397,16 @@ AllocEngine::updateLease6ExtendedInfo(const Lease6Ptr& lease, } // Add/replace the extended info entry. - user_context->set("ISC", extended_info); + ConstElementPtr old_extended_info = user_context->get("ISC"); + if (!old_extended_info || (*old_extended_info != *extended_info)) { + changed = true; + user_context->set("ISC", extended_info); + } // Update the lease's user_context. lease->setContext(user_context); + + return (changed); } void @@ -4370,6 +4418,9 @@ AllocEngine::setLeaseRemainingLife(const Lease4Ptr& lease, if (!subnet) { return; } + if (lease->state_ != Lease::STATE_DEFAULT) { + return; + } // Always reuse infinite lifetime leases. if (lease->valid_lft_ == Lease::INFINITY_LFT) { @@ -4378,11 +4429,11 @@ AllocEngine::setLeaseRemainingLife(const Lease4Ptr& lease, } // Refuse time not going forward. - if (lease->cltt_ >= lease->current_cltt_) { + if (lease->cltt_ < lease->current_cltt_) { return; } - uint32_t age = lease->current_cltt_ - lease->cltt_; + uint32_t age = lease->cltt_ - lease->current_cltt_; // Already expired. if (age >= lease->current_valid_lft_) { return; @@ -4409,6 +4460,11 @@ AllocEngine::setLeaseRemainingLife(const Lease4Ptr& lease, } } + // No cache. + if (max_age == 0) { + return; + } + // Seems to be reusable. lease->remaining_valid_lft_ = lease->current_valid_lft_ - age; } @@ -4422,13 +4478,16 @@ AllocEngine::setLeaseRemainingLife(const Lease6Ptr& lease, if (!subnet) { return; } + if (lease->state_ != Lease::STATE_DEFAULT) { + return; + } // Refuse time not going forward. - if (lease->cltt_ >= lease->current_cltt_) { + if (lease->cltt_ < lease->current_cltt_) { return; } - uint32_t age = lease->current_cltt_ - lease->cltt_; + uint32_t age = lease->cltt_ - lease->current_cltt_; // Already expired. if (age >= lease->current_valid_lft_) { return; @@ -4455,6 +4514,11 @@ AllocEngine::setLeaseRemainingLife(const Lease6Ptr& lease, } } + // No cache. + if (max_age == 0) { + return; + } + // Seems to be reusable. if ((lease->remaining_preferred_lft_ == Lease::INFINITY_LFT) || (lease->remaining_preferred_lft_ == 0)) { diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index 6e1cdf903e..540a1dabf4 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.h @@ -1806,7 +1806,9 @@ private: /// @param [out] lease A pointer to the lease to be updated. /// @param ctx A context containing information from the server about the /// client and its message. - void updateLease4Information(const Lease4Ptr& lease, + /// @return True if there was a significant (e.g. other than cltt) change, + /// false otherwise. + bool updateLease4Information(const Lease4Ptr& lease, ClientContext4& ctx) const; protected: @@ -1824,7 +1826,9 @@ protected: /// @param [out] lease A pointer to the lease to be updated. /// @param ctx A context containing information from the server about the /// client and its message. - void updateLease4ExtendedInfo(const Lease4Ptr& lease, + /// @return True if there was a significant (e.g. other than cltt) change, + /// false otherwise. + bool updateLease4ExtendedInfo(const Lease4Ptr& lease, const ClientContext4& ctx) const; /// @brief Stores additional client query parameters on a V6 lease @@ -1842,7 +1846,9 @@ protected: /// @param [out] lease A pointer to the lease to be updated. /// @param ctx A context containing information from the server about the /// client and its message. - void updateLease6ExtendedInfo(const Lease6Ptr& lease, + /// @return True if there was a significant (e.g. other than cltt) change, + /// false otherwise. + bool updateLease6ExtendedInfo(const Lease6Ptr& lease, const ClientContext6& ctx) const; private: diff --git a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc index ffb2696f62..84b348697f 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc @@ -3501,6 +3501,7 @@ TEST_F(AllocEngine4Test, updateExtendedInfo4) { std::string orig_context_json_; // user context the lease begins with std::string rai_data_; // RAI option the client packet contains std::string exp_context_json_; // expected user context on the lease + bool exp_ret; // expected returned value }; // Test scenarios. @@ -3509,37 +3510,43 @@ TEST_F(AllocEngine4Test, updateExtendedInfo4) { "no context, no rai", "", "", - "" + "", + false }, { "some original context, no rai", "{\"foo\": 123}", "", - "{\"foo\": 123}" + "{\"foo\": 123}", + false }, { "no original context, rai", "", "0x52050104aabbccdd", "{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }", + true }, { "some original context, rai", "{\"foo\": 123}", "0x52050104aabbccdd", - "{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" }, \"foo\": 123 }" + "{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" }, \"foo\": 123 }", + true }, { "original rai context, no rai", "{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }", "", "{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }", + false }, { "original rai context, different rai", "{ \"ISC\": { \"relay-agent-info\": \"0x52050104AABBCCDD\" } }", "0x52050104ddeeffaa", "{ \"ISC\": { \"relay-agent-info\": \"0x52050104DDEEFFAA\" } }", + true }}; // Create the allocation engine, context and lease. @@ -3609,7 +3616,9 @@ TEST_F(AllocEngine4Test, updateExtendedInfo4) { } // Call AllocEngine::updateLease4ExtendeInfo(). - ASSERT_NO_THROW_LOG(engine.callUpdateLease4ExtendedInfo(lease, ctx)); + bool ret = false; + ASSERT_NO_THROW_LOG(ret = engine.callUpdateLease4ExtendedInfo(lease, ctx)); + ASSERT_EQ(scenario.exp_ret, ret); // Verify the lease has the expected user context content. if (!exp_context) { @@ -3827,6 +3836,435 @@ TEST_F(AllocEngine4Test, storeExtendedInfoDisabled4) { } } +// This test checks if a lease can be reused in DHCPDISCOVER (fake allocation) +// using cache threshold. +TEST_F(AllocEngine4Test, discoverCacheThreshold4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the threshold to 25%. + subnet_->setCacheThreshold(.25); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for fake allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", true); + + ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was reused. + time_t age = lease->cltt_ - now; + EXPECT_GE(age, 100); + EXPECT_LE(age, 110); + EXPECT_EQ(500 - age, lease->remaining_valid_lft_); + + // Check other lease parameters. + EXPECT_EQ(lease->subnet_id_, subnet_->getID()); + ASSERT_TRUE(lease->client_id_); + EXPECT_TRUE(*lease->client_id_ == *clientid_); + ASSERT_TRUE(lease->hwaddr_); + EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_); +} + +// This test checks if a lease can be reused in DHCPREQUEST (real allocation) +// using cache threshold. +TEST_F(AllocEngine4Test, requestCacheThreshold4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the threshold to 25%. + subnet_->setCacheThreshold(.25); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + // Copy the lease, so as it can be compared with. + Lease4Ptr original_lease(new Lease4(*lease)); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for real allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", false); + + ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was reused. + time_t age = lease->cltt_ - now; + EXPECT_GE(age, 100); + EXPECT_LE(age, 110); + EXPECT_EQ(500 - age, lease->remaining_valid_lft_); + + // Check other lease parameters. + EXPECT_EQ(lease->subnet_id_, subnet_->getID()); + ASSERT_TRUE(lease->client_id_); + EXPECT_TRUE(*lease->client_id_ == *clientid_); + ASSERT_TRUE(lease->hwaddr_); + EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_); + + // Check the lease was not updated in the database. + Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(from_mgr); + + detailCompareLease(original_lease, from_mgr); +} + +/// We proved that there is no different from the "cache" feature between +/// discovers and request at the exception of the lease database update. + +// This test checks if a lease can be reused in DHCPDISCOVER (fake allocation) +// using cache max age. +TEST_F(AllocEngine4Test, discoverCacheMaxAge4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the max age to 200. + subnet_->setCacheMaxAge(200); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for fake allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", true); + + ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was reused. + time_t age = lease->cltt_ - now; + EXPECT_GE(age, 100); + EXPECT_LE(age, 110); + EXPECT_EQ(500 - age, lease->remaining_valid_lft_); + + // Check other lease parameters. + EXPECT_EQ(lease->subnet_id_, subnet_->getID()); + ASSERT_TRUE(lease->client_id_); + EXPECT_TRUE(*lease->client_id_ == *clientid_); + ASSERT_TRUE(lease->hwaddr_); + EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_); +} + +// This test checks if a lease can be reused in DHCPREQUEST (real allocation) +// using both cache threshold and max age. +TEST_F(AllocEngine4Test, requestCacheBoth4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the threshold to 25%. + subnet_->setCacheThreshold(.25); + + // Set the max age to 200. + subnet_->setCacheMaxAge(200); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + // Copy the lease, so as it can be compared with. + Lease4Ptr original_lease(new Lease4(*lease)); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for real allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", false); + + ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was reused. + time_t age = lease->cltt_ - now; + EXPECT_GE(age, 100); + EXPECT_LE(age, 110); + EXPECT_EQ(500 - age, lease->remaining_valid_lft_); + + // Check other lease parameters. + EXPECT_EQ(lease->subnet_id_, subnet_->getID()); + ASSERT_TRUE(lease->client_id_); + EXPECT_TRUE(*lease->client_id_ == *clientid_); + ASSERT_TRUE(lease->hwaddr_); + EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_); + + // Check the lease was not updated in the database. + Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(from_mgr); + + detailCompareLease(original_lease, from_mgr); +} + +// This test checks if a lease can't be reused in DHCPDISCOVER (fake allocation) +// using too small cache threshold. +TEST_F(AllocEngine4Test, discoverCacheBadThreshold4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the threshold to 10%. + subnet_->setCacheThreshold(.1); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for fake allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", true); + + ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was not reused. + EXPECT_EQ(0, lease->remaining_valid_lft_); +} + +// This test checks if a lease can't be reused in DHCPREQUEST (real allocation) +// using too small cache max age. +TEST_F(AllocEngine4Test, requestCacheBadMaxAge4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the max age to 50. + subnet_->setCacheMaxAge(50); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + + // Create a context for real allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", false); + + ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was not reused. + EXPECT_EQ(0, lease->remaining_valid_lft_); + + // Check the lease was updated in the database. + Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(from_mgr); + + detailCompareLease(lease, from_mgr); +} + +// This test checks if a lease can't be reused in DHCPDISCOVER (fake allocation) +// when the valid lifetime was reduced. +TEST_F(AllocEngine4Test, discoverCacheReducedValid4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 200. + subnet_->setValid(200); + + // Set the threshold to 10%. + subnet_->setCacheThreshold(.1); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for fake allocation. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "", true); + + ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was not reused. + EXPECT_EQ(0, lease->remaining_valid_lft_); +} + +// This test checks if a lease can't be reused in DHCPREQUEST (real allocation) +// when DDNS parameter changed. +TEST_F(AllocEngine4Test, requestCacheFwdDDNS4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the max age to 200. + subnet_->setCacheMaxAge(200); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + + // Create a context for real allocation with fwd_dns_update changed. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + true, false, "", false); + + ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was not reused. + EXPECT_EQ(0, lease->remaining_valid_lft_); + + // Check the lease was updated in the database. + Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(from_mgr); + + detailCompareLease(lease, from_mgr); +} + +// This test checks if a lease can't be reused in DHCPDISCOVER (fake allocation) +// when DDNS parameter changed. +TEST_F(AllocEngine4Test, discoverCacheRevDDNS4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the threshold to 10%. + subnet_->setCacheThreshold(.1); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID())); + ASSERT_FALSE(lease->expired()); + ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); + + // Create a context for fake allocation with rev_dns_update changed. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, true, "", true); + + ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was not reused. + EXPECT_EQ(0, lease->remaining_valid_lft_); +} + +// This test checks if a lease can't be reused in DHCPREQUEST (real allocation) +// when hostname changed. +TEST_F(AllocEngine4Test, requestCacheHostname4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 0, false))); + ASSERT_TRUE(engine); + + // Set valid lifetime to 500. + subnet_->setValid(500); + + // Set the max age to 200. + subnet_->setCacheMaxAge(200); + + IOAddress addr("192.0.2.105"); + time_t now = time(NULL) - 100; // Allocated 100 seconds ago. + Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_, + 500, now, subnet_->getID(), + false, false, "foo")); + ASSERT_FALSE(lease->expired()); + + // Create a context for real allocation with fwd_dns_update changed. + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, addr, + false, false, "bar", false); + + ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); + lease = engine->allocateLease4(ctx); + // Check that we got that single lease. + ASSERT_TRUE(lease); + EXPECT_EQ(addr, lease->addr_); + + // The lease was not reused. + EXPECT_EQ(0, lease->remaining_valid_lft_); + EXPECT_EQ("bar", lease->hostname_); + + // Check the lease was updated in the database. + Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr); + ASSERT_TRUE(from_mgr); + + detailCompareLease(lease, from_mgr); +} + } // namespace test } // namespace dhcp } // namespace isc diff --git a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc index b1c9f855af..cc782eb914 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc @@ -4050,6 +4050,7 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { std::string orig_context_json_; // user context the lease begins with std::vector relays_; // vector of relays from pkt std::string exp_context_json_; // expected user context on the lease + bool exp_ret; // expected returned value }; // Test scenarios. @@ -4058,20 +4059,23 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { "no context, no relay", "", {}, - "" + "", + false }, { "some original context, no relay", "{\"foo\": 123}", {}, - "{\"foo\": 123}" + "{\"foo\": 123}", + false }, { "no original context, one relay", "", { relay1_ }, "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\"," - " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }" + " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }", + true }, { "some original context, one relay", @@ -4079,7 +4083,8 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { { relay1_ }, "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\"," " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] }," - " \"foo\": 123 }" + " \"foo\": 123 }", + true }, { "no original context, two relays", @@ -4087,7 +4092,8 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { { relay1_, relay2_ }, "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\"," " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" }," - " {\"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }" + " {\"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }", + true }, { "original relay context, no relay", @@ -4095,7 +4101,8 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }", {}, "{ \"ISC\": { \"relays\": [ { \"hop\": 33, \"link\": \"2001:db8::1\"," - " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }" + " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }", + false }, { "original relay context, different relay", @@ -4103,7 +4110,8 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { " \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }", { relay2_ }, "{ \"ISC\": { \"relays\": [ { \"hop\": 77, \"link\": \"2001:db8::3\"," - " \"peer\": \"2001:db8::4\" } ] } }" + " \"peer\": \"2001:db8::4\" } ] } }", + true }}; // Allocate a lease. @@ -4156,7 +4164,9 @@ TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) { ctx.query_->relay_info_ = scenario.relays_; // Call AllocEngine::updateLease6ExtendeInfo(). - ASSERT_NO_THROW_LOG(engine_.callUpdateLease6ExtendedInfo(lease, ctx)); + bool ret; + ASSERT_NO_THROW_LOG(ret = engine_.callUpdateLease6ExtendedInfo(lease, ctx)); + ASSERT_EQ(scenario.exp_ret, ret); // Verify the lease has the expected user context content. if (!exp_context) { diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.h b/src/lib/dhcpsrv/tests/alloc_engine_utils.h index 28dc70ac06..fde5a37ad0 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_utils.h +++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.h @@ -93,17 +93,19 @@ public: /// @brief Wrapper method for invoking AllocEngine4::updateLease4ExtendedInfo(). /// @param lease lease to update /// @param ctx current packet processing context - void callUpdateLease4ExtendedInfo(const Lease4Ptr& lease, + /// @return the changed returned value + bool callUpdateLease4ExtendedInfo(const Lease4Ptr& lease, AllocEngine::ClientContext4& ctx) const { - updateLease4ExtendedInfo(lease,ctx); + return (updateLease4ExtendedInfo(lease, ctx)); } /// @brief Wrapper method for invoking AllocEngine6::updateLease6ExtendedInfo(). /// @param lease lease to update /// @param ctx current packet processing context - void callUpdateLease6ExtendedInfo(const Lease6Ptr& lease, + /// @return the changed returned value + bool callUpdateLease6ExtendedInfo(const Lease6Ptr& lease, AllocEngine::ClientContext6& ctx) const { - updateLease6ExtendedInfo(lease,ctx); + return (updateLease6ExtendedInfo(lease, ctx)); } }; @@ -559,6 +561,7 @@ public: EXPECT_TRUE(*lease->client_id_ == *clientid_); } EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_); + EXPECT_EQ(0, lease->remaining_valid_lft_); /// @todo: check cltt } @@ -620,8 +623,8 @@ public: ///< allocation engine functions. }; -}; // namespace test -}; // namespace dhcp -}; // namespace isc +} // namespace test +} // namespace dhcp +} // namespace isc #endif