.arg(ia->getIAID())
.arg(lease->toText());
- ia_rsp->setT1(subnet->getT1());
- ia_rsp->setT2(subnet->getT2());
+ // Set the values for T1 and T2.
+ setTeeTimes(lease->valid_lft_, subnet, ia_rsp);
Option6IAAddrPtr addr(new Option6IAAddr(D6O_IAADDR, lease->addr_,
lease->preferred_lft_,
if (!leases.empty()) {
- ia_rsp->setT1(subnet->getT1());
- ia_rsp->setT2(subnet->getT2());
+ // Need to retain the shortest valid lease time to use
+ // for calculating T1 and T2.
+ uint32_t min_valid_lft = (*leases.begin())->valid_lft_;
const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
-
for (Lease6Collection::iterator l = leases.begin();
l != leases.end(); ++l) {
+ // Check for new minimum lease time
+ if (min_valid_lft > (*l)->valid_lft_) {
+ min_valid_lft = (*l)->valid_lft_;
+ }
+
// We have a lease! Let's wrap its content into IA_PD option
// with IAADDR suboption.
LOG_INFO(lease6_logger, ctx.fake_allocation_ ?
}
}
+ // Set T1 and T2, using the shortest valid lifetime among the leases.
+ setTeeTimes(min_valid_lft, subnet, ia_rsp);
+
// It would be possible to insert status code=0(success) as well,
// but this is considered waste of bandwidth as absence of status
// code is considered a success.
return (ia_rsp);
}
- // Set up T1, T2 timers
- ia_rsp->setT1(subnet->getT1());
- ia_rsp->setT2(subnet->getT2());
-
// Get DDNS update directions
bool do_fwd = false;
bool do_rev = false;
// into temporary container.
AllocEngine::HintContainer hints = ctx.currentIA().hints_;
+ // Retains the shortest valid lease time to use
+ // for calculating T1 and T2.
+ uint32_t min_valid_lft = std::numeric_limits<uint32_t>::max();
+
// For all leases we have now, add the IAADDR with non-zero lifetimes.
for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
(*l)->addr_, (*l)->preferred_lft_, (*l)->valid_lft_));
ia_rsp->addOption(iaaddr);
- LOG_INFO(lease6_logger, DHCP6_LEASE_RENEW)
+
+ // Check for new minimum lease time
+ if ((*l)->valid_lft_ < min_valid_lft) {
+ min_valid_lft = (*l)->valid_lft_;
+ }
+
+ LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW)
.arg(query->getLabel())
.arg((*l)->addr_.toText())
- .arg(ia_rsp->getIAID());
+ .arg(static_cast<int>((*l)->prefixlen_))
+ .arg(ia->getIAID());
- // Now remove this address from the hints list.
- AllocEngine::ResourceType hint_type((*l)->addr_, 128);
+ // Now remove this prefix from the hints list.
+ AllocEngine::ResourceType hint_type((*l)->addr_, (*l)->prefixlen_);
hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
hints.end());
}
ia_rsp->addOption(iaaddr);
}
- // All is left is to insert the status code.
- if (leases.empty()) {
-
+ if (!leases.empty()) {
+ // We allocated leases so we need to update T1 and T2.
+ setTeeTimes(min_valid_lft, subnet, ia_rsp);
+ } else {
// The server wasn't able allocate new lease and renew an existing
// lease. In that case, the server sends NoAddrsAvail per RFC 8415.
ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
}
}
- // Set up T1, T2 timers
- ia_rsp->setT1(subnet->getT1());
- ia_rsp->setT2(subnet->getT2());
-
// Set per-IA context values.
ctx.createIAContext();
ctx.currentIA().iaid_ = ia->getIAID();
const bool pd_exclude_requested = requestedInORO(query, D6O_PD_EXCLUDE);
- // For all the leases we have now, add the IAPREFIX with non-zero lifetimes
+ // Retains the shortest valid lease time to use
+ // for calculating T1 and T2.
+ uint32_t min_valid_lft = std::numeric_limits<uint32_t>::max();
+
for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
Option6IAPrefixPtr prf(new Option6IAPrefix(D6O_IAPREFIX,
}
}
+ // Check for new minimum lease time
+ if ((*l)->valid_lft_ < min_valid_lft) {
+ min_valid_lft = (*l)->valid_lft_;
+ }
LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW)
.arg(query->getLabel())
}
}
- // All is left is to insert the status code.
- if (leases.empty()) {
-
+ if (!leases.empty()) {
+ // We allocated leases so we need to update T1 and T2.
+ setTeeTimes(min_valid_lft, subnet, ia_rsp);
+ } else {
+ // All is left is to insert the status code.
// The server wasn't able allocate new lease and renew an existing
// lease. In that case, the server sends NoPrefixAvail per RFC 8415.
ia_rsp->addOption(createStatusCode(*query, *ia_rsp,
HooksManager::clearParkingLots();
}
+void
+Dhcpv6Srv::setTeeTimes(uint32_t valid_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp) {
+ uint32_t t2_time = 0;
+ // If T2 is explicitly configured we'll use try value.
+ if (!subnet->getT2().unspecified()) {
+ t2_time = subnet->getT2();
+ } else if (subnet->getCalculateTeeTimes()) {
+ // Calculating tee times is enabled, so calculated it.
+ t2_time = static_cast<uint32_t>(subnet->getT2Percent() * valid_lft);
+ }
+
+ // Send the T2 candidate value only if it's sane: to be sane it must be less than
+ // the valid life time.
+ uint32_t timer_ceiling = valid_lft;
+ if (t2_time > 0 && t2_time < timer_ceiling) {
+ resp->setT2(t2_time);
+ // When we send T2, timer ceiling for T1 becomes T2.
+ timer_ceiling = t2_time;
+ } else {
+ // It's either explicitly 0 or insane, leave it to the client
+ resp->setT2(0);
+ }
+
+ uint32_t t1_time = 0;
+ // If T1 is explicitly configured we'll use try value.
+ if (!subnet->getT1().unspecified()) {
+ t1_time = subnet->getT1();
+ } else if (subnet->getCalculateTeeTimes()) {
+ // Calculating tee times is enabled, so calculate it.
+ t1_time = static_cast<uint32_t>(subnet->getT1Percent() * valid_lft);
+ }
+
+ // Send T1 if it's sane: If we sent T2, T1 must be less than that. If not it must be
+ // less than the valid life time.
+ if (t1_time > 0 && t1_time < timer_ceiling) {
+ resp->setT1(t1_time);
+ } else {
+ // It's either explicitly 0 or insane, leave it to the client
+ resp->setT1(0);
+ }
+}
+
};
};
void extendLeases(const Pkt6Ptr& query, Pkt6Ptr& reply,
AllocEngine::ClientContext6& ctx);
+ /// @brief Sets the T1 and T2 timers in the outbound IA
+ ///
+ /// This method determines the values for both the T1 and T2
+ /// timers for the given IA. It is influenced by the
+ /// lease's subnet's values for renew-timer, rebind-timer,
+ /// calculate-tee-times, t1-percent, and t2-percent as follows:
+ ///
+ /// T2:
+ ///
+ /// The candidate value for T2 defaults to zero. If the rebind-timer value
+ /// is specified then use it. If not and calculate-tee-times is true, then
+ /// use the value given by: valid lease time * t2-percent.
+ ///
+ /// If the T2 candidate is less than the valid lease time use it,
+ /// otherwise set T2 to zero.
+ ///
+ /// T1:
+ ///
+ /// The candidate value for T1 defaults to zero. If the renew-timer value
+ /// is specified then use it. If not and calculate-tee-times is true, then
+ /// use the value given by: valid lease time * t1-percent.
+ ///
+ /// The T1 candidate will be used provided it less than the T2 when T2 is
+ /// is greater than zero. When T2 is zero then the T1 candidate must be
+ /// less than the valid lease time, otherwise T1 will be set to zero.
+ ///
+ /// @param lease lease being assigned to the client
+ /// @param subnet the subnet to which the lease belongs
+ /// @param resp outbound IA option in which the timers are set.
+ void setTeeTimes(uint32_t valid_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp);
+
/// @brief Attempts to release received addresses
///
/// It iterates through received IA_NA options and attempts to release
ASSERT_TRUE(adv);
}
+// Check that T1 and T2 values are set correctly.
+TEST_F(Dhcpv6SrvTest, calculateTeeTimers) {
+ NakedDhcpv6Srv srv(0);
+
+ // Struct for describing an individual timer test scenario
+ struct TimerTest {
+ // logged test description
+ std::string description_;
+ // configured value for subnet's T1
+ Triplet<uint32_t> cfg_t1_;
+ // configured value for subnet's T1
+ Triplet<uint32_t> cfg_t2_;
+ // whether or not calculation is enabled
+ bool calculate_tee_times;
+ // configured value for sunbet's t1_percent.
+ double t1_percent_;
+ // configured value for sunbet's t2_percent.
+ double t2_percent_;
+ // expected value for T1 in server response.
+ // A value of 0 means server should not have sent T1.
+ uint32_t t1_exp_value_;
+ // expected value for T2 in server response.
+ // A value of 0 means server should not have sent T2.
+ uint32_t t2_exp_value_;
+ };
+
+ // Handy constants
+ Triplet<uint32_t> unspecified;
+ Triplet<uint32_t> valid_lft(1000);
+ bool calculate_enabled = true;
+
+ // Test scenarios
+ std::vector<TimerTest> tests = {
+ // Tests with calculation disabled
+ {
+ "T1 and T2 calculated",
+ unspecified, unspecified,
+ calculate_enabled,
+ 0.4, 0.8,
+ 400, 800
+ },
+ {
+ "T1 and T2 specified insane",
+ valid_lft + 1, valid_lft + 2,
+ calculate_enabled,
+ 0.4, 0.8,
+ 0, 0
+ },
+ {
+ "T1 should be calculated, T2 specified",
+ unspecified, valid_lft - 1,
+ calculate_enabled,
+ 0.4, 0.8,
+ 400, valid_lft - 1
+ },
+ {
+ "T1 specified, T2 should be calculated",
+ 299, unspecified,
+ calculate_enabled,
+ 0.4, 0.8,
+ 299, 800
+ },
+ {
+ "T1 specified insane (> T2), T2 should be calculated",
+ valid_lft - 1, unspecified,
+ calculate_enabled,
+ 0.4, 0.8,
+ 0, 800
+ },
+ // Tests with calculation disabled
+ {
+ "T1 and T2 unspecified, (no calculation)",
+ unspecified, unspecified,
+ !calculate_enabled,
+ 0.4, 0.8,
+ 0, 0
+ },
+ {
+ "T1 specified, T2 unspecified (no calculation)",
+ valid_lft - 1, unspecified,
+ !calculate_enabled,
+ 0.4, 0.8,
+ valid_lft - 1, 0
+ },
+ {
+ "both T1 and T2 specified (no calculation)",
+ valid_lft - 2, valid_lft - 1,
+ !calculate_enabled,
+ 0.4, 0.8,
+ valid_lft - 2, valid_lft - 1
+ },
+ {
+ "T1 specified insane (>= lease T2), T2 specified (no calculation)",
+ valid_lft - 1, valid_lft - 1,
+ !calculate_enabled,
+ 0.4, 0.8,
+ 0, valid_lft - 1
+ },
+ {
+ "T1 specified insane (>= lease time), T2 not specified (no calculation)",
+ valid_lft, unspecified,
+ !calculate_enabled,
+ 0.4, 0.8,
+ 0, 0
+ }
+ };
+
+ // Calculation is enabled for all the scenarios.
+ subnet_->setValid(valid_lft);
+
+ // Create a discover packet to use
+ Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+ sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
+ OptionPtr clientid = generateClientId();
+ sol->addOption(clientid);
+ sol->setIface("eth0");
+
+ // Iterate over the test scenarios.
+ for (auto test = tests.begin(); test != tests.end(); ++test) {
+ {
+ SCOPED_TRACE((*test).description_);
+ // Configure sunbet for the scenario
+ subnet_->setT1((*test).cfg_t1_);
+ subnet_->setT2((*test).cfg_t2_);
+ subnet_->setCalculateTeeTimes((*test).calculate_tee_times);
+ subnet_->setT1Percent((*test).t1_percent_);
+ subnet_->setT2Percent((*test).t2_percent_);
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(sol, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processSolicit(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+ // check that IA_NA was returned and T1 and T2 are correct.
+ checkIA_NA(reply, 234, (*test).t1_exp_value_, (*test).t2_exp_value_);
+ }
+ }
+}
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
/// to call processX() methods.
-// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// The following values are used by the callout to override
/// renewed lease parameters
static const uint32_t override_iaid_;
- static const uint32_t override_t1_;
- static const uint32_t override_t2_;
static const uint32_t override_preferred_;
static const uint32_t override_valid_;
EXPECT_TRUE(callback_lease6_);
// Let's override some values in the lease
callback_lease6_->iaid_ = override_iaid_;
- callback_lease6_->t1_ = override_t1_;
- callback_lease6_->t2_ = override_t2_;
callback_lease6_->preferred_lft_ = override_preferred_;
callback_lease6_->valid_lft_ = override_valid_;
EXPECT_TRUE(callback_ia_na_);
// Override the values to be sent to the client as well
callback_ia_na_->setIAID(override_iaid_);
- callback_ia_na_->setT1(override_t1_);
- callback_ia_na_->setT2(override_t2_);
callback_argument_names_ = callout_handle.getArgumentNames();
return (0);
EXPECT_TRUE(callback_lease6_);
// Let's override some values in the lease
callback_lease6_->iaid_ = override_iaid_;
- callback_lease6_->t1_ = override_t1_;
- callback_lease6_->t2_ = override_t2_;
callback_lease6_->preferred_lft_ = override_preferred_;
callback_lease6_->valid_lft_ = override_valid_;
EXPECT_TRUE(callback_ia_na_);
// Override the values to be sent to the client as well
callback_ia_na_->setIAID(override_iaid_);
- callback_ia_na_->setT1(override_t1_);
- callback_ia_na_->setT2(override_t2_);
callback_argument_names_ = callout_handle.getArgumentNames();
return (0);
// The following parameters are used by callouts to override
// renewed lease parameters
const uint32_t HooksDhcpv6SrvTest::override_iaid_ = 1000;
-const uint32_t HooksDhcpv6SrvTest::override_t1_ = 1001;
-const uint32_t HooksDhcpv6SrvTest::override_t2_ = 1002;
const uint32_t HooksDhcpv6SrvTest::override_preferred_ = 1003;
const uint32_t HooksDhcpv6SrvTest::override_valid_ = 1004;
addr);
ASSERT_TRUE(l);
- // Check that T1, T2, preferred, valid and cltt really set and not using
+ // Check that 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));
addr);
ASSERT_TRUE(l);
- // Check that T1, T2, preferred, valid and cltt really set and not using
+ // Check that 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));
// Server-id is mandatory in RENEW
req->addOption(srv.getServerID());
+ // Turn on tee time calculation so we can see the effect of overriding
+ // the lease life time.
+ subnet_->setCalculateTeeTimes(true);
+ Triplet<uint32_t> unspecified;
+ subnet_->setT1(unspecified);
+ subnet_->setT2(unspecified);
+ subnet_->setT1Percent(0.60);
+ subnet_->setT2Percent(0.80);
+
// Pass it to the server and hope for a REPLY
Pkt6Ptr reply = srv.processRenew(req);
ASSERT_TRUE(reply);
ASSERT_TRUE(tmp);
// Check that IA_NA was returned and that there's an address included
- boost::shared_ptr<Option6IAAddr> addr_opt = checkIA_NA(reply, 1000, 1001, 1002);
+ boost::shared_ptr<Option6IAAddr> addr_opt = checkIA_NA(reply, 1000, 602, 803);
ASSERT_TRUE(addr_opt);
// Check that the lease is really in the database
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_);
+ // Check that preferred, valid were overridden the the callout
EXPECT_EQ(override_preferred_, l->preferred_lft_);
EXPECT_EQ(override_valid_, l->valid_lft_);
addr);
ASSERT_TRUE(l);
- // Check that T1, T2, preferred, valid and cltt really set and not using
+ // Check that preferred, valid and cltt are 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));
// 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));
addr);
ASSERT_TRUE(l);
- // Check that T1, T2, preferred, valid and cltt really set and not using
+ // Check that 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));
// 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));
req->addOption(ia);
req->addOption(clientid);
+ // Turn on tee time calculation so we can see the effect of overriding
+ // the lease life time.
+ subnet_->setCalculateTeeTimes(true);
+ Triplet<uint32_t> unspecified;
+ subnet_->setT1(unspecified);
+ subnet_->setT2(unspecified);
+ subnet_->setT1Percent(0.60);
+ subnet_->setT2Percent(0.80);
+
// Pass it to the server and hope for a REPLY
Pkt6Ptr reply = srv.processRebind(req);
ASSERT_TRUE(reply);
ASSERT_TRUE(tmp);
// Check that IA_NA was returned and that there's an address included
- boost::shared_ptr<Option6IAAddr> addr_opt = checkIA_NA(reply, 1000, 1001, 1002);
+ // Note we also verify that T1 and T2 were calculated correctly.
+ boost::shared_ptr<Option6IAAddr> addr_opt = checkIA_NA(reply, 1000, 602, 803);
ASSERT_TRUE(addr_opt);
// Check that the lease is really in the database
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_);
+ // Check that preferred and valid were overridden in the callout
EXPECT_EQ(override_preferred_, l->preferred_lft_);
EXPECT_EQ(override_valid_, l->valid_lft_);
addr);
ASSERT_TRUE(l);
- // Check that T1, T2, preferred, valid and cltt really set and not using
+ // Check that 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));
// Check that the old values are still there and they were not
// updated by the rebinding
- 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));