From: Marcin Siodelski Date: Mon, 13 Jul 2015 17:35:28 +0000 (+0200) Subject: [3947] Implemented 'new-leases-on-renew' parameter. X-Git-Tag: trac3238_base~6^2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8564e578c423ab8049e913a582806282681d879a;p=thirdparty%2Fkea.git [3947] Implemented 'new-leases-on-renew' parameter. --- diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec index e0fa45be45..e577e889f3 100644 --- a/src/bin/dhcp6/dhcp6.spec +++ b/src/bin/dhcp6/dhcp6.spec @@ -62,6 +62,12 @@ "item_default": 4000 }, + { "item_name": "new-leases-on-renew", + "item_type": "boolean", + "item_optional": true, + "item_default": true + }, + { "item_name": "option-def", "item_type": "list", "item_optional": false, diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 18860b9901..da58f13d47 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -1721,7 +1721,7 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer, ctx.ia_rsp_ = ia_rsp; ctx.hwaddr_ = orig_ctx.hwaddr_; ctx.host_ = orig_ctx.host_; - ctx.allow_new_leases_in_renewals_ = true; + ctx.allow_new_leases_in_renewals_ = subnet->getAllocLeasesOnRenew(); // Extract the addresses that the client is trying to obtain. OptionCollection addrs = ia->getOptions(); @@ -1889,7 +1889,7 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query, ctx.ia_rsp_ = ia_rsp; ctx.hwaddr_ = orig_ctx.hwaddr_; ctx.host_ = orig_ctx.host_; - ctx.allow_new_leases_in_renewals_ = true; + ctx.allow_new_leases_in_renewals_ = subnet->getAllocLeasesOnRenew(); // Extract prefixes that the client is trying to renew. OptionCollection addrs = ia->getOptions(); diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 3f3f45a60a..f7c4efdc60 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -473,14 +473,19 @@ protected: } } + // Gather boolean parameters values. bool rapid_commit = boolean_values_->getOptionalParam("rapid-commit", false); + bool alloc_leases_on_renew = globalContext()-> + boolean_values_->getOptionalParam("new-leases-on-renew", true); std::ostringstream output; output << addr << "/" << static_cast(len) << " with params t1=" << t1 << ", t2=" << t2 << ", preferred-lifetime=" << pref << ", valid-lifetime=" << valid - << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled"); + << ", rapid-commit is " << (rapid_commit ? "enabled" : "disabled") + << ", new-leases-on-renew is " << (alloc_leases_on_renew ? "enabled" : "disabled"); + LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(output.str()); @@ -497,6 +502,8 @@ protected: // Enable or disable Rapid Commit option support for the subnet. subnet6->setRapidCommit(rapid_commit); + // Enable or disable allocation of the new leases for the Renew or/and Rebind message. + subnet6->setAllocLeasesOnRenew(alloc_leases_on_renew); // Try setting up client class (if specified) try { @@ -693,6 +700,8 @@ namespace dhcp { parser = new RSOOListConfigParser(config_id); } else if (config_id.compare("control-socket") == 0) { parser = new ControlSocketParser(config_id); + } else if (config_id.compare("new-leases-on-renew") == 0) { + parser = new BooleanParser(config_id, globalContext()->boolean_values_); } else { isc_throw(DhcpConfigError, "unsupported global configuration parameter: " diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 46f6d91601..5b905c2d10 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -531,6 +531,37 @@ public: CfgMgr::instance().clear(); } + /// @brief Test the 'new-leases-on-renew' configuration flag. + /// + /// @param config Server configuration, possibly including the + /// 'new-leases-on-renew' parameter. + /// @param exp_alloc_leases_on_renew Expected value of the flag + void testAllocLeasesOnRenew(const std::string& config, + const bool exp_alloc_leases_on_renew) { + // Clear any existing configuration. + CfgMgr::instance().clear(); + + // Configure the server. + ElementPtr json = Element::fromJSON(config); + + // Make sure that the configuration was successful. + ConstElementPtr status; + EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); + checkResult(status, 0); + + // Get the subnet. + Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()-> + selectSubnet(IOAddress("2001:db8:1::5"), classify_); + ASSERT_TRUE(subnet); + + // Check the flag against the expected value. + EXPECT_EQ(exp_alloc_leases_on_renew, subnet->getAllocLeasesOnRenew()); + + // Clear any existing configuration. + CfgMgr::instance().clear(); + } + + int rcode_; ///< Return code (see @ref isc::config::parseAnswer) Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer) @@ -1172,6 +1203,55 @@ TEST_F(Dhcp6ParserTest, subnetRapidCommit) { } } +// This test checks the configuration of the Rapid Commit option +// support for the subnet. +TEST_F(Dhcp6ParserTest, subnetAllocLeasesOnRenew) { + { + // new-leases-on-renew implicitly set to false. + SCOPED_TRACE("Default setting for new-leases-on-renew"); + testAllocLeasesOnRenew("{ \"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::1 - " + "2001:db8:1::ffff\" } ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }", + true); + } + + { + // new-leases-on-renew explicitly set to true. + SCOPED_TRACE("Enable new-leases-on-renew"); + testAllocLeasesOnRenew("{ \"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": True," + + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::1 - " + "2001:db8:1::ffff\" } ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }", + true); + } + + { + // new-leases-on-renew explicitly set to false. + SCOPED_TRACE("Disable new-leases-on-renew"); + testAllocLeasesOnRenew("{ \"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::1 - " + "2001:db8:1::ffff\" } ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }", + false); + } +} + // This test checks that multiple pools can be defined and handled properly. // The test defines 2 subnets, each with 2 pools. TEST_F(Dhcp6ParserTest, multiplePools) { diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index 9e6fe0fd99..138144a560 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -444,6 +444,9 @@ Dhcpv6SrvTest::testRenewReject(Lease::Type type, const IOAddress& addr) { // Quick sanity check that the address we're about to use is ok ASSERT_TRUE(subnet_->inPool(type, addr)); + // Do not allocate leases as a result of Renew/Rebind. + subnet_->setAllocLeasesOnRenew(false); + // GenerateClientId() also sets duid_ OptionPtr clientid = generateClientId(); diff --git a/src/bin/dhcp6/tests/rebind_unittest.cc b/src/bin/dhcp6/tests/rebind_unittest.cc index 02b6dfc676..e3f36abd8f 100644 --- a/src/bin/dhcp6/tests/rebind_unittest.cc +++ b/src/bin/dhcp6/tests/rebind_unittest.cc @@ -70,6 +70,7 @@ const char* REBIND_CONFIGS[] = { "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," " \"subnet\": \"2001:db8:1::/48\", " @@ -91,6 +92,7 @@ const char* REBIND_CONFIGS[] = { "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"2001:db8:3::/64\" } ]," " \"subnet\": \"2001:db8:3::/48\", " @@ -112,6 +114,7 @@ const char* REBIND_CONFIGS[] = { "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"3000:1::/64\" } ]," " \"subnet\": \"3000:1::/48\", " @@ -133,6 +136,7 @@ const char* REBIND_CONFIGS[] = { "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," "\"subnet6\": [ { " " \"pools\": [ { \"pool\": \"3000:3::/64\" } ]," " \"subnet\": \"3000:3::/48\", " @@ -154,6 +158,7 @@ const char* REBIND_CONFIGS[] = { "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," "\"subnet6\": [ { " " \"pd-pools\": [" " { \"prefix\": \"3000::\", " @@ -183,6 +188,7 @@ const char* REBIND_CONFIGS[] = { "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " + "\"new-leases-on-renew\": False," "\"subnet6\": [ { " " \"pd-pools\": [" " { \"prefix\": \"2001:db8:3:01::\", " diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index f4d5c3ec9d..3b13d7e03c 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -330,7 +330,8 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, const Triplet& valid_lifetime, const SubnetID id) :Subnet(prefix, length, t1, t2, valid_lifetime, RelayInfo(IOAddress("::")), id), - preferred_(preferred_lifetime), rapid_commit_(false) { + preferred_(preferred_lifetime), rapid_commit_(false), + alloc_leases_on_renew_(true) { if (!prefix.isV6()) { isc_throw(BadValue, "Non IPv6 prefix " << prefix << " specified in subnet6"); diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 92c896db2a..730ed7d0bd 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -655,6 +655,21 @@ public: return (rapid_commit_); } + /// @brief Enables or disables the allocation of the new leases for the + /// Renew and Rebind case. + /// + /// @param alloc_leases_on_renew A boolean value indicating if the server + /// can allocate new leases for the Renew and Rebind case. + void setAllocLeasesOnRenew(const bool alloc_leases_on_renew) { + alloc_leases_on_renew_ = alloc_leases_on_renew; + } + + /// @brief Returns boolean value indicating if the new leases are allocated + /// by the server as a result of processing Renew and/or Rebind. + bool getAllocLeasesOnRenew() const { + return (alloc_leases_on_renew_); + } + private: /// @brief Returns default address for pool selection @@ -683,6 +698,18 @@ private: /// It's default value is false, which indicates that the Rapid /// Commit is disabled for the subnet. bool rapid_commit_; + + /// @brief A flag indicating if the server may allocate new leases + /// for the client sending a Renew or Rebind message. + /// + /// This flag indicates that the client may request allocation of the + /// new leases (of any type) when it renews existing leases. This + /// facilitates the use cases described in RFC7550. If the server is + /// configured to allocate new leases for the Renew and Rebind case + /// but it can't allocate them (e.g. because of the pool exhaustion) + /// it will send the NoAddrsAvail or the NoPrefixAvail status code + /// in the IA, depending on the IA type. + bool alloc_leases_on_renew_; }; /// @brief A pointer to a Subnet6 object diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index 8a5ad8af72..aa75f155aa 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -1059,6 +1059,24 @@ TEST(Subnet6Test, rapidCommit) { EXPECT_FALSE(subnet.getRapidCommit()); } +// This test checks that the flag which indicates if the new leases are +// allocated as a result of processing the Renew and Rebind message can +// be set to "enable" or "disable". +TEST(Subnet6Test, allocNewLeasesOnRenew) { + Subnet6 subnet(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4); + + // By default, the flag should be enabled. + EXPECT_TRUE(subnet.getAllocLeasesOnRenew()); + + // Disable it. + subnet.setAllocLeasesOnRenew(false); + EXPECT_FALSE(subnet.getAllocLeasesOnRenew()); + + // Enable again. + subnet.setAllocLeasesOnRenew(true); + EXPECT_TRUE(subnet.getAllocLeasesOnRenew()); +} + // Checks if last allocated address/prefix is stored/retrieved properly TEST(Subnet6Test, lastAllocated) { IOAddress ia("2001:db8:1::1");