]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1716] Add retry bind unicast unit test
authorSlawek Figiel <slawek@isc.org>
Fri, 25 Mar 2022 17:15:40 +0000 (18:15 +0100)
committerRazvan Becheriu <razvan@isc.org>
Mon, 4 Apr 2022 14:46:44 +0000 (17:46 +0300)
src/lib/dhcp/tests/iface_mgr_unittest.cc

index ec23a55ef38c991b6284512417537aa3f2eac2bf..e1f976f41e2070a8bc86cbdc8cde14c2c10568ed 100644 (file)
@@ -2511,8 +2511,9 @@ TEST_F(IfaceMgrTest, openSocket6ErrorHandler) {
 }
 
 // Test that the external retry callback is called when trying to bind a new
-// socket to the address and port being in use. The opening should be repeated.
-TEST_F(IfaceMgrTest, openSocket6RetryCallback) {
+// multicast socket to the address and port being in use. The opening should
+// be repeated.
+TEST_F(IfaceMgrTest, openMulticastSocket6RetryCallback) {
     NakedIfaceMgr ifacemgr;
 
     // Remove all real interfaces and create a set of dummy interfaces.
@@ -2565,6 +2566,64 @@ TEST_F(IfaceMgrTest, openSocket6RetryCallback) {
     EXPECT_EQ(5, total_attempts);
 }
 
+// Test that the external retry callback is called when trying to bind a new
+// unicast socket to the address and port being in use. The opening should be
+// repeated.
+TEST_F(IfaceMgrTest, openUnicastSocket6RetryCallback) {
+    NakedIfaceMgr ifacemgr;
+
+    // Remove all real interfaces and create a set of dummy interfaces.
+    ifacemgr.createIfaces();
+    // Add an unicast.
+    ifacemgr.getIface("eth1")->addUnicast(IOAddress("2001:db8:1::2"));
+
+    boost::shared_ptr<PktFilter6Stub> filter(new PktFilter6Stub());
+    ASSERT_TRUE(filter);
+    ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
+
+    // Open unicast socket on eth1.
+    ASSERT_NO_THROW(ifacemgr.openSocket("eth1",
+                                        IOAddress("2001:db8:1::2"),
+                                        DHCP6_SERVER_PORT, false));
+
+    // Install an retry callback before trying to open sockets. This handler
+    // should be called when the IfaceMgr fails to open socket on eth0.
+    // The callback counts the retry attempts. The retry indices must be sequential.
+    // The caller must wait specific time between calls.
+    uint16_t total_attempts = 0;
+    auto last_call_time = std::chrono::system_clock::time_point::min();
+    isc::dhcp::IfaceMgrRetryCallback retry_callback =
+        [&total_attempts, &last_call_time](uint16_t current_attempt, const std::string& msg){
+            // An attempt index must be sequential.
+            EXPECT_EQ(total_attempts, current_attempt);
+            total_attempts++;
+
+            // A waiting time isn't too short.
+            auto now = std::chrono::system_clock::now();
+            if (current_attempt != 0) {
+                auto interval = now - last_call_time;
+                EXPECT_GE(interval, std::chrono::milliseconds(10));
+            }
+            last_call_time = now;
+
+            // Message has content.
+            EXPECT_FALSE(msg.empty());
+
+            // Test for 5 retries with 10 milliseconds waiting time.
+            return std::make_pair(current_attempt < 4, 10);
+        };
+
+    // The openSockets6 should detect that there is another socket already
+    // open and bound to the same address and port. An attempt to open
+    // another socket and bind to this address and port should fail and be repeated
+    // a few times. The exception is thrown because the error handler is NULL.
+    EXPECT_THROW(ifacemgr.openSockets6(DHCP6_SERVER_PORT, nullptr, retry_callback),
+        isc::dhcp::SocketConfigError);
+
+    // The callback should notice 5 attempts to open a port - 1 initial and 4 retries.
+    EXPECT_EQ(5, total_attempts);
+}
+
 // This test verifies that the function correctly checks that the v6 socket is
 // open and bound to a specific address.
 TEST_F(IfaceMgrTest, hasOpenSocketForAddress6) {