]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2797] add unit tests for lease reuse statistics
authorAndrei Pavel <andrei@isc.org>
Fri, 28 Apr 2023 12:17:11 +0000 (15:17 +0300)
committerAndrei Pavel <andrei@isc.org>
Wed, 3 May 2023 19:26:09 +0000 (22:26 +0300)
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dora_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/sarr_unittest.cc

index f827f0ebff665ecb7536d5c82e7bc1b42d7e6b66..02167097a1dce606d0fd2e37e704258ad1fe5db4 100644 (file)
@@ -647,7 +647,8 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) {
         "v4-allocation-fail-subnet",
         "v4-allocation-fail-no-pools",
         "v4-allocation-fail-classes",
-        "v4-reservation-conflicts"
+        "v4-reservation-conflicts",
+        "v4-lease-reuses",
     };
 
     // preparing the schema which check if all statistics are set to zero
index 801647ec97b1766ebc27f96411cd7ef49a5dd844..abba841404b8b577d0ff097ddd6ed675fe3042eb 100644 (file)
@@ -579,7 +579,32 @@ const char* DORA_CONFIGS[] = {
         "        \"interface\": \"eth0\""
         "    }"
         "]"
-    "}"
+    "}",
+
+    // Configuration 18
+    R"({
+        "cache-max-age": 600,
+        "cache-threshold": .50,
+        "interfaces-config": {
+            "interfaces": [ "*" ]
+        },
+        "subnet4": [
+            {
+                "id": 1,
+                "pools": [
+                    {
+                        "pool": "10.0.0.10 - 10.0.0.20"
+                    },
+                ],
+                "subnet": "10.0.0.0/24"
+            },
+            {
+                "id": 2,
+                "subnet": "192.168.0.0/24"
+            }
+        ],
+        "valid-lifetime": 600
+    })",
 };
 
 /// @brief Test fixture class for testing 4-way (DORA) exchanges.
@@ -831,6 +856,9 @@ public:
     /// to the configuration and it allocates random addresses.
     void randomAllocation();
 
+    /// @brief Checks that features related to lease caching (such as lease reuse statistics) work.
+    void leaseCaching();
+
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
 };
@@ -2800,6 +2828,163 @@ TEST_F(DORATest, randomAllocationMultiThreading) {
     randomAllocation();
 }
 
+void
+DORATest::leaseCaching() {
+    // Configure a DHCP client.
+    Dhcp4Client client;
+    client.includeClientId("11:22");
+
+    // Configure a DHCP server.
+    configure(DORA_CONFIGS[18], *client.getServer());
+
+    // Obtain a lease from the server using the 4-way exchange.
+    ASSERT_NO_THROW(client.doDORA());
+
+    // Make sure that the server responded.
+    Pkt4Ptr response(client.getContext().response_);
+    ASSERT_TRUE(response);
+
+    // Make sure that the server has responded with DHCPACK.
+    EXPECT_EQ(DHCPACK, response->getType());
+
+    // Make sure that the server ID is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+
+    // Make sure that the client has got the first lease in the pool.
+    ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
+
+    // There should only be the default global statistic point.
+    ObservationPtr lease_reuses(StatsMgr::instance().getObservation("v4-lease-reuses"));
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // There should only be the default subnet statistic point.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // There should only be the default statistic point in the other subnet.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // Assume the client enters init-reboot and does a request.
+    client.setState(Dhcp4Client::INIT_REBOOT);
+    ASSERT_NO_THROW(client.doRequest());
+
+    // Make sure that the server responded.
+    response = client.getContext().response_;
+    ASSERT_TRUE(response);
+
+    // Make sure that the server has responded with DHCPACK.
+    EXPECT_EQ(DHCPACK, response->getType());
+
+    // Make sure that the server ID is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+
+    // Make sure that the client has got the same lease.
+    ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
+
+    // There should be one global lease reuse.
+    lease_reuses = StatsMgr::instance().getObservation("v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(2, lease_reuses->getSize());
+    EXPECT_EQ(1, lease_reuses->getInteger().first);
+
+    // There should be one subnet lease reuse.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(2, lease_reuses->getSize());
+    EXPECT_EQ(1, lease_reuses->getInteger().first);
+
+    // Statistics for the other subnet should not be affected.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // Let's say the client suddenly decides to do a full DORA.
+    ASSERT_NO_THROW(client.doDORA());
+
+    // Make sure that the server responded.
+    response = client.getContext().response_;
+    ASSERT_TRUE(response);
+
+    // Make sure that the server has responded with DHCPACK.
+    EXPECT_EQ(DHCPACK, response->getType());
+
+    // Make sure that the server ID is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+
+    // Make sure that the client has got the same lease.
+    ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
+
+    // There should be three global lease reuses (REQUEST + DISCOVER + REQUEST).
+    lease_reuses = StatsMgr::instance().getObservation("v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(4, lease_reuses->getSize());
+    EXPECT_EQ(3, lease_reuses->getInteger().first);
+
+    // There should be three subnet lease reuses (REQUEST + DISCOVER + REQUEST).
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(4, lease_reuses->getSize());
+    EXPECT_EQ(3, lease_reuses->getInteger().first);
+
+    // Statistics for the other subnet should not be affected.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // Try to request a different address than the client has. The server
+    // should respond with DHCPNAK.
+    client.config_.lease_.addr_ = IOAddress("10.0.0.30");
+    ASSERT_NO_THROW(client.doRequest());
+
+    // Make sure that the server responded.
+    response = client.getContext().response_;
+    ASSERT_TRUE(response);
+    EXPECT_EQ(DHCPNAK, response->getType());
+
+    // Change client identifier. The server should treat the request
+    // as a request from unknown client and ignore it.
+    client.includeClientId("12:34");
+    ASSERT_NO_THROW(client.doRequest());
+    ASSERT_FALSE(client.getContext().response_);
+
+    // Global statistics should remain unchanged.
+    lease_reuses = StatsMgr::instance().getObservation("v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(4, lease_reuses->getSize());
+    EXPECT_EQ(3, lease_reuses->getInteger().first);
+
+    // Subnet statistics should remain unchanged.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(4, lease_reuses->getSize());
+    EXPECT_EQ(3, lease_reuses->getInteger().first);
+
+    // Statistics for the other subnet should certainly not be affected.
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v4-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+}
+
+TEST_F(DORATest, leaseCaching) {
+    Dhcpv4SrvMTTestGuard guard(*this, false);
+    leaseCaching();
+}
+
+TEST_F(DORATest, leaseCachingMultiThreading) {
+    Dhcpv4SrvMTTestGuard guard(*this, true);
+    leaseCaching();
+}
+
 // Starting tests which require MySQL backend availability. Those tests
 // will not be executed if Kea has been compiled without the
 // --with-mysql.
index a5837514213815864407672a60589020720b2aae..d1ed5217130b3396f6be0258e5b46b1eedb41f4b 100644 (file)
@@ -1360,7 +1360,9 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelStats) {
         "v6-allocation-fail-shared-network",
         "v6-allocation-fail-subnet",
         "v6-allocation-fail-no-pools",
-        "v6-allocation-fail-classes"
+        "v6-allocation-fail-classes",
+        "v6-ia-na-lease-reuses",
+        "v6-ia-pd-lease-reuses",
     };
 
     std::ostringstream s;
index 43dc922cee15371b3e33d45af1fc58b3fcd1281f..ee4c8f69a88a1840339099244d22a1f7aeecd5aa 100644 (file)
@@ -21,6 +21,7 @@ using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
+using namespace isc::stats;
 
 namespace {
 
@@ -286,6 +287,39 @@ const char* CONFIGS[] = {
         "    }"
         "],"
         "\"valid-lifetime\": 4000 }",
+
+    // Configuration 7
+    R"({
+        "cache-max-age": 600,
+        "cache-threshold": .50,
+        "interfaces-config": {
+            "interfaces": [ "*" ]
+        },
+        "subnet6": [
+            {
+                "id": 1,
+                "interface": "eth0",
+                "pools": [
+                    {
+                        "pool": "2001:db8::10 - 2001:db8::20"
+                    },
+                ],
+                "pd-pools": [
+                    {
+                        "prefix": "2001:db8:1::",
+                        "prefix-len": 64,
+                        "delegated-len": 96
+                    },
+                ],
+                "subnet": "2001:db8::/32"
+            },
+            {
+                "id": 2,
+                "subnet": "3001:db8::/32"
+            }
+        ],
+        "valid-lifetime": 600
+    })",
 };
 
 /// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise,
@@ -387,6 +421,9 @@ public:
     /// to the configuration and it allocates random prefixes.
     void randomPrefixAllocation();
 
+    /// @brief Checks that features related to lease caching (such as lease reuse statistics) work.
+    void leaseCaching();
+
     /// @brief Interface Manager's fake configuration control.
     IfaceMgrTestConfig iface_mgr_test_config_;
 };
@@ -1159,5 +1196,177 @@ TEST_F(SARRTest, randomPrefixAllocationMultiThreading) {
     randomPrefixAllocation();
 }
 
+void
+SARRTest::leaseCaching() {
+    // Configure a DHCP client.
+    Dhcp6Client client;
+
+    // Configure a DHCP server.
+    configure(CONFIGS[7], *client.getServer());
+
+    // Statistics should have default values.
+    ObservationPtr lease_reuses(StatsMgr::instance().getObservation("v6-ia-na-lease-reuses"));
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // Append IAADDR and IAPREFIX options to the client's message.
+    ASSERT_NO_THROW(client.requestAddress(1234, asiolink::IOAddress("2001:db8::10")));
+    ASSERT_NO_THROW(client.requestPrefix(5678, 32, asiolink::IOAddress("2001:db8:1::")));
+
+    // Perform 4-way exchange.
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Server should have assigned an address and a prefix.
+    ASSERT_EQ(2, client.getLeaseNum());
+
+    // The server should respect the hints.
+    Lease6 lease_client(client.getLease(0));
+    EXPECT_EQ("2001:db8::10", lease_client.addr_.toText());
+    EXPECT_EQ(128, lease_client.prefixlen_);
+    Lease6Ptr lease_server(checkLease(lease_client));
+    EXPECT_TRUE(lease_server);
+    lease_client = client.getLease(1);
+    EXPECT_EQ("2001:db8:1::", lease_client.addr_.toText());
+    EXPECT_EQ(96, lease_client.prefixlen_);
+    lease_server = checkLease(lease_client);
+    EXPECT_TRUE(lease_server);
+
+    // Check statistics.
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // Request the same prefix with a different length. The server should
+    // return an existing lease.
+    client.clearRequestedIAs();
+    ASSERT_NO_THROW(client.requestAddress(1234, asiolink::IOAddress("2001:db8::10")));
+    ASSERT_NO_THROW(client.requestPrefix(5678, 80, IOAddress("2001:db8:1::")));
+    ASSERT_NO_THROW(client.doSARR());
+    ASSERT_EQ(2, client.getLeaseNum());
+    lease_client = client.getLease(0);
+    EXPECT_EQ("2001:db8::10", lease_client.addr_.toText());
+    EXPECT_EQ(128, lease_client.prefixlen_);
+    lease_client = client.getLease(1);
+    EXPECT_EQ("2001:db8:1::", lease_client.addr_.toText());
+    EXPECT_EQ(96, lease_client.prefixlen_);
+
+    // Check statistics.
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(2, lease_reuses->getSize());
+    EXPECT_EQ(1, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(2, lease_reuses->getSize());
+    EXPECT_EQ(1, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(2, lease_reuses->getSize());
+    EXPECT_EQ(1, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(2, lease_reuses->getSize());
+    EXPECT_EQ(1, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+
+    // Try to request another prefix. The client should still get the existing
+    // lease.
+    client.clearRequestedIAs();
+    ASSERT_NO_THROW(client.requestAddress(1234, asiolink::IOAddress("2001:db8::10")));
+    ASSERT_NO_THROW(client.requestPrefix(5678, 64, IOAddress("2001:db8:2::")));
+    ASSERT_NO_THROW(client.doRequest());
+    ASSERT_EQ(2, client.getLeaseNum());
+    lease_client = client.getLease(0);
+    EXPECT_EQ("2001:db8::10", lease_client.addr_.toText());
+    EXPECT_EQ(128, lease_client.prefixlen_);
+    lease_client = client.getLease(1);
+    EXPECT_EQ("2001:db8:1::", lease_client.addr_.toText());
+    EXPECT_EQ(96, lease_client.prefixlen_);
+
+    // Check statistics.
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(3, lease_reuses->getSize());
+    EXPECT_EQ(2, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(3, lease_reuses->getSize());
+    EXPECT_EQ(2, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-na-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(3, lease_reuses->getSize());
+    EXPECT_EQ(2, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[1].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(3, lease_reuses->getSize());
+    EXPECT_EQ(2, lease_reuses->getInteger().first);
+    lease_reuses = StatsMgr::instance().getObservation("subnet[2].v6-ia-pd-lease-reuses");
+    ASSERT_TRUE(lease_reuses);
+    EXPECT_EQ(1, lease_reuses->getSize());
+    EXPECT_EQ(0, lease_reuses->getInteger().first);
+}
+
+TEST_F(SARRTest, leaseCaching) {
+    Dhcpv6SrvMTTestGuard guard(*this, false);
+    leaseCaching();
+}
+
+TEST_F(SARRTest, leaseCachingMultiThreading) {
+    Dhcpv6SrvMTTestGuard guard(*this, true);
+    leaseCaching();
+}
 
 } // end of anonymous namespace