Browse Source

[2231] Implemented sub-second timeouts for IfaceMgr::receiveX functions.

Marcin Siodelski 12 years ago
parent
commit
d8dd6f7cb2
3 changed files with 124 additions and 16 deletions
  1. 9 11
      src/lib/dhcp/iface_mgr.cc
  2. 9 5
      src/lib/dhcp/iface_mgr.h
  3. 106 0
      src/lib/dhcp/tests/iface_mgr_unittest.cc

+ 9 - 11
src/lib/dhcp/iface_mgr.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -785,7 +785,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
 
 
 
 
 boost::shared_ptr<Pkt4>
 boost::shared_ptr<Pkt4>
-IfaceMgr::receive4(uint32_t timeout) {
+IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec) {
 
 
     const SocketInfo* candidate = 0;
     const SocketInfo* candidate = 0;
     IfaceCollection::const_iterator iface;
     IfaceCollection::const_iterator iface;
@@ -825,13 +825,12 @@ IfaceMgr::receive4(uint32_t timeout) {
         names << session_socket_ << "(session)";
         names << session_socket_ << "(session)";
     }
     }
 
 
-    /// @todo: implement sub-second precision one day
     struct timeval select_timeout;
     struct timeval select_timeout;
-    select_timeout.tv_sec = timeout;
-    select_timeout.tv_usec = 0;
+    select_timeout.tv_sec = timeout_sec;
+    select_timeout.tv_usec = timeout_usec;
 
 
     cout << "Trying to receive data on sockets: " << names.str()
     cout << "Trying to receive data on sockets: " << names.str()
-         << ". Timeout is " << timeout << " seconds." << endl;
+         << ". Timeout is " << timeout_sec << " seconds." << endl;
     int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
     int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
     cout << "select returned " << result << endl;
     cout << "select returned " << result << endl;
 
 
@@ -953,7 +952,7 @@ IfaceMgr::receive4(uint32_t timeout) {
     return (pkt);
     return (pkt);
 }
 }
 
 
-Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
+Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec) {
 
 
     const SocketInfo* candidate = 0;
     const SocketInfo* candidate = 0;
     fd_set sockets;
     fd_set sockets;
@@ -994,12 +993,11 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
     }
     }
 
 
     cout << "Trying to receive data on sockets:" << names.str()
     cout << "Trying to receive data on sockets:" << names.str()
-         << ".Timeout is " << timeout << " seconds." << endl;
+         << ".Timeout is " << timeout_sec << " seconds." << endl;
 
 
-    /// @todo: implement sub-second precision one day
     struct timeval select_timeout;
     struct timeval select_timeout;
-    select_timeout.tv_sec = timeout;
-    select_timeout.tv_usec = 0;
+    select_timeout.tv_sec = timeout_sec;
+    select_timeout.tv_usec = timeout_usec;
 
 
     int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
     int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
 
 

+ 9 - 5
src/lib/dhcp/iface_mgr.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -345,10 +345,12 @@ public:
     /// to not wait infinitely, but rather do something useful
     /// to not wait infinitely, but rather do something useful
     /// (e.g. remove expired leases)
     /// (e.g. remove expired leases)
     ///
     ///
-    /// @param timeout specifies timeout (in seconds)
+    /// @param timeout_sec specifies integral part of the timeout (in seconds)
+    /// @param timeout_usec specifies fractional part of the timeout
+    /// (in microseconds)
     ///
     ///
     /// @return Pkt6 object representing received packet (or NULL)
     /// @return Pkt6 object representing received packet (or NULL)
-    Pkt6Ptr receive6(uint32_t timeout);
+    Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0);
 
 
     /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
     /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
     ///
     ///
@@ -356,10 +358,12 @@ public:
     /// If reception is successful and all information about its sender
     /// If reception is successful and all information about its sender
     /// are obtained, Pkt4 object is created and returned.
     /// are obtained, Pkt4 object is created and returned.
     ///
     ///
-    /// @param timeout specifies timeout (in seconds)
+    /// @param timeout_sec specifies integral part of the timeout (in seconds)
+    /// @param timeout_usec specifies fractional part of the timeout
+    /// (in microseconds)
     ///
     ///
     /// @return Pkt4 object representing received packet (or NULL)
     /// @return Pkt4 object representing received packet (or NULL)
-    Pkt4Ptr receive4(uint32_t timeout);
+    Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0);
 
 
     /// Opens UDP/IP socket and binds it to address, interface and port.
     /// Opens UDP/IP socket and binds it to address, interface and port.
     ///
     ///

+ 106 - 0
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -217,6 +217,112 @@ TEST_F(IfaceMgrTest, getIface) {
 
 
 }
 }
 
 
+TEST_F(IfaceMgrTest, receiveTimeout6) {
+    std::cout << "Testing DHCPv6 packet reception timeouts."
+              << " Test will block for a few seconds when waiting"
+              << " for timeout to occur." << std::endl;
+
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+    // Open socket on the lo interface.
+    IOAddress loAddr("::1");
+    int socket1 = 0;
+    ASSERT_NO_THROW(
+        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547)
+    );
+    // Socket is open if its descriptor is greater than zero.
+    ASSERT_GT(socket1, 0);
+
+    // Time when call to IfaceMgr::receive6() started.
+    timeval start_time;
+    // Time when call to IfaceMgr::receive6() ended.
+    timeval stop_time;
+
+    // Remember when we call receive6().
+    gettimeofday(&start_time, NULL);
+    // Call receive with timeout of 1s + 1000us.
+    Pkt6Ptr pkt = ifacemgr->receive6(1, 1000);
+    // Remember when call to receive6() ended.
+    gettimeofday(&stop_time, NULL);
+    // We did not send a packet to lo interface so we expect that
+    // nothing has been received and timeout has been reached.
+    ASSERT_FALSE(pkt);
+    // Calculate duration of call to receive6().
+    stop_time.tv_sec -= start_time.tv_sec;
+    stop_time.tv_usec -= start_time.tv_usec;
+    // Duration should be equal or greater than timeout specified
+    // for the receive6() call.
+    EXPECT_EQ(stop_time.tv_sec, 1);
+    EXPECT_GT(stop_time.tv_usec, 1000);
+
+    // Test timeout shorter than 1s.
+    gettimeofday(&start_time, NULL);
+    pkt = ifacemgr->receive6(0, 500);
+    gettimeofday(&stop_time, NULL);
+    ASSERT_FALSE(pkt);
+    stop_time.tv_sec -= start_time.tv_sec;
+    stop_time.tv_usec -= start_time.tv_usec;
+    // The timeout has been set to 500us. Even though the way we measure
+    // duration of receive6() may result in durations slightly longer than
+    // timeout it is safe to assume that measured value will not exceed 1s
+    // when timeout is only 500us. If it exceeds, this is an error and
+    // should be investigated.
+    EXPECT_EQ(0, stop_time.tv_sec);
+    EXPECT_GT(stop_time.tv_usec, 500);
+}
+
+TEST_F(IfaceMgrTest, receiveTimeout4) {
+    std::cout << "Testing DHCPv6 packet reception timeouts."
+              << " Test will block for a few seconds when waiting"
+              << " for timeout to occur." << std::endl;
+
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+    // Open socket on the lo interface.
+    IOAddress loAddr("127.0.0.1");
+    int socket1 = 0;
+    ASSERT_NO_THROW(
+        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10067)
+    );
+    // Socket is open if its descriptor is greater than zero.
+    ASSERT_GT(socket1, 0);
+
+    // Time when call to IfaceMgr::receive4() started.
+    timeval start_time;
+    // Time when call to IfaceMgr::receive4() ended.
+    timeval stop_time;
+
+    // Remember when we call receive4().
+    gettimeofday(&start_time, NULL);
+    // Call receive with timeout of 2s + 150us.
+    Pkt4Ptr pkt = ifacemgr->receive4(2, 150);
+    // Remember when call to receive4() ended.
+    gettimeofday(&stop_time, NULL);
+    // We did not send a packet to lo interface so we expect that
+    // nothing has been received and timeout has been reached.
+    ASSERT_FALSE(pkt);
+    // Calculate duration of call to receive4().
+    stop_time.tv_sec -= start_time.tv_sec;
+    stop_time.tv_usec -= start_time.tv_usec;
+    // Duration should be equal or greater than timeout specified
+    // for the receive4() call.
+    EXPECT_EQ(stop_time.tv_sec, 2);
+    EXPECT_GT(stop_time.tv_usec, 150);
+
+    // Test timeout shorter than 1s.
+    gettimeofday(&start_time, NULL);
+    pkt = ifacemgr->receive4(0, 350);
+    gettimeofday(&stop_time, NULL);
+    ASSERT_FALSE(pkt);
+    stop_time.tv_sec -= start_time.tv_sec;
+    stop_time.tv_usec -= start_time.tv_usec;
+    // The timeout has been set to 350us. Even though the way we measure
+    // duration of receive4() may result in durations slightly longer than
+    // timeout it is safe to assume that measured value will not exceed 1s
+    // when timeout is only 350us. If it exceeds, this is an error and
+    // should be investigated.
+    EXPECT_EQ(0, stop_time.tv_sec);
+    EXPECT_GT(stop_time.tv_usec, 350);
+}
+
 TEST_F(IfaceMgrTest, sockets6) {
 TEST_F(IfaceMgrTest, sockets6) {
     // testing socket operation in a portable way is tricky
     // testing socket operation in a portable way is tricky
     // without interface detection implemented
     // without interface detection implemented