Browse Source

[3947] Implemented 'new-leases-on-renew' parameter.

Marcin Siodelski 9 years ago
parent
commit
8564e578c4

+ 6 - 0
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,

+ 2 - 2
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();

+ 10 - 1
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<int>(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: "

+ 80 - 0
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) {

+ 3 - 0
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();
 

+ 6 - 0
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::\", "

+ 2 - 1
src/lib/dhcpsrv/subnet.cc

@@ -330,7 +330,8 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& 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");

+ 27 - 0
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

+ 18 - 0
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");