<para>Moving onto the DHCPv4 configuration elements, the first few elements
define some global parameters. <command>valid-lifetime</command>
-defines for how long the addresses (leases) given out by the
+defines how long the addresses (leases) given out by the
server are valid. If nothing changes, a client that got an address is allowed to
use it for 4000 seconds. (Note that integer numbers are specified as is,
without any quotes around them.) <command>renew-timer</command> and
<command>rebind-timer</command> are values (also in seconds) that
define T1 and T2 timers that govern when the client will begin the renewal and
-rebind procedures. Note that <command>renew-timer</command> and
-<command>rebind-timer</command> are optional. If they are not specified the
-client will select values for T1 and T2 timers according to the
-<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://tools.ietf.org/html/rfc2131">RFC 2131</link>.</para>
+rebind procedures. <note> Both <command>renew-timer</command> and
+<command>rebind-timer</command> are optional. The server will only send
+rebind-timer to the client, via DHPv4 option code 59, if it is less than
+valid-lifetime; and it will only send renew-timer, via DHCPv4 option code 58,
+if it is less than rebind-timer (or valid-lifetime if rebind-timer was not
+specified). In their absence, the client should select values for T1 and T2
+timers according to the <link xmlns:xlink="http://www.w3.org/1999/xlink"
+xlink:href="http://tools.ietf.org/html/rfc2131">RFC 2131</link>.</note></para>
<para>The <command>interfaces-config</command> map specifies the server
configuration concerning the network interfaces, on which the server should
<para>If "relay" is specified, the "ip-addresses" parameter within
it is mandatory.</para>
-
- <note>
+
+ <note>
<para>
- As of Kea 1.4, the "ip-address" parameter has been deprecated in favor
- of "ip-addresses" which supports specifying a list of addresses.
+ As of Kea 1.4, the "ip-address" parameter has been deprecated in favor
+ of "ip-addresses" which supports specifying a list of addresses.
Configuration parsing, will honor the singular form for now but users are
encouraged to migrate.
</para>
- </note>
+ </note>
</section>
// Subnet mask (type 1)
resp->addOption(getNetmaskOption(subnet));
- // renewal-timer (type 58)
- if (!subnet->getT1().unspecified()) {
+ // rebind timer (type 59) - if specified then send it only it if
+ // it is lease than lease life time. Note we "sanity" check T1
+ // and T2 against lease lifetime here in event the lifetime has
+ // been altered somewhere along the line.
+ uint32_t timer_ceiling = lease->valid_lft_;
+ if ((!subnet->getT2().unspecified()) &&
+ (subnet->getT2() < timer_ceiling)) {
+ OptionUint32Ptr t2(new OptionUint32(Option::V4,
+ DHO_DHCP_REBINDING_TIME,
+ subnet->getT2()));
+ resp->addOption(t2);
+
+ // If T2 is specified, then it becomes the ceiling for T1
+ timer_ceiling = subnet->getT2();
+ }
+
+ // renewal-timer (type 58) - if specified then send it only if
+ // it is less than the ceiling (T2 if given, lease life time if not)
+ if ((!subnet->getT1().unspecified()) &&
+ (subnet->getT1() < timer_ceiling)) {
OptionUint32Ptr t1(new OptionUint32(Option::V4,
DHO_DHCP_RENEWAL_TIME,
subnet->getT1()));
resp->addOption(t1);
}
- // rebind timer (type 59)
- if (!subnet->getT2().unspecified()) {
- OptionUint32Ptr t2(new OptionUint32(Option::V4,
- DHO_DHCP_REBINDING_TIME,
- subnet->getT2()));
- resp->addOption(t2);
- }
// Create NameChangeRequests if DDNS is enabled and this is a
// real allocation.
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
ASSERT_TRUE(subnet);
- EXPECT_FALSE(subnet->getT1().unspecified());
+
+ EXPECT_TRUE(subnet->getT1().unspecified());
EXPECT_FALSE(subnet->getT2().unspecified());
- EXPECT_EQ(900, subnet->getT1()); // that's the default value
EXPECT_EQ(2000, subnet->getT2());
EXPECT_EQ(4000, subnet->getValid());
// Check that subnet-id is 1
EXPECT_EQ(1, subnet->getID());
-
}
/// Check that the rebind-timer doesn't have to be specified, in which case
ASSERT_TRUE(subnet);
EXPECT_FALSE(subnet->getT1().unspecified());
EXPECT_EQ(1000, subnet->getT1());
- EXPECT_FALSE(subnet->getT2().unspecified());
- EXPECT_EQ(1800, subnet->getT2()); // that's the default value
+ EXPECT_TRUE(subnet->getT2().unspecified());
EXPECT_EQ(4000, subnet->getValid());
// Check that subnet-id is 1
checkClientId(offer, clientid);
}
-// Check that option 58 and 59 are not included if they are not specified.
-TEST_F(Dhcpv4SrvTest, DiscoverNoTimers) {
+// Check that option 58 and 59 are only included if they were specified
+// and T2 is less than valid lft; T1 is less than T2 (if given) or valid
+// lft if T2 is not given.
+TEST_F(Dhcpv4SrvTest, DiscoverTimers) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
- Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
- dis->setRemoteAddr(IOAddress("192.0.2.1"));
- OptionPtr clientid = generateClientId();
- dis->addOption(clientid);
- dis->setIface("eth1");
-
- // Recreate a subnet but set T1 and T2 to "unspecified".
+ // Recreate subnet
+ Triplet<uint32_t> unspecified;
+ Triplet<uint32_t> valid_lft(1000);
subnet_.reset(new Subnet4(IOAddress("192.0.2.0"), 24,
- Triplet<uint32_t>(),
- Triplet<uint32_t>(),
- 3000));
+ unspecified,
+ unspecified,
+ valid_lft));
+
pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
IOAddress("192.0.2.110")));
subnet_->addPool(pool_);
CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
CfgMgr::instance().commit();
- // Pass it to the server and get an offer
- Pkt4Ptr offer = srv->processDiscover(dis);
+ // 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_;
+ // True if Offer should contain Subnet's T1 value
+ bool exp_t1_;
+ // True if Offer should contain Subnet's T2 value
+ bool exp_t2_;
+ };
- // Check if we get response at all
- checkResponse(offer, DHCPOFFER, 1234);
+ // Convenience constants
+ bool T1 = true;
+ bool T2 = true;
+
+ // Test scenarios
+ std::vector<TimerTest> tests = {
+ {
+ "T1:unspecified, T2:unspecified",
+ unspecified, unspecified,
+ // Client should neither.
+ !T1, !T2
+ },
+ {
+ "T1 unspecified, T2 < VALID",
+ unspecified, valid_lft - 1,
+ // Client should only get T2.
+ !T1, T2
+ },
+ {
+ "T1:unspecified, T2 = VALID",
+ unspecified, valid_lft,
+ // Client should get neither.
+ !T1, !T2
+ },
+ {
+ "T1:unspecified, T2 > VALID",
+ unspecified, valid_lft + 1,
+ // Client should get neither.
+ !T1, !T2
+ },
+
+ {
+ "T1 < VALID, T2:unspecified",
+ valid_lft - 1, unspecified,
+ // Client should only get T1.
+ T1, !T2
+ },
+ {
+ "T1 = VALID, T2:unspecified",
+ valid_lft, unspecified,
+ // Client should get neither.
+ !T1, !T2
+ },
+ {
+ "T1 > VALID, T2:unspecified",
+ valid_lft + 1, unspecified,
+ // Client should get neither.
+ !T1, !T2
+ },
+ {
+ "T1 < T2 < VALID",
+ valid_lft - 2, valid_lft - 1,
+ // Client should get both.
+ T1, T2
+ },
+ {
+ "T1 = T2 < VALID",
+ valid_lft - 1, valid_lft - 1,
+ // Client should only get T2.
+ !T1, T2
+ },
+ {
+ "T1 > T2 < VALID",
+ valid_lft - 1, valid_lft - 2,
+ // Client should only get T2.
+ !T1, T2
+ },
+ {
+ "T1 = T2 = VALID",
+ valid_lft, valid_lft,
+ // Client should get neither.
+ !T1, !T2
+ },
+ {
+ "T1 > VALID < T2, T2 > VALID",
+ valid_lft + 1, valid_lft + 2,
+ // Client should get neither.
+ !T1, !T2
+ }
+ };
- // T1 and T2 timers must not be present.
- checkAddressParams(offer, subnet_, false, false);
+ // Create a discover packet to use
+ Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+ dis->setRemoteAddr(IOAddress("192.0.2.1"));
+ OptionPtr clientid = generateClientId();
+ dis->addOption(clientid);
+ dis->setIface("eth1");
+
+ // Iterate over the test scenarios.
+ for (auto test = tests.begin(); test != tests.end(); ++test) {
+ {
+ SCOPED_TRACE((*test).description_);
+ // Configure sunbet's timer values
+ subnet_->setT1((*test).cfg_t1_);
+ subnet_->setT2((*test).cfg_t2_);
+
+ // Discover/Offer exchange with the server
+ Pkt4Ptr offer = srv->processDiscover(dis);
+
+ // Verify we have an offer
+ checkResponse(offer, DHCPOFFER, 1234);
+
+ // Verify the timers are as expected.
+ checkAddressParams(offer, subnet_,
+ (*test).exp_t1_, (*test).exp_t2_);
+ }
+ }
- // Check identifiers
- checkServerId(offer, srv->getServerID());
- checkClientId(offer, clientid);
}
+
// This test verifies that incoming DISCOVER can be handled properly, that an
// OFFER is generated, that the response has an address and that address
// really belongs to the configured pool.
ASSERT_TRUE(offer);
}
-
/// @todo: Implement proper tests for MySQL lease/host database,
/// see ticket #4214.
" \"relay\": {\n"
" \"ip-addresses\": [ ]\n"
" },\n"
-" \"renew-timer\": 900,\n"
" \"reservation-mode\": \"all\",\n"
" \"reservations\": [ ],\n"
" \"server-hostname\": \"\",\n"
" \"pool\": \"192.0.2.1-192.0.2.100\"\n"
" }\n"
" ],\n"
-" \"rebind-timer\": 1800,\n"
" \"relay\": {\n"
" \"ip-addresses\": [ ]\n"
" },\n"
" \"match-client-id\": true,\n"
" \"name\": \"foo\",\n"
" \"option-data\": [ ],\n"
-" \"rebind-timer\": 0,\n"
" \"relay\": {\n"
" \"ip-addresses\": [ ]\n"
" },\n"
-" \"renew-timer\": 0,\n"
" \"reservation-mode\": \"all\",\n"
" \"subnet4\": [\n"
" {\n"
" \"pool\": \"192.0.1.1-192.0.1.10\"\n"
" }\n"
" ],\n"
-" \"rebind-timer\": 1800,\n"
" \"relay\": {\n"
" \"ip-addresses\": [ ]\n"
" },\n"
-" \"renew-timer\": 900,\n"
" \"reservation-mode\": \"all\",\n"
" \"reservations\": [\n"
" {\n"
" \"subnet\": \"192.0.1.0/24\",\n"
" \"valid-lifetime\": 7200\n"
" }\n"
-" ],\n"
-" \"valid-lifetime\": 0\n"
+" ]\n"
" }\n"
" ],\n"
" \"subnet4\": [ ]\n"
-// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2018 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
EXPECT_NO_THROW(num = SimpleParser4::setAllDefaults(empty));
- // We expect at least 3 parameters to be inserted.
- EXPECT_TRUE(num >= 3);
+ // We expect at least 1 parameter to be inserted.
+ EXPECT_TRUE(num >= 1);
checkIntegerValue(empty, "valid-lifetime", 7200);
- checkIntegerValue(empty, "rebind-timer", 1800);
- checkIntegerValue(empty, "renew-timer", 900);
+
+ // Timers are optional and by default are not present
+ EXPECT_FALSE(empty->contains("rebind-timer"));
+ EXPECT_FALSE(empty->contains("renew-timer"));
// Make sure that preferred-lifetime is not set for v4 (it's v6 only
// parameter)
" \"option-data\": [ ],\n"
" \"preferred-lifetime\": 0,\n"
" \"rapid-commit\": false,\n"
-" \"rebind-timer\": 0,\n"
" \"relay\": {\n"
" \"ip-addresses\": [ ]\n"
" },\n"
-" \"renew-timer\": 0,\n"
" \"reservation-mode\": \"all\",\n"
" \"subnet6\": [\n"
" {\n"
" \"subnet\": \"2001:db1::/48\",\n"
" \"valid-lifetime\": 7200\n"
" }\n"
-" ],\n"
-" \"valid-lifetime\": 0\n"
+" ]\n"
" }\n"
" ],\n"
" \"subnet6\": [ ]\n"
map->set("require-client-classes", class_list);
}
- // Set renew-timer
- map->set("renew-timer",
- Element::create(static_cast<long long>
- (getT1().get())));
+ // T1, T2, and Valid are optional for SharedNetworks, and
+ // T1 and T2 are optional for Subnet4 thus we will only
+ // output them if they are marked as specified.
+ if (!getT1().unspecified()) {
+ map->set("renew-timer",
+ Element::create(static_cast<long long>(getT1().get())));
+ }
+
// Set rebind-timer
- map->set("rebind-timer",
- Element::create(static_cast<long long>
- (getT2().get())));
+ if (!getT2().unspecified()) {
+ map->set("rebind-timer",
+ Element::create(static_cast<long long>(getT2().get())));
+ }
+
// Set valid-lifetime
- map->set("valid-lifetime",
- Element::create(static_cast<long long>
+ if (!getValid().unspecified()) {
+ map->set("valid-lifetime",
+ Element::create(static_cast<long long>
(getValid().get())));
+ }
// Set reservation mode
Network::HRMode hrmode = getHostReservationMode();
/// @brief Constructor.
Network()
- : iface_name_(), client_class_(""), t1_(0), t2_(0), valid_(0),
+ : iface_name_(), client_class_(""), t1_(), t2_(), valid_(),
host_reservation_mode_(HR_ALL), cfg_option_(new CfgOption()) {
}
asiolink::IOAddress addr, uint8_t len) {
// The renew-timer and rebind-timer are optional. If not set, the
// option 58 and 59 will not be sent to a client. In this case the
- // client will use default values based on the valid-lifetime.
- Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
- Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
+ // client should formulate default values based on the valid-lifetime.
+ Triplet<uint32_t> t1;
+ if (params->contains("renew-timer")) {
+ t1 = getInteger(params, "renew-timer");
+ }
+
+ Triplet<uint32_t> t2;
+ if (params->contains("rebind-timer")) {
+ t2 = getInteger(params, "rebind-timer");
+ }
// The valid-lifetime is mandatory. It may be specified for a
// particular subnet. If not, the global value should be present.
-// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2018 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
/// in Dhcp4) are optional. If not defined, the following values will be
/// used.
const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
- { "renew-timer", Element::integer, "900" },
- { "rebind-timer", Element::integer, "1800" },
{ "valid-lifetime", Element::integer, "7200" },
{ "decline-probation-period", Element::integer, "86400" }, // 24h
{ "dhcp4o6-port", Element::integer, "0" },
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 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
TEST(CfgSharedNetworks4Test, unparse) {
SharedNetwork4Ptr network1(new SharedNetwork4("frog"));
SharedNetwork4Ptr network2(new SharedNetwork4("dog"));
-
network1->setIface("eth0");
network1->addRelayAddress(IOAddress("198.16.1.1"));
network1->addRelayAddress(IOAddress("198.16.1.2"));
network2->setIface("eth1");
+ network2->setT1(Triplet<uint32_t>(100));
+ network2->setT2(Triplet<uint32_t>(200));
+ network2->setValid(Triplet<uint32_t>(300));
CfgSharedNetworks4 cfg;
ASSERT_NO_THROW(cfg.add(network1));
" \"interface\": \"eth1\",\n"
" \"match-client-id\": true,\n"
" \"name\": \"dog\",\n"
+ " \"rebind-timer\": 200,\n"
" \"option-data\": [ ],\n"
- " \"rebind-timer\": 0,\n"
+ " \"renew-timer\": 100,\n"
" \"relay\": { \"ip-addresses\": [ ] },\n"
- " \"renew-timer\": 0,\n"
" \"reservation-mode\": \"all\","
" \"subnet4\": [ ],\n"
- " \"valid-lifetime\": 0\n"
+ " \"valid-lifetime\": 300\n"
" },\n"
" {\n"
" \"interface\": \"eth0\",\n"
" \"match-client-id\": true,\n"
" \"name\": \"frog\",\n"
" \"option-data\": [ ],\n"
- " \"rebind-timer\": 0,\n"
" \"relay\": { \"ip-addresses\": [ \"198.16.1.1\", \"198.16.1.2\" ] },\n"
- " \"renew-timer\": 0,\n"
" \"reservation-mode\": \"all\","
- " \"subnet4\": [ ],\n"
- " \"valid-lifetime\": 0\n"
+ " \"subnet4\": [ ]\n"
" }\n"
"]\n";
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 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
network1->setIface("eth0");
network1->addRelayAddress(IOAddress("2001:db8:1::1"));
network1->addRelayAddress(IOAddress("2001:db8:1::2"));
+
network2->setIface("eth1");
+ network2->setT1(Triplet<uint32_t>(100));
+ network2->setT2(Triplet<uint32_t>(200));
+ network2->setValid(Triplet<uint32_t>(300));
CfgSharedNetworks6 cfg;
ASSERT_NO_THROW(cfg.add(network1));
" \"option-data\": [ ],\n"
" \"preferred-lifetime\": 0,\n"
" \"rapid-commit\": false,\n"
- " \"rebind-timer\": 0,\n"
+ " \"rebind-timer\": 200,\n"
" \"relay\": { \"ip-addresses\": [ ] },\n"
- " \"renew-timer\": 0,\n"
+ " \"renew-timer\": 100,\n"
" \"reservation-mode\": \"all\","
" \"subnet6\": [ ],\n"
- " \"valid-lifetime\": 0\n"
+ " \"valid-lifetime\": 300\n"
" },\n"
" {\n"
" \"interface\": \"eth0\",\n"
" \"option-data\": [ ],\n"
" \"preferred-lifetime\": 0,\n"
" \"rapid-commit\": false,\n"
- " \"rebind-timer\": 0,\n"
" \"relay\": { \"ip-addresses\": [ \"2001:db8:1::1\", \"2001:db8:1::2\" ] },\n"
- " \"renew-timer\": 0,\n"
" \"reservation-mode\": \"all\","
- " \"subnet6\": [ ],\n"
- " \"valid-lifetime\": 0\n"
+ " \"subnet6\": [ ]\n"
" }\n"
"]\n";