From: Francis Dupont Date: Mon, 20 May 2019 12:30:37 +0000 (+0200) Subject: [295-min-max-lease-time-configuration-options] Added DHCPv4 server tests X-Git-Tag: Kea-1.6.0-beta2~245 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=102bf07484f952fb2b8cdd3f1b547b71f923120c;p=thirdparty%2Fkea.git [295-min-max-lease-time-configuration-options] Added DHCPv4 server tests --- diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 885c145cc8..9f40899945 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -546,6 +546,8 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, if ( (config_pair.first == "renew-timer") || (config_pair.first == "rebind-timer") || (config_pair.first == "valid-lifetime") || + (config_pair.first == "min-valid-lifetime") || + (config_pair.first == "max-valid-lifetime") || (config_pair.first == "decline-probation-period") || (config_pair.first == "dhcp4o6-port") || (config_pair.first == "echo-client-id") || diff --git a/src/bin/dhcp4/tests/config_backend_unittest.cc b/src/bin/dhcp4/tests/config_backend_unittest.cc index 1dc87ad71b..0a125a89e0 100644 --- a/src/bin/dhcp4/tests/config_backend_unittest.cc +++ b/src/bin/dhcp4/tests/config_backend_unittest.cc @@ -315,7 +315,7 @@ TEST_F(Dhcp4CBTest, mergeOptions) { " \"name\": \"dhcp-message\", \n" " \"data\": \"0A0B0C0D\", \n" " \"csv-format\": false \n" - " },{ \n" + " },{ \n" " \"name\": \"host-name\", \n" " \"data\": \"old.example.com\", \n" " \"csv-format\": true \n" @@ -372,7 +372,7 @@ TEST_F(Dhcp4CBTest, mergeOptions) { ASSERT_TRUE(found_opt.option_); EXPECT_EQ("0x0A0B0C0D", found_opt.option_->toHexString()); - // host-name should come from the first back end, + // host-name should come from the first back end, // (overwriting the original). found_opt = options->get("dhcp4", DHO_HOST_NAME); ASSERT_TRUE(found_opt.option_); diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 2ee1bca774..319408b1db 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -726,10 +726,15 @@ public: /// @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(); auto subnet_it = index.find(subnet); if (subnet_it == index.cend()) { @@ -741,6 +746,8 @@ public: 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); } @@ -835,6 +842,35 @@ TEST_F(Dhcp4ParserTest, emptySubnet) { 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) { @@ -912,7 +948,9 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) { "\"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)); @@ -932,6 +970,8 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) { 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()); @@ -1645,8 +1685,12 @@ TEST_F(Dhcp4ParserTest, subnetLocal) { " \"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)); @@ -1664,6 +1708,8 @@ TEST_F(Dhcp4ParserTest, subnetLocal) { 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. @@ -5910,6 +5956,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworks1subnet) { 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" @@ -5924,7 +5972,9 @@ TEST_F(Dhcp4ParserTest, sharedNetworks3subnets) { " \"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" @@ -5954,9 +6004,9 @@ TEST_F(Dhcp4ParserTest, sharedNetworks3subnets) { 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(); @@ -5964,9 +6014,9 @@ TEST_F(Dhcp4ParserTest, sharedNetworks3subnets) { 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: @@ -5990,6 +6040,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { "\"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" @@ -6005,6 +6057,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { " \"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" @@ -6016,6 +6070,8 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { " \"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" @@ -6063,7 +6119,7 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { // 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: @@ -6080,7 +6136,7 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { // 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()); @@ -6102,7 +6158,7 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { // 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()); diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index e838a2607c..699b1a86f7 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -764,6 +764,82 @@ TEST_F(Dhcpv4SrvTest, DiscoverBasic) { 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 srv; + ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0))); + + // Recreate subnet + Triplet unspecified; + Triplet 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 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 @@ -1370,6 +1446,377 @@ TEST_F(Dhcpv4SrvTest, RenewBasic) { 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 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(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(l->cltt_); + int32_t expected = static_cast(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 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(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(l->cltt_); + int32_t expected = static_cast(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 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(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(l->cltt_); + int32_t expected = static_cast(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 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(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(l->cltt_); + int32_t expected = static_cast(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, diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index c223984eab..08f335e0cf 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -273,7 +273,8 @@ HWAddrPtr Dhcpv4SrvTest::generateHWAddr(size_t size /*= 6*/) { void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, const Subnet4Ptr subnet, bool t1_present, - bool t2_present) { + bool t2_present, + uint32_t expected_valid) { // Technically inPool implies inRange, but let's be on the safe // side and check both. @@ -286,10 +287,18 @@ void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, if (!opt) { ADD_FAILURE() << "Lease time option missing in response or the" " option has unexpected type"; + } else if (subnet->getValid().getMin() != subnet->getValid().getMax()) { + EXPECT_GE(opt->getValue(), subnet->getValid().getMin()); + EXPECT_LE(opt->getValue(), subnet->getValid().getMax()); } else { EXPECT_EQ(opt->getValue(), subnet->getValid()); } + // Check expected value when wanted. + if (opt && expected_valid) { + EXPECT_EQ(opt->getValue(), expected_valid); + } + // Check T1 timer opt = boost::dynamic_pointer_cast< OptionUint32>(rsp->getOption(DHO_DHCP_RENEWAL_TIME)); diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index 2bc5cf5d70..433db9f7b9 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -354,9 +354,12 @@ public: /// present (false) /// @param t2_present check that t2 must be present (true) or must not be /// present (false) + /// @param expected_valid check that lease lifetime has the not-zero + /// expected value (zero value means that do not check). void checkAddressParams(const Pkt4Ptr& rsp, const Subnet4Ptr subnet, bool t1_present = false, - bool t2_present = false); + bool t2_present = false, + uint32_t expected_valid = 0); /// @brief Basic checks for generated response (message type and trans-id). /// diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index 8cc5079252..409583a8f1 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -123,6 +123,8 @@ const char* EXTRACTED_CONFIGS[] = { " \"interfaces\": [ \"*\" ],\n" " \"re-detect\": false\n" " },\n" +" \"max-valid-lifetime\": 5000,\n" +" \"min-valid-lifetime\": 3000,\n" " \"rebind-timer\": 2000,\n" " \"renew-timer\": 1000,\n" " \"subnet4\": [\n" @@ -470,10 +472,14 @@ const char* EXTRACTED_CONFIGS[] = { " \"interfaces\": [ \"*\" ],\n" " \"re-detect\": false\n" " },\n" +" \"max-valid-lifetime\": 5000,\n" +" \"min-valid-lifetime\": 3000,\n" " \"rebind-timer\": 2000,\n" " \"renew-timer\": 1000,\n" " \"subnet4\": [\n" " {\n" +" \"max-valid-lifetime\": 5,\n" +" \"min-valid-lifetime\": 3,\n" " \"pools\": [\n" " {\n" " \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" @@ -2330,6 +2336,8 @@ const char* UNPARSED_CONFIGS[] = { " \"type\": \"memfile\"\n" " },\n" " \"match-client-id\": true,\n" +" \"max-valid-lifetime\": 5000,\n" +" \"min-valid-lifetime\": 3000,\n" " \"next-server\": \"0.0.0.0\",\n" " \"option-data\": [ ],\n" " \"option-def\": [ ],\n" @@ -2350,6 +2358,8 @@ const char* UNPARSED_CONFIGS[] = { " \"calculate-tee-times\": false,\n" " \"id\": 1,\n" " \"match-client-id\": true,\n" +" \"max-valid-lifetime\": 5000,\n" +" \"min-valid-lifetime\": 3000,\n" " \"next-server\": \"0.0.0.0\",\n" " \"option-data\": [ ],\n" " \"pools\": [\n" @@ -3810,6 +3820,8 @@ const char* UNPARSED_CONFIGS[] = { " \"type\": \"memfile\"\n" " },\n" " \"match-client-id\": true,\n" +" \"max-valid-lifetime\": 5000,\n" +" \"min-valid-lifetime\": 3000,\n" " \"next-server\": \"0.0.0.0\",\n" " \"option-data\": [ ],\n" " \"option-def\": [ ],\n" @@ -3831,6 +3843,8 @@ const char* UNPARSED_CONFIGS[] = { " \"calculate-tee-times\": false,\n" " \"id\": 1,\n" " \"match-client-id\": true,\n" +" \"max-valid-lifetime\": 5,\n" +" \"min-valid-lifetime\": 3,\n" " \"next-server\": \"0.0.0.0\",\n" " \"option-data\": [ ],\n" " \"pools\": [\n" diff --git a/src/bin/dhcp4/tests/simple_parser4_unittest.cc b/src/bin/dhcp4/tests/simple_parser4_unittest.cc index 395b6f37a2..6ac157a99c 100644 --- a/src/bin/dhcp4/tests/simple_parser4_unittest.cc +++ b/src/bin/dhcp4/tests/simple_parser4_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -115,6 +115,8 @@ TEST_F(SimpleParser4Test, inheritGlobalToSubnet4) { ElementPtr global = parseJSON("{ \"renew-timer\": 1," " \"rebind-timer\": 2," " \"valid-lifetime\": 4," + " \"min-valid-lifetime\": 3," + " \"max-valid-lifetime\": 5," " \"subnet4\": [ { \"renew-timer\": 100 } ] " "}"); ConstElementPtr subnets = global->find("subnet4"); @@ -122,17 +124,19 @@ TEST_F(SimpleParser4Test, inheritGlobalToSubnet4) { ConstElementPtr subnet = subnets->get(0); ASSERT_TRUE(subnet); - // we should inherit 3 parameters. Renew-timer should remain intact, + // we should inherit 4 parameters. Renew-timer should remain intact, // as it was already defined in the subnet scope. size_t num; EXPECT_NO_THROW(num = SimpleParser4::deriveParameters(global)); - EXPECT_EQ(2, num); + EXPECT_EQ(4, num); // Check the values. 2 of them are inherited, while the third one // was already defined in the subnet, so should not be inherited. checkIntegerValue(subnet, "renew-timer", 100); checkIntegerValue(subnet, "rebind-timer", 2); checkIntegerValue(subnet, "valid-lifetime", 4); + checkIntegerValue(subnet, "min-valid-lifetime", 3); + checkIntegerValue(subnet, "max-valid-lifetime", 5); } // This test checks if the parameters in "subnet4" are assigned default values diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index ff6cce7e13..6434f45ebc 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -668,7 +668,11 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, if ( (config_pair.first == "renew-timer") || (config_pair.first == "rebind-timer") || (config_pair.first == "preferred-lifetime") || + (config_pair.first == "min-preferred-lifetime") || + (config_pair.first == "max-preferred-lifetime") || (config_pair.first == "valid-lifetime") || + (config_pair.first == "min-valid-lifetime") || + (config_pair.first == "max-valid-lifetime") || (config_pair.first == "decline-probation-period") || (config_pair.first == "dhcp4o6-port") || (config_pair.first == "server-tag") ||