From: Francis Dupont Date: Thu, 12 Sep 2019 14:17:42 +0000 (+0200) Subject: [897-add-infinite-valid-lifetime] Finished (added server tests) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2a1b733e21ac67bf80eff7a4997eefcaf5061b7f;p=thirdparty%2Fkea.git [897-add-infinite-valid-lifetime] Finished (added server tests) --- diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 07e6fe6d9d..27928f5c14 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -1002,7 +1002,8 @@ TEST_F(Dhcpv4SrvTest, DiscoverValidLifetime) { { "default valid lifetime", 0, 1000 }, { "specified valid lifetime", 1001, 1001 }, { "too small valid lifetime", 100, 500 }, - { "too large valid lifetime", 2000, 1500 } + { "too large valid lifetime", 2000, 1500 }, + { "infinite valid lifetime", 0xffffffff, 1500 } }; // Iterate over the test scenarios. @@ -1328,6 +1329,77 @@ TEST_F(Dhcpv4SrvTest, calculateTeeTimers) { } } +// This test verifies that OFFERs handle static leases. +// (specialized version of previous test with some T1/T2 additions) +TEST_F(Dhcpv4SrvTest, DiscoverStaticLease) { + 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_); + // Allow static leases + subnet_->setAllowStaticLeases(true); + // Set inheritance + subnet_->setFetchGlobalsFn([] () -> ConstElementPtr { + return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals()); + }); + // Set T1/T2 calculation (ignored for static leases) + subnet_->setCalculateTeeTimes(true); + subnet_->setT1Percent(.5); + subnet_->setT2Percent(1.); + CfgMgr::instance().clear(); + CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_); + + // Set bigger T1 and T2 defaults. + uint32_t t1 = 10*24*60*60; + ConstElementPtr t1_elem = Element::create(static_cast(t1)); + CfgMgr::instance().getStagingCfg()->addConfiguredGlobal("renew-timer", t1_elem); + uint32_t t2 = 30*24*60*60; + ConstElementPtr t2_elem = Element::create(static_cast(t2)); + CfgMgr::instance().getStagingCfg()->addConfiguredGlobal("rebind-timer", t2_elem); + + CfgMgr::instance().commit(); + + // 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. + uint32_t infinity_lft = Lease::INFINITY_LFT; + OptionUint32Ptr opt(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME, + infinity_lft)); + 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 infinite. + checkAddressParams(offer, subnet_, true, true, infinity_lft); + + // 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 @@ -1903,6 +1975,128 @@ TEST_F(Dhcpv4SrvTest, RenewMaxLifetime) { } // end of Renew*Lifetime +// This test verifies that renewal handles static leases +TEST_F(Dhcpv4SrvTest, RenewStaticLease) { + 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_); + // Allow static leases + subnet_->setAllowStaticLeases(true); + // Set inheritance + subnet_->setFetchGlobalsFn([] () -> ConstElementPtr { + return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals()); + }); + // Set T1/T2 calculation (ignored for static leases) + subnet_->setCalculateTeeTimes(true); + subnet_->setT1Percent(.5); + subnet_->setT2Percent(1.); + CfgMgr::instance().clear(); + CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_); + + // Set bigger T1 and T2 defaults. + uint32_t t1 = 10*24*60*60; + ConstElementPtr t1_elem = Element::create(static_cast(t1)); + CfgMgr::instance().getStagingCfg()->addConfiguredGlobal("renew-timer", t1_elem); + uint32_t t2 = 30*24*60*60; + ConstElementPtr t2_elem = Element::create(static_cast(t2)); + CfgMgr::instance().getStagingCfg()->addConfiguredGlobal("rebind-timer", t2_elem); + + CfgMgr::instance().commit(); + + const IOAddress& addr = IOAddress("192.0.2.106"); + ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr)); + + // let's create a lease and put it in the LeaseMgr + uint8_t hwaddr_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe}; + HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER)); + // Generate client-id also sets client_id_ member + OptionPtr clientid = generateClientId(); + + uint32_t infinity_lft = Lease::INFINITY_LFT; + time_t timestamp = time(NULL) - 10; + Lease4Ptr used(new Lease4(addr, hwaddr, + &client_id_->getDuid()[0], + client_id_->getDuid().size(), + infinity_lft, 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 valid and cltt really set. + // Constructed lease looks as if it was assigned 10 seconds ago + EXPECT_EQ(infinity_lft, l->valid_lft_); + EXPECT_EQ(timestamp, l->cltt_); + + // Set the valid lifetime interval. + subnet_->setValid(Triplet(2000, 3000, 4000)); + + // Allow static leases + subnet_->setAllowStaticLeases(true); + + // Let's create a RENEW + Pkt4Ptr req(new Pkt4(DHCPREQUEST, 1234)); + req->setRemoteAddr(IOAddress(addr)); + req->setYiaddr(addr); + req->setCiaddr(addr); // client's address + req->setIface("eth0"); + req->setHWAddr(hwaddr); + + req->addOption(clientid); + req->addOption(srv->getServerID()); + + // Add a dhcp-lease-time with infinite valid lifetime hint. + OptionPtr opt(new OptionUint32(Option::V4, DHO_DHCP_LEASE_TIME, + infinity_lft)); + 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 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 address was returned from proper range, that its lease + // lifetime is correct, that T1 and T2 are returned properly + checkAddressParams(ack, subnet_, true, true, infinity_lft); + + // Check that valid and cltt were really updated + EXPECT_EQ(infinity_lft, l->valid_lft_); + + // 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 08f335e0cf..ba11f335c7 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -287,6 +287,9 @@ void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, if (!opt) { ADD_FAILURE() << "Lease time option missing in response or the" " option has unexpected type"; + } else if ((expected_valid == Lease::INFINITY_LFT) && + subnet->getAllowStaticLeases()) { + EXPECT_EQ(opt->getValue(), expected_valid); } else if (subnet->getValid().getMin() != subnet->getValid().getMax()) { EXPECT_GE(opt->getValue(), subnet->getValid().getMin()); EXPECT_LE(opt->getValue(), subnet->getValid().getMax()); @@ -296,7 +299,7 @@ void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp, // Check expected value when wanted. if (opt && expected_valid) { - EXPECT_EQ(opt->getValue(), expected_valid); + EXPECT_EQ(opt->getValue(), expected_valid); } // Check T1 timer diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index e8062b43ac..b02bcbffc0 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -780,6 +780,105 @@ TEST_F(Dhcpv6SrvTest, maxLifetimeSolicit) { checkClientId(reply, clientid); } +// This test verifies that ADVERTISE handle static leases. +// the client adds an IAPREFIX sub option with infinite valid lifetime hint. +TEST_F(Dhcpv6SrvTest, staticLeaseSolicit) { + NakedDhcpv6Srv srv(0); + + subnet_->setPreferred(Triplet(2000, 3000, 4000)); + subnet_->setValid(Triplet(3000, 4000, 5000)); + // Allow static leases + subnet_->setAllowStaticLeases(true); + + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->setIface("eth0"); + OptionPtr iapd = generateIA(D6O_IA_PD, 234, 1500, 3000); + sol->addOption(iapd); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Add an IAPREFIX sub option with infinite valid lifetime. + uint32_t infinity_lft = Lease::INFINITY_LFT; + OptionPtr subopt(new Option6IAPrefix(D6O_IAPREFIX, IOAddress("::"), 0, + 4000, infinity_lft)); + iapd->addOption(subopt); + + // Pass it to the server and get an advertise + 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_PD was returned and that there's an address included + boost::shared_ptr prefix = checkIA_PD(reply, 234, + subnet_->getT1(), + subnet_->getT2()); + ASSERT_TRUE(prefix); + + // Check that the assigned prefix is indeed from the configured pool + checkIAAddr(prefix, prefix->getAddress(), Lease::TYPE_PD, + subnet_->getPreferred().getMax(), + infinity_lft); + + // check DUIDs + checkServerId(reply, srv.getServerID()); + checkClientId(reply, clientid); +} + +// This test verifies that ADVERTISE handle static leases. +// the client adds an IAADDR sub option with infinite lifetime hints. +TEST_F(Dhcpv6SrvTest, staticLeaseSolicit2) { + NakedDhcpv6Srv srv(0); + + subnet_->setPreferred(Triplet(2000, 3000, 4000)); + subnet_->setValid(Triplet(3000, 4000, 5000)); + // Allow static leases + subnet_->setAllowStaticLeases(true); + + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->setIface("eth0"); + OptionPtr iana = generateIA(D6O_IA_NA, 234, 1500, 3000); + sol->addOption(iana); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Add an IAADDR sub option with infinite lifetime. + uint32_t infinity_lft = Lease::INFINITY_LFT; + OptionPtr subopt(new Option6IAAddr(D6O_IAADDR, IOAddress("::"), + infinity_lft, infinity_lft)); + iana->addOption(subopt); + + // Pass it to the server and get an advertise + 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 that there's an address included + boost::shared_ptr addr = checkIA_NA(reply, 234, + subnet_->getT1(), + subnet_->getT2()); + ASSERT_TRUE(addr); + + // Check that the assigned address is indeed from the configured pool + checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA, + infinity_lft, infinity_lft); + + // check DUIDs + checkServerId(reply, srv.getServerID()); + checkClientId(reply, clientid); +} + // This test verifies that incoming SOLICIT can be handled properly, that an // ADVERTISE is generated, that the response has an address and that address // really belongs to the configured pool. @@ -1293,12 +1392,32 @@ TEST_F(Dhcpv6SrvTest, minLifetimeRenew) { // This test verifies that a renewal returns max ifetimes when // the client adds an IAPREFIX sub option with too large lifetime hints. TEST_F(Dhcpv6SrvTest, maxLifetimeRenew) { - // Max values are 4000 and 5000. + // Max values are 4000 and 5000. testRenewBasic(Lease::TYPE_PD, "2001:db8:1:2::", "2001:db8:1:2::", pd_pool_->getLength(), true, false, 5000, 6000, 4000, 5000); } +// This test verifies that a renewal handles static leases when +// the client adds an IAADDR sub option with infinite valid lifetime hint. +TEST_F(Dhcpv6SrvTest, staticLeaseRenew) { + uint32_t infinity_lft = Lease::INFINITY_LFT; + testRenewBasic(Lease::TYPE_NA, "2001:db8:1:1::cafe:babe", + "2001:db8:1:1::cafe:babe", 128, + true, false, 4000, infinity_lft, + 4000, infinity_lft); +} + +// This test verifies that a renewal handles static leases when +// the client adds an IAPREFIX sub option with infinite lifetime hints. +TEST_F(Dhcpv6SrvTest, staticLeaseRenew2) { + uint32_t infinity_lft = Lease::INFINITY_LFT; + testRenewBasic(Lease::TYPE_PD, "2001:db8:1:2::", + "2001:db8:1:2::", pd_pool_->getLength(), + true, false, infinity_lft, infinity_lft, + infinity_lft, infinity_lft); +} + // This test is a mixed of FqdnDhcpv6SrvTest.processRequestReuseExpiredLease // and testRenewBasic. The idea is to force the reuse of an expired lease // so the allocation engine reuseExpiredLease routine is called instead @@ -1344,6 +1463,26 @@ TEST_F(Dhcpv6SrvTest, maxLifetimeReuseExpired) { true, true, 5000, 6000, 4000, 5000); } +// This test verifies that an expired reuse handles static leases when +// the client adds an IAADDR sub option with infinite lifetime hints. +TEST_F(Dhcpv6SrvTest, staticLeaseReuseExpired) { + uint32_t infinity_lft = Lease::INFINITY_LFT; + testRenewBasic(Lease::TYPE_NA, "2001:db8:1:1::cafe:babe", + "2001:db8:1:1::cafe:babe", 128, + true, true, 4000, infinity_lft, + 4000, infinity_lft); +} + +// This test verifies that an expired reuse handles static leases when +// the client adds an IAADDR sub option with infinite valid lifetime hint. +TEST_F(Dhcpv6SrvTest, staticLeaseReuseExpired2) { + uint32_t infinity_lft = Lease::INFINITY_LFT; + testRenewBasic(Lease::TYPE_NA, "2001:db8:1:1::cafe:babe", + "2001:db8:1:1::cafe:babe", 128, + true, true, infinity_lft, infinity_lft, + infinity_lft, infinity_lft); +} + // This test verifies that incoming (positive) RELEASE with address can be // handled properly, that a REPLY is generated, that the response has status // code and that the lease is indeed removed from the database. @@ -2939,6 +3078,84 @@ TEST_F(Dhcpv6SrvTest, calculateTeeTimers) { } } +// Check that T1 and T2 values are set correctly for static leases. +TEST_F(Dhcpv6SrvTest, staticLeaseTeeTimers) { + NakedDhcpv6Srv srv(0); + + // Recreate subnet + Triplet unspecified; + Triplet preferred_lft(2000, 3000, 4000); + Triplet valid_lft(3000, 4000, 5000); + subnet_.reset(new Subnet6(IOAddress("2001:db8:1::"), 48, + unspecified, + unspecified, + preferred_lft, + valid_lft)); + subnet_->setIface("eth0"); + + pool_.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64)); + subnet_->addPool(pool_); + + // Allow static leases + subnet_->setAllowStaticLeases(true); + // Set inheritancea + subnet_->setFetchGlobalsFn([] () -> ConstElementPtr { + return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals()); + }); + // Set T1/T2 calculation (ignored for static leases) + subnet_->setCalculateTeeTimes(true); + subnet_->setT1Percent(.5); + subnet_->setT2Percent(1.); + CfgMgr::instance().clear(); + CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_); + + // Set bigger T1 and T2 defaults. + uint32_t t1 = 10*24*60*60; + ConstElementPtr t1_elem = Element::create(static_cast(t1)); + CfgMgr::instance().getStagingCfg()->addConfiguredGlobal("renew-timer", t1_elem); + uint32_t t2 = 30*24*60*60; + ConstElementPtr t2_elem = Element::create(static_cast(t2)); + CfgMgr::instance().getStagingCfg()->addConfiguredGlobal("rebind-timer", t2_elem); + + CfgMgr::instance().commit(); + + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->setIface("eth0"); + OptionPtr iana = generateIA(D6O_IA_NA, 234, 1500, 3000); + sol->addOption(iana); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Add an IAADDR sub option with infinite lifetime. + uint32_t infinity_lft = Lease::INFINITY_LFT; + OptionPtr subopt(new Option6IAAddr(D6O_IAADDR, IOAddress("::"), + infinity_lft, infinity_lft)); + iana->addOption(subopt); + + // Pass it to the server and get an advertise + 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 that there's an address included + boost::shared_ptr addr = checkIA_NA(reply, 234, t1, t2); + ASSERT_TRUE(addr); + + // Check that the assigned address is indeed from the configured pool + checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA, + infinity_lft, infinity_lft); + + // check DUIDs + checkServerId(reply, srv.getServerID()); + checkClientId(reply, clientid); +} + /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test /// to call processX() methods. diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index eef9b61efc..f2ef1f577d 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -279,6 +279,11 @@ Dhcpv6SrvTest::testRenewBasic(Lease::Type type, subnet_->setValid(Triplet(3000, 4000, 5000)); } + // Allow static leases for infinite lifetime. + if (expected_valid == Lease::INFINITY_LFT) { + subnet_->setAllowStaticLeases(true); + } + // Generate client-id also duid_ OptionPtr clientid = generateClientId(); diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index 8ae6b80029..ff0ce30cd8 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -560,13 +560,19 @@ public: // an ostream, which means it can't be used in EXPECT_EQ. EXPECT_TRUE(subnet_->inPool(type, addr->getAddress())); EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText()); - if (subnet_->getPreferred().getMin() != subnet_->getPreferred().getMax()) { + if ((expected_pref == Lease::INFINITY_LFT) && + subnet_->getAllowStaticLeases()) { + EXPECT_EQ(expected_pref, addr->getPreferred()); + } else if (subnet_->getPreferred().getMin() != subnet_->getPreferred().getMax()) { EXPECT_LE(subnet_->getPreferred().getMin(), addr->getPreferred()); EXPECT_GE(subnet_->getPreferred().getMax(), addr->getPreferred()); } else { EXPECT_EQ(subnet_->getPreferred(), addr->getPreferred()); } - if (subnet_->getValid().getMin() != subnet_->getValid().getMax()) { + if ((expected_valid == Lease::INFINITY_LFT) && + subnet_->getAllowStaticLeases()) { + EXPECT_EQ(expected_valid, addr->getValid()); + } else if (subnet_->getValid().getMin() != subnet_->getValid().getMax()) { EXPECT_LE(subnet_->getValid().getMin(), addr->getValid()); EXPECT_GE(subnet_->getValid().getMax(), addr->getValid()); } else {