}
}
+\"calculate-tee-times\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser6Context::DHCP6:
+ case isc::dhcp::Parser6Context::SUBNET6:
+ case isc::dhcp::Parser6Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp6Parser::make_CALCULATE_TEE_TIMES(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp6Parser::make_STRING("calculate-tee-times", driver.loc_);
+ }
+}
+
+\"t1-percent\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser6Context::DHCP6:
+ case isc::dhcp::Parser6Context::SUBNET6:
+ case isc::dhcp::Parser6Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp6Parser::make_T1_PERCENT(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp6Parser::make_STRING("t1-percent", driver.loc_);
+ }
+}
+
+\"t2-percent\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser6Context::DHCP6:
+ case isc::dhcp::Parser6Context::SUBNET6:
+ case isc::dhcp::Parser6Context::SHARED_NETWORK:
+ return isc::dhcp::Dhcp6Parser::make_T2_PERCENT(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp6Parser::make_STRING("t2-percent", driver.loc_);
+ }
+}
+
\"Logging\" {
switch(driver.ctx_) {
case isc::dhcp::Parser6Context::CONFIG:
VALID_LIFETIME "valid-lifetime"
RENEW_TIMER "renew-timer"
REBIND_TIMER "rebind-timer"
+ CALCULATE_TEE_TIMES "calculate-tee-times"
+ T1_PERCENT "t1-percent"
+ T2_PERCENT "t2-percent"
DECLINE_PROBATION_PERIOD "decline-probation-period"
SERVER_TAG "server-tag"
SUBNET6 "subnet6"
| config_control
| server_tag
| reservation_mode
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
| unknown_map_entry
;
ctx.stack_.back()->set("rebind-timer", prf);
};
+calculate_tee_times: CALCULATE_TEE_TIMES COLON BOOLEAN {
+ ElementPtr ctt(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("calculate-tee-times", ctt);
+};
+
+t1_percent: T1_PERCENT COLON FLOAT {
+ ElementPtr t1(new DoubleElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("t1-percent", t1);
+};
+
+t2_percent: T2_PERCENT COLON FLOAT {
+ ElementPtr t2(new DoubleElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("t2-percent", t2);
+};
+
decline_probation_period: DECLINE_PROBATION_PERIOD COLON INTEGER {
ElementPtr dpp(new IntElement($3, ctx.loc2pos(@3)));
ctx.stack_.back()->set("decline-probation-period", dpp);
(config_pair.first == "decline-probation-period") ||
(config_pair.first == "dhcp4o6-port") ||
(config_pair.first == "server-tag") ||
- (config_pair.first == "reservation-mode")) {
+ (config_pair.first == "reservation-mode") ||
+ (config_pair.first == "calculate-tee-times") ||
+ (config_pair.first == "t1-percent") ||
+ (config_pair.first == "t2-percent")) {
+
CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
config_pair.second);
continue;
dhcp6_unittests_SOURCES += sarr_unittest.cc
dhcp6_unittests_SOURCES += simple_parser6_unittest.cc
dhcp6_unittests_SOURCES += shared_network_unittest.cc
+dhcp6_unittests_SOURCES += tee_times_unittest.cc
dhcp6_unittests_SOURCES += vendor_opts_unittest.cc
nodist_dhcp6_unittests_SOURCES = marker_file.h test_libraries.h
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
return (0xFFFF);
}
+bool
+Dhcp6Client::getTeeTimes(const uint32_t iaid, uint32_t& t1, uint32_t& t2) const {
+
+ auto leases = getLeasesByIAID(iaid);
+ if (leases.empty()) {
+ // No aquired leases so punt.
+ return (false);
+ }
+
+ // All leases for a given iaid should have the same values for T1
+ // and T2, so using them from the first one should be fine.
+ t1 = leases[0].t1_;
+ t2 = leases[0].t2_;
+ return (true);
+}
void
Dhcp6Client::setDUID(const std::string& str) {
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
/// @brief Returns status code set by the server for the IAID.
///
- /// @warning This method doesn't check if the specified index is out of
- /// range. The caller is responsible for using a correct offset by
- /// invoking the @c getLeaseNum function.
- ///
- /// @param at Index of the lease held by the client.
- /// @return A status code for the lease at the specified index.
+ /// @param iaid for which the status is desired
+ /// @return A status code for the given iaid
uint16_t getStatusCode(const uint32_t iaid) const;
+ /// @brief Returns T1 and T2 timers associated with a given iaid
+ ///
+ /// Currently this method gleans the T1 an T2 times from the first
+ /// aquired lease belonging to the target iaid. Since all leases for
+ /// an iaid should have the same values for T1 and T2, this should be
+ /// fine. If there are no acquired leases for the iaid, then t1 and
+ /// t2 are indeterminate.
+ ///
+ /// The primary impetus for this method is isolate the fact that T1
+ /// and T2 are stored on each lease. This may change in the future.
+ ///
+ /// @param iaid iaid of the target IA
+ /// @param[out] t1 set to the value of the IA's T1
+ /// @param[out] t2 set to the value of the IA's T2
+ /// if there are no leases for the iaid
+ ///
+ /// @return true if there are aquired leases for the given iaid
+ bool getTeeTimes(const uint32_t iaid, uint32_t& t1, uint32_t& t2) const;
+
/// @brief Returns number of acquired leases.
size_t getLeaseNum() const {
return (config_.leases_.size());
--- /dev/null
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/option_string.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp6/tests/dhcp6_message_test.h>
+#include <dhcpsrv/utils.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used throughout the Rebind tests.
+///
+/// - Configuration 0:
+/// - only addresses (no prefixes)
+/// - 2 subnets with 2001:db8:1::/64 and 2001:db8:2::/64
+/// - 1 subnet for eth0 and 1 subnet for eth1
+///
+const char* TEE_CONFIGS[] = {
+ // Configuration 0, Timers explicitly set
+ "{ \n"
+ " \"interfaces-config\": { \n"
+ " \"interfaces\": [ \"*\" ] \n"
+ " }, \n"
+ " \"renew-timer\": 1000, \n"
+ " \"rebind-timer\": 2000, \n"
+ " \"preferred-lifetime\": 3000, \n"
+ " \"valid-lifetime\": 4000, \n"
+ " \"subnet6\": [ { \n"
+ " \"interface\": \"eth0\", \n"
+ " \"subnet\": \"2001:db8:1::/48\", \n"
+ " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], \n"
+ " \"pd-pools\": [ \n"
+ " { \n"
+ " \"prefix\": \"3000::\", \n "
+ " \"prefix-len\": 72, \n"
+ " \"delegated-len\": 80 \n"
+ " }] \n"
+ " }] \n"
+ "} \n"
+ , // Configuration 1, Calculate default timers
+ "{ \n"
+ " \"interfaces-config\": { \n"
+ " \"interfaces\": [ \"*\" ] \n"
+ " }, \n"
+ " \"calculate-tee-times\": true, \n"
+ " \"preferred-lifetime\": 3000, \n"
+ " \"valid-lifetime\": 4000, \n"
+ " \"subnet6\": [ { \n"
+ " \"interface\": \"eth0\", \n"
+ " \"subnet\": \"2001:db8:1::/48\", \n"
+ " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], \n"
+ " \"pd-pools\": [ \n"
+ " { \n"
+ " \"prefix\": \"3000::\", \n "
+ " \"prefix-len\": 72, \n"
+ " \"delegated-len\": 80 \n"
+ " }] \n"
+ " }] \n"
+ "} \n"
+};
+
+/// @brief Test fixture class for testing Rebind.
+class TeeTest : public Dhcpv6MessageTest {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Sets up fake interfaces.
+ TeeTest()
+ : Dhcpv6MessageTest() {
+ }
+
+ void genRequest(const std::string& config, Dhcp6Client& client,
+ uint32_t exp_leases) {
+ // Configure the server.
+ ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+ // Do the actual 4-way exchange.
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Make sure that we go the expectec number of leases.
+ ASSERT_EQ(exp_leases, client.getLeaseNum());
+
+ // Simulate aging of leases, by moving their cltt_ back by 1000s.
+ client.fastFwdTime(1000);
+ }
+};
+
+TEST_F(TeeTest, explicitTimers) {
+ Dhcp6Client client;
+
+ uint32_t na_iaid = 2222;
+ client.requestAddress(na_iaid);
+
+ uint32_t pd_iaid = 3333;
+ client.requestPrefix(pd_iaid);
+
+ uint32_t exp_leases = 2;
+
+ // Configure client to request IA_NA.
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(genRequest(TEE_CONFIGS[0], client, exp_leases));
+
+ // Make sure the timers are right for both IAs
+ uint32_t actual_t1;
+ uint32_t actual_t2;
+
+ ASSERT_TRUE(client.getTeeTimes(na_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(1000, actual_t1);
+ EXPECT_EQ(2000, actual_t2);
+
+ ASSERT_TRUE(client.getTeeTimes(pd_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(1000, actual_t1);
+ EXPECT_EQ(2000, actual_t2);
+
+ // Let's renew the leases.
+ ASSERT_NO_THROW(client.doRenew());
+
+ // Now check the timers again.
+ ASSERT_TRUE(client.getTeeTimes(na_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(1000, actual_t1);
+ EXPECT_EQ(2000, actual_t2);
+
+ ASSERT_TRUE(client.getTeeTimes(pd_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(1000, actual_t1);
+ EXPECT_EQ(2000, actual_t2);
+}
+
+TEST_F(TeeTest, calculateTimers) {
+ Dhcp6Client client;
+
+ uint32_t na_iaid = 2222;
+ client.requestAddress(na_iaid);
+
+ uint32_t pd_iaid = 3333;
+ client.requestPrefix(pd_iaid);
+
+ uint32_t exp_leases = 2;
+
+ // Configure client to request IA_NA.
+ // Make 4-way exchange to get the lease.
+ ASSERT_NO_FATAL_FAILURE(genRequest(TEE_CONFIGS[1], client, exp_leases));
+
+ // Make sure the timers are right for both IAs
+ uint32_t actual_t1;
+ uint32_t actual_t2;
+
+ ASSERT_TRUE(client.getTeeTimes(na_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(2000, actual_t1);
+ EXPECT_EQ(3200, actual_t2);
+
+ ASSERT_TRUE(client.getTeeTimes(pd_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(2000, actual_t1);
+ EXPECT_EQ(3200, actual_t2);
+
+ // Let's renew the leases.
+ ASSERT_NO_THROW(client.doRenew());
+
+ // Now check the timers again.
+ ASSERT_TRUE(client.getTeeTimes(na_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(2000, actual_t1);
+ EXPECT_EQ(3200, actual_t2);
+
+ ASSERT_TRUE(client.getTeeTimes(pd_iaid, actual_t1, actual_t2));
+ EXPECT_EQ(2000, actual_t1);
+ EXPECT_EQ(3200, actual_t2);
+}
+
+
+} // end of anonymous namespace
// Copy options to the subnet configuration.
options_->copyTo(*subnet4->getCfgOption());
+ // Parse t1-percent and t2-percent
parseTeePercents(params, network);
}
// Copy options to the subnet configuration.
options_->copyTo(*subnet6->getCfgOption());
+
+ // Parse t1-percent and t2-percent
+ parseTeePercents(params, network);
}
//**************************** Subnet6ListConfigParser ********************
{ "reservations", Element::list },
{ "config-control", Element::map },
{ "server-tag", Element::string },
- { "reservation-mode", Element::string }
+ { "reservation-mode", Element::string },
+ { "calculate-tee-times", Element::boolean },
+ { "t1-percent", Element::real },
+ { "t2-percent", Element::real }
};
/// @brief This table defines default values for option definitions in DHCPv6.
/// in Dhcp6) are optional. If not defined, the following values will be
/// used.
const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
- { "renew-timer", Element::integer, "900" },
- { "rebind-timer", Element::integer, "1800" },
{ "preferred-lifetime", Element::integer, "3600" },
{ "valid-lifetime", Element::integer, "7200" },
{ "decline-probation-period", Element::integer, "86400" }, // 24h
{ "dhcp4o6-port", Element::integer, "0" },
{ "server-tag", Element::string, "" },
- { "reservation-mode", Element::string, "all" }
+ { "reservation-mode", Element::string, "all" },
+ { "calculate-tee-times", Element::boolean, "false" },
+ { "t1-percent", Element::real, ".50" },
+ { "t2-percent", Element::real, ".80" }
};
/// @brief This table defines default values for each IPv6 subnet.
"relay",
"renew-timer",
"reservation-mode",
- "valid-lifetime"
+ "valid-lifetime",
+ "calculate-tee-times",
+ "t1-percent",
+ "t2-percent"
};
/// @brief This table defines default values for dhcp-queue-control in DHCPv4.
network1->setIface("eth0");
network1->addRelayAddress(IOAddress("2001:db8:1::1"));
network1->addRelayAddress(IOAddress("2001:db8:1::2"));
+ network1->setCalculateTeeTimes(true);
+ network1->setT1Percent(.35);
+ network1->setT2Percent(.655);
network2->setIface("eth1");
network2->setT1(Triplet<uint32_t>(100));
" \"valid-lifetime\": 300\n"
" },\n"
" {\n"
+ " \"calculate-tee-times\": true,\n"
" \"interface\": \"eth0\",\n"
" \"name\": \"frog\",\n"
" \"option-data\": [ ],\n"
" \"relay\": { \"ip-addresses\": [ \"2001:db8:1::1\", \"2001:db8:1::2\" ] },\n"
- " \"subnet6\": [ ]\n"
+ " \"subnet6\": [ ],\n"
+ " \"t1-percent\": .35,\n"
+ " \"t2-percent\": .655\n"
" }\n"
"]\n";
#include <dhcp/option_string.h>
#include <dhcpsrv/cfg_shared_networks.h>
#include <dhcpsrv/cfg_subnets6.h>
+#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <testutils/test_to_element.h>
+#include <util/doubles.h>
+
#include <gtest/gtest.h>
#include <string>
OptionPtr ifaceid = generateInterfaceId("relay.eth0");
subnet1->setInterfaceId(ifaceid);
subnet1->allowClientClass("foo");
+
+ subnet1->setT1Percent(0.45);
+ subnet1->setT2Percent(0.70);
+
subnet2->setIface("lo");
subnet2->addRelayAddress(IOAddress("2001:db8:ff::2"));
subnet3->setIface("eth1");
subnet3->requireClientClass("bar");
subnet3->setHostReservationMode(Network::HR_ALL);
subnet3->setRapidCommit(false);
+ subnet3->setCalculateTeeTimes(true);
+ subnet3->setT1Percent(0.50);
+ subnet3->setT2Percent(0.65);
data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }");
subnet1->setContext(ctx1);
" \"comment\": \"foo\",\n"
" \"id\": 123,\n"
" \"subnet\": \"2001:db8:1::/48\",\n"
+ " \"t1-percent\": 0.45,"
+ " \"t2-percent\": 0.7,"
" \"interface-id\": \"relay.eth0\",\n"
" \"renew-timer\": 1,\n"
" \"rebind-timer\": 2,\n"
" \"pools\": [ ],\n"
" \"pd-pools\": [ ],\n"
" \"option-data\": [ ],\n"
- " \"require-client-classes\": [ \"foo\", \"bar\" ]\n"
+ " \"require-client-classes\": [ \"foo\", \"bar\" ],\n"
+ " \"calculate-tee-times\": true,\n"
+ " \"t1-percent\": 0.50,\n"
+ " \"t2-percent\": 0.65\n"
"} ]\n";
runToElementTest<CfgSubnets6>(expected, cfg);
}
EXPECT_EQ("RULE!", opstr->getValue());
}
+// This test verifies the Subnet6 parser's validation logic for
+// t1-percent and t2-percent parameters.
+TEST(CfgSubnets6Test, teeTimePercentValidation) {
+
+ // Describes a single test scenario.
+ struct Scenario {
+ std::string label; // label used for logging test failures
+ bool calculate_tee_times; // value of calculate-tee-times parameter
+ double t1_percent; // value of t1-percent parameter
+ double t2_percent; // value of t2-percent parameter
+ std::string error_message; // expected error message is parsing should fail
+ };
+
+ // Test Scenarios.
+ std::vector<Scenario> tests = {
+ {"off and valid", false, .5, .95, ""},
+ {"on and valid", true, .5, .95, ""},
+ {"t2_negative", true, .5, -.95,
+ "subnet configuration failed: t2-percent:"
+ " -0.95 is invalid, it must be greater than 0.0 and less than 1.0"
+ },
+ {"t2_too_big", true, .5, 1.95,
+ "subnet configuration failed: t2-percent:"
+ " 1.95 is invalid, it must be greater than 0.0 and less than 1.0"
+ },
+ {"t1_negative", true, -.5, .95,
+ "subnet configuration failed: t1-percent:"
+ " -0.5 is invalid it must be greater than 0.0 and less than 1.0"
+ },
+ {"t1_too_big", true, 1.5, .95,
+ "subnet configuration failed: t1-percent:"
+ " 1.5 is invalid it must be greater than 0.0 and less than 1.0"
+ },
+ {"t1_bigger_than_t2", true, .85, .45,
+ "subnet configuration failed: t1-percent:"
+ " 0.85 is invalid, it must be less than t2-percent: 0.45"
+ }
+ };
+
+ // First we create a set of elements that provides all
+ // required for a Subnet6.
+ std::string json =
+ " {"
+ " \"id\": 1,\n"
+ " \"subnet\": \"2001:db8:1::/64\", \n"
+ " \"interface\": \"\", \n"
+ " \"renew-timer\": 100, \n"
+ " \"rebind-timer\": 200, \n"
+ " \"valid-lifetime\": 300, \n"
+ " \"client-class\": \"\", \n"
+ " \"require-client-classes\": [] \n,"
+ " \"reservation-mode\": \"all\", \n"
+ " \"4o6-interface\": \"\", \n"
+ " \"4o6-interface-id\": \"\", \n"
+ " \"4o6-subnet\": \"\", \n"
+ " \"dhcp4o6-port\": 0, \n"
+ " \"decline-probation-period\": 86400 \n"
+ " }";
+
+
+ data::ElementPtr elems;
+ ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+ << "invalid JSON:" << json << "\n test is broken";
+
+ // Iterate over the test scenarios, verifying each prescribed
+ // outcome.
+ for (auto test = tests.begin(); test != tests.end(); ++test) {
+ {
+ SCOPED_TRACE("test: " + (*test).label);
+
+ // Set this scenario's configuration parameters
+ elems->set("calculate-tee-times", data::Element::create((*test).calculate_tee_times));
+ elems->set("t1-percent", data::Element::create((*test).t1_percent));
+ elems->set("t2-percent", data::Element::create((*test).t2_percent));
+
+ Subnet6Ptr subnet;
+ try {
+ // Attempt to parse the configuration.
+ Subnet6ConfigParser parser;
+ subnet = parser.parse(elems);
+ } catch (const std::exception& ex) {
+ if (!(*test).error_message.empty()) {
+ // We expected a failure, did we fail the correct way?
+ EXPECT_EQ((*test).error_message, ex.what());
+ } else {
+ // Should not have failed.
+ ADD_FAILURE() << "Scenario should not have failed: " << ex.what();
+ }
+
+ // Either way we're done with this scenario.
+ continue;
+ }
+
+ // We parsed correctly, make sure the values are right.
+ EXPECT_EQ((*test).calculate_tee_times, subnet->getCalculateTeeTimes());
+ EXPECT_TRUE(util::areDoublesEquivalent((*test).t1_percent, subnet->getT1Percent()))
+ << "expected:" << (*test).t1_percent << " actual: " << subnet->getT1Percent();
+ EXPECT_TRUE(util::areDoublesEquivalent((*test).t2_percent, subnet->getT2Percent()))
+ << "expected:" << (*test).t2_percent << " actual: " << subnet->getT2Percent();
+ }
+ }
+}
+
} // end of anonymous namespace