/// @param t1 expected renew-timer value
/// @param t2 expected rebind-timer value
/// @param valid expected valid-lifetime value
+ /// @param min_valid expected min-valid-lifetime value
+ /// (0 (default) means same than valid)
+ /// @param max_valid expected max-valid-lifetime value
+ /// (0 (default) means same than valid)
/// @return the subnet that was examined
Subnet4Ptr
checkSubnet(const Subnet4Collection& col, std::string subnet,
- uint32_t t1, uint32_t t2, uint32_t valid) {
+ uint32_t t1, uint32_t t2, uint32_t valid,
+ uint32_t min_valid = 0, uint32_t max_valid = 0) {
const auto& index = col.get<SubnetPrefixIndexTag>();
auto subnet_it = index.find(subnet);
if (subnet_it == index.cend()) {
EXPECT_EQ(t1, s->getT1());
EXPECT_EQ(t2, s->getT2());
EXPECT_EQ(valid, s->getValid());
+ EXPECT_EQ(min_valid ? min_valid : valid, s->getValid().getMin());
+ EXPECT_EQ(max_valid ? max_valid : valid, s->getValid().getMax());
return (s);
}
checkResult(status, 0);
}
+/// Check that valid-lifetime must be between min-valid-lifetime and
+/// max-valid-lifetime when a bound is specified, *AND* a subnet is
+/// specified (boundary check is done when lifetimes are applied).
+TEST_F(Dhcp4ParserTest, outBoundValidLifetime) {
+
+ string too_small = "{ " + genIfaceConfig() + "," +
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+ " \"subnet\": \"192.0.2.0/24\" } ],"
+ "\"valid-lifetime\": 1000, \"min-valid-lifetime\": 1001 }";
+
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = parseDHCP4(too_small));
+
+ ConstElementPtr status;
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+ checkResult(status, 1);
+
+ string too_large = "{ " + genIfaceConfig() + "," +
+ "\"subnet4\": [ { "
+ " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+ " \"subnet\": \"192.0.2.0/24\" } ],"
+ "\"valid-lifetime\": 4001, \"max-valid-lifetime\": 4000 }";
+
+ ASSERT_NO_THROW(json = parseDHCP4(too_large));
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+ checkResult(status, 1);
+}
+
/// Check that the renew-timer doesn't have to be specified, in which case
/// it is marked unspecified in the Subnet.
TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) {
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
- "\"valid-lifetime\": 4000 }";
+ "\"valid-lifetime\": 4000,"
+ "\"min-valid-lifetime\": 3000,"
+ "\"max-valid-lifetime\": 5000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config));
EXPECT_EQ(1000, subnet->getT1());
EXPECT_EQ(2000, subnet->getT2());
EXPECT_EQ(4000, subnet->getValid());
+ EXPECT_EQ(3000, subnet->getValid().getMin());
+ EXPECT_EQ(5000, subnet->getValid().getMax());
// Check that subnet-id is 1
EXPECT_EQ(1, subnet->getID());
" \"renew-timer\": 1, "
" \"rebind-timer\": 2, "
" \"valid-lifetime\": 4,"
+ " \"min-valid-lifetime\": 3,"
+ " \"max-valid-lifetime\": 5,"
" \"subnet\": \"192.0.2.0/24\" } ],"
- "\"valid-lifetime\": 4000 }";
+ "\"valid-lifetime\": 4000,"
+ "\"min-valid-lifetime\": 3000,"
+ "\"max-valid-lifetime\": 5000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config));
EXPECT_EQ(1, subnet->getT1());
EXPECT_EQ(2, subnet->getT2());
EXPECT_EQ(4, subnet->getValid());
+ EXPECT_EQ(3, subnet->getValid().getMin());
+ EXPECT_EQ(5, subnet->getValid().getMax());
}
// This test checks that multiple pools can be defined and handled properly.
TEST_F(Dhcp4ParserTest, sharedNetworks3subnets) {
string config = "{\n"
"\"valid-lifetime\": 4000, \n"
+ "\"min-valid-lifetime\": 3000, \n"
+ "\"max-valid-lifetime\": 5000, \n"
"\"rebind-timer\": 2000, \n"
"\"renew-timer\": 1000, \n"
"\"shared-networks\": [ {\n"
" \"pools\": [ { \"pool\": \"192.0.2.1-192.0.2.10\" } ],\n"
" \"renew-timer\": 2,\n"
" \"rebind-timer\": 22,\n"
- " \"valid-lifetime\": 222\n"
+ " \"valid-lifetime\": 222,\n"
+ " \"min-valid-lifetime\": 111,\n"
+ " \"max-valid-lifetime\": 333\n"
" },\n"
" { \n"
" \"subnet\": \"192.0.3.0/24\",\n"
const Subnet4Collection * subs = net->getAllSubnets();
ASSERT_TRUE(subs);
EXPECT_EQ(3, subs->size());
- checkSubnet(*subs, "192.0.1.0/24", 1000, 2000, 4000);
- checkSubnet(*subs, "192.0.2.0/24", 2, 22, 222);
- checkSubnet(*subs, "192.0.3.0/24", 1000, 2000, 4000);
+ checkSubnet(*subs, "192.0.1.0/24", 1000, 2000, 4000, 3000, 5000);
+ checkSubnet(*subs, "192.0.2.0/24", 2, 22, 222, 111, 333);
+ checkSubnet(*subs, "192.0.3.0/24", 1000, 2000, 4000, 3000, 5000);
// Now make sure the subnet was added to global list of subnets.
CfgSubnets4Ptr subnets4 = CfgMgr::instance().getStagingCfg()->getCfgSubnets4();
subs = subnets4->getAll();
ASSERT_TRUE(subs);
- checkSubnet(*subs, "192.0.1.0/24", 1000, 2000, 4000);
- checkSubnet(*subs, "192.0.2.0/24", 2, 22, 222);
- checkSubnet(*subs, "192.0.3.0/24", 1000, 2000, 4000);
+ checkSubnet(*subs, "192.0.1.0/24", 1000, 2000, 4000, 3000, 5000);
+ checkSubnet(*subs, "192.0.2.0/24", 2, 22, 222, 111, 333);
+ checkSubnet(*subs, "192.0.3.0/24", 1000, 2000, 4000, 3000, 5000);
}
// This test checks if parameters are derived properly:
"\"renew-timer\": 1, \n" // global values here
"\"rebind-timer\": 2, \n"
"\"valid-lifetime\": 4, \n"
+ "\"min-valid-lifetime\": 3, \n"
+ "\"max-valid-lifetime\": 5, \n"
"\"shared-networks\": [ {\n"
" \"name\": \"foo\"\n," // shared network values here
" \"interface\": \"eth0\",\n"
" \"renew-timer\": 10,\n"
" \"rebind-timer\": 20,\n"
" \"valid-lifetime\": 40,\n"
+ " \"min-valid-lifetime\": 30,\n"
+ " \"max-valid-lifetime\": 50,\n"
" \"subnet4\": [\n"
" { \n"
" \"subnet\": \"192.0.1.0/24\",\n"
" \"renew-timer\": 100,\n"
" \"rebind-timer\": 200,\n"
" \"valid-lifetime\": 400,\n"
+ " \"min-valid-lifetime\": 300,\n"
+ " \"max-valid-lifetime\": 500,\n"
" \"match-client-id\": true,\n"
" \"next-server\": \"11.22.33.44\",\n"
" \"server-hostname\": \"some-name.example.org\",\n"
// derived from shared-network level. Other parameters a derived
// from global scope to shared-network level and later again to
// subnet4 level.
- Subnet4Ptr s = checkSubnet(*subs, "192.0.1.0/24", 10, 20, 40);
+ Subnet4Ptr s = checkSubnet(*subs, "192.0.1.0/24", 10, 20, 40, 30, 50);
ASSERT_TRUE(s);
// These are values derived from shared network scope:
// was specified explicitly. Other parameters a derived
// from global scope to shared-network level and later again to
// subnet4 level.
- s = checkSubnet(*subs, "192.0.2.0/24", 100, 200, 400);
+ s = checkSubnet(*subs, "192.0.2.0/24", 100, 200, 400, 300, 500);
// These are values derived from shared network scope:
EXPECT_EQ("eth0", s->getIface().get());
// This subnet should derive its renew-timer from global scope.
// All other parameters should have default values.
- s = checkSubnet(*subs, "192.0.3.0/24", 1, 2, 4);
+ s = checkSubnet(*subs, "192.0.3.0/24", 1, 2, 4, 3, 5);
EXPECT_EQ("", s->getIface().get());
EXPECT_TRUE(s->getMatchClientId());
EXPECT_FALSE(s->getAuthoritative());
checkClientId(offer, clientid);
}
+// This test verifies that OFFERs return expected valid lifetimes.
+TEST_F(Dhcpv4SrvTest, DiscoverValidLifetime) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ boost::scoped_ptr<NakedDhcpv4Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+
+ // Recreate subnet
+ Triplet<uint32_t> unspecified;
+ Triplet<uint32_t> valid_lft(500, 1000, 1500);
+ subnet_.reset(new Subnet4(IOAddress("192.0.2.0"), 24,
+ unspecified,
+ unspecified,
+ valid_lft));
+
+ pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
+ IOAddress("192.0.2.110")));
+ subnet_->addPool(pool_);
+ CfgMgr::instance().clear();
+ CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
+ CfgMgr::instance().commit();
+
+ // Struct for describing an individual lifetime test scenario
+ struct LifetimeTest {
+ // logged test description
+ std::string description_;
+ // lifetime hint (0 means not send dhcp-lease-time option)
+ uint32_t hint;
+ // expected returned value
+ uint32_t expected;
+ };
+
+ // Test scenarios
+ std::vector<LifetimeTest> tests = {
+ { "default valid lifetime", 0, 1000 },
+ { "specified valid lifetime", 1001, 1001 },
+ { "too small valid lifetime", 100, 500 },
+ { "too large valid lifetime", 2000, 1500 }
+ };
+
+ // Iterate over the test scenarios.
+ for (auto test : tests) {
+ SCOPED_TRACE(test.description_);
+
+ // 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");
+
+ // Add dhcp-lease-time option.
+ if (test.hint) {
+ OptionUint32Ptr opt(new OptionUint32(Option::V4,
+ DHO_DHCP_LEASE_TIME,
+ test.hint));
+ dis->addOption(opt);
+ }
+
+ // 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 that address was returned from proper range, that its lease
+ // lifetime is correct and has the expected value.
+ checkAddressParams(offer, subnet_, false, false, test.expected);
+
+ // Check identifiers
+ checkServerId(offer, srv->getServerID());
+ checkClientId(offer, clientid);
+ }
+}
+
// Check that option 58 and 59 are only included if they were specified
// (and calculate-tee-times = false) and the values are sane:
// T2 is less than valid lft; T1 is less than T2 (if given) or valid
EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
}
+// This test verifies that renewal returns the default valid lifetime
+// when the client does not specify a value.
+TEST_F(Dhcpv4SrvTest, RenewDefaultLifetime) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ boost::scoped_ptr<NakedDhcpv4Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+
+ const IOAddress addr("192.0.2.106");
+ const uint32_t temp_t1 = 50;
+ const uint32_t temp_t2 = 75;
+ const uint32_t temp_valid = 100;
+ const time_t temp_timestamp = time(NULL) - 10;
+
+ // 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_t1, temp_t2, 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 T1, T2, preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 10 seconds ago
+ // EXPECT_EQ(l->t1_, temp_t1);
+ // EXPECT_EQ(l->t2_, temp_t2);
+ EXPECT_EQ(l->valid_lft_, temp_valid);
+ EXPECT_EQ(l->cltt_, temp_timestamp);
+
+ // Set the valid lifetime interval.
+ subnet_->setValid(Triplet<uint32_t>(2000, 3000, 4000));
+
+ // 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->setHWAddr(hwaddr2);
+
+ req->addOption(clientid);
+ req->addOption(srv->getServerID());
+
+ // There is no valid lifetime hint so the default will be returned.
+
+ // 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);
+ EXPECT_EQ(addr, ack->getYiaddr());
+
+ // Check that address was returned from proper range, that its lease
+ // lifetime is correct, that T1 and T2 are returned properly
+ checkAddressParams(ack, subnet_, true, true, subnet_->getValid());
+
+ // Check identifiers
+ checkServerId(ack, srv->getServerID());
+ checkClientId(ack, clientid);
+
+ // Check that the lease is really in the database
+ l = checkLease(ack, clientid, req->getHWAddr(), addr);
+ ASSERT_TRUE(l);
+
+ // Check that T1, T2, preferred, valid and cltt were really updated
+ EXPECT_EQ(l->t1_, subnet_->getT1());
+ EXPECT_EQ(l->t2_, subnet_->getT2());
+ EXPECT_EQ(l->valid_lft_, subnet_->getValid());
+
+ // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+ int32_t cltt = static_cast<int32_t>(l->cltt_);
+ int32_t expected = static_cast<int32_t>(time(NULL));
+ // Equality or difference by 1 between cltt and expected is ok.
+ EXPECT_GE(1, abs(cltt - expected));
+
+ EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+// This test verifies that renewal returns the specified valid lifetime
+// when the client adds an in-bound hint in the DISCOVER.
+TEST_F(Dhcpv4SrvTest, RenewHintLifetime) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ boost::scoped_ptr<NakedDhcpv4Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+
+ const IOAddress addr("192.0.2.106");
+ const uint32_t temp_t1 = 50;
+ const uint32_t temp_t2 = 75;
+ const uint32_t temp_valid = 100;
+ const time_t temp_timestamp = time(NULL) - 10;
+
+ // 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_t1, temp_t2, 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 T1, T2, preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 10 seconds ago
+ // EXPECT_EQ(l->t1_, temp_t1);
+ // EXPECT_EQ(l->t2_, temp_t2);
+ EXPECT_EQ(l->valid_lft_, temp_valid);
+ EXPECT_EQ(l->cltt_, temp_timestamp);
+
+ // Set the valid lifetime interval.
+ subnet_->setValid(Triplet<uint32_t>(2000, 3000, 4000));
+
+ // 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->setHWAddr(hwaddr2);
+
+ req->addOption(clientid);
+ req->addOption(srv->getServerID());
+
+ // Add a dhcp-lease-time with an in-bound valid lifetime hint
+ // which will be returned in the OFFER.
+ uint32_t hint = 3001;
+ OptionPtr opt(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME, hint));
+ req->addOption(opt);
+
+ // 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);
+ EXPECT_EQ(addr, ack->getYiaddr());
+
+ // Check that address was returned from proper range, that its lease
+ // lifetime is correct, that T1 and T2 are returned properly
+ checkAddressParams(ack, subnet_, true, true, hint);
+
+ // Check identifiers
+ checkServerId(ack, srv->getServerID());
+ checkClientId(ack, clientid);
+
+ // Check that the lease is really in the database
+ l = checkLease(ack, clientid, req->getHWAddr(), addr);
+ ASSERT_TRUE(l);
+
+ // Check that T1, T2, preferred, valid and cltt were really updated
+ EXPECT_EQ(l->t1_, subnet_->getT1());
+ EXPECT_EQ(l->t2_, subnet_->getT2());
+ EXPECT_EQ(l->valid_lft_, hint);
+
+ // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+ int32_t cltt = static_cast<int32_t>(l->cltt_);
+ int32_t expected = static_cast<int32_t>(time(NULL));
+ // Equality or difference by 1 between cltt and expected is ok.
+ EXPECT_GE(1, abs(cltt - expected));
+
+ EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+// This test verifies that renewal returns the min valid lifetime
+// when the client adds a too small hint in the DISCOVER.
+TEST_F(Dhcpv4SrvTest, RenewMinLifetime) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ boost::scoped_ptr<NakedDhcpv4Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+
+ const IOAddress addr("192.0.2.106");
+ const uint32_t temp_t1 = 50;
+ const uint32_t temp_t2 = 75;
+ const uint32_t temp_valid = 100;
+ const time_t temp_timestamp = time(NULL) - 10;
+
+ // 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_t1, temp_t2, 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 T1, T2, preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 10 seconds ago
+ // EXPECT_EQ(l->t1_, temp_t1);
+ // EXPECT_EQ(l->t2_, temp_t2);
+ EXPECT_EQ(l->valid_lft_, temp_valid);
+ EXPECT_EQ(l->cltt_, temp_timestamp);
+
+ // Set the valid lifetime interval.
+ subnet_->setValid(Triplet<uint32_t>(2000, 3000, 4000));
+
+ // 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->setHWAddr(hwaddr2);
+
+ req->addOption(clientid);
+ req->addOption(srv->getServerID());
+
+ // Add a dhcp-lease-time with too small valid lifetime hint.
+ // The min valid lifetime will be returned in the OFFER.
+ OptionPtr opt(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME, 1000));
+ req->addOption(opt);
+
+ // 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);
+ EXPECT_EQ(addr, ack->getYiaddr());
+
+ // Check that address was returned from proper range, that its lease
+ // lifetime is correct, that T1 and T2 are returned properly
+ // Note that T2 should be false for a reason which does not matter...
+ checkAddressParams(ack, subnet_, true, false, subnet_->getValid().getMin());
+
+ // Check identifiers
+ checkServerId(ack, srv->getServerID());
+ checkClientId(ack, clientid);
+
+ // Check that the lease is really in the database
+ l = checkLease(ack, clientid, req->getHWAddr(), addr);
+ ASSERT_TRUE(l);
+
+ // Check that T1, T2, preferred, valid and cltt were really updated
+ EXPECT_EQ(l->t1_, subnet_->getT1());
+ EXPECT_EQ(l->t2_, subnet_->getT2());
+ EXPECT_EQ(l->valid_lft_, subnet_->getValid().getMin());
+
+ // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+ int32_t cltt = static_cast<int32_t>(l->cltt_);
+ int32_t expected = static_cast<int32_t>(time(NULL));
+ // Equality or difference by 1 between cltt and expected is ok.
+ EXPECT_GE(1, abs(cltt - expected));
+
+ EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
+// This test verifies that renewal returns the max valid lifetime
+// when the client adds a too large hint in the DISCOVER.
+TEST_F(Dhcpv4SrvTest, RenewMaxLifetime) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ boost::scoped_ptr<NakedDhcpv4Srv> srv;
+ ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
+
+ const IOAddress addr("192.0.2.106");
+ const uint32_t temp_t1 = 50;
+ const uint32_t temp_t2 = 75;
+ const uint32_t temp_valid = 100;
+ const time_t temp_timestamp = time(NULL) - 10;
+
+ // 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_t1, temp_t2, 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 T1, T2, preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 10 seconds ago
+ // EXPECT_EQ(l->t1_, temp_t1);
+ // EXPECT_EQ(l->t2_, temp_t2);
+ EXPECT_EQ(l->valid_lft_, temp_valid);
+ EXPECT_EQ(l->cltt_, temp_timestamp);
+
+ // Set the valid lifetime interval.
+ subnet_->setValid(Triplet<uint32_t>(2000, 3000, 4000));
+
+ // 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->setHWAddr(hwaddr2);
+
+ req->addOption(clientid);
+ req->addOption(srv->getServerID());
+
+ // Add a dhcp-lease-time with too large valid lifetime hint.
+ // The max valid lifetime will be returned in the OFFER.
+ OptionPtr opt(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME, 5000));
+ req->addOption(opt);
+
+ // 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);
+ EXPECT_EQ(addr, ack->getYiaddr());
+
+ // Check that address was returned from proper range, that its lease
+ // lifetime is correct, that T1 and T2 are returned properly
+ checkAddressParams(ack, subnet_, true, true, subnet_->getValid().getMax());
+
+ // Check identifiers
+ checkServerId(ack, srv->getServerID());
+ checkClientId(ack, clientid);
+
+ // Check that the lease is really in the database
+ l = checkLease(ack, clientid, req->getHWAddr(), addr);
+ ASSERT_TRUE(l);
+
+ // Check that T1, T2, preferred, valid and cltt were really updated
+ EXPECT_EQ(l->t1_, subnet_->getT1());
+ EXPECT_EQ(l->t2_, subnet_->getT2());
+ EXPECT_EQ(l->valid_lft_, subnet_->getValid().getMax());
+
+ // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
+ int32_t cltt = static_cast<int32_t>(l->cltt_);
+ int32_t expected = static_cast<int32_t>(time(NULL));
+ // Equality or difference by 1 between cltt and expected is ok.
+ EXPECT_GE(1, abs(cltt - expected));
+
+ EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
// 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,