Parcourir la source

Merge branch 'trac2231'

Conflicts:
	src/lib/dhcp/tests/iface_mgr_unittest.cc
Marcin Siodelski il y a 12 ans
Parent
commit
15560cac16
3 fichiers modifiés avec 133 ajouts et 18 suppressions
  1. 22 13
      src/lib/dhcp/iface_mgr.cc
  2. 11 5
      src/lib/dhcp/iface_mgr.h
  3. 100 0
      src/lib/dhcp/tests/iface_mgr_unittest.cc

+ 22 - 13
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
 // purpose with or without fee is hereby granted, provided that the above
@@ -820,8 +820,12 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
 
 
 boost::shared_ptr<Pkt4>
-IfaceMgr::receive4(uint32_t timeout) {
-
+IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
+    // Sanity check for microsecond timeout.
+    if (timeout_usec >= 1000000) {
+        isc_throw(BadValue, "fractional timeout must be shorter than"
+                  " one million microseconds");
+    }
     const SocketInfo* candidate = 0;
     IfaceCollection::const_iterator iface;
     fd_set sockets;
@@ -861,13 +865,13 @@ IfaceMgr::receive4(uint32_t timeout) {
         names << session_socket_ << "(session)";
     }
 
-    /// @todo: implement sub-second precision one day
     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()
-         << ". Timeout is " << timeout << " seconds." << endl;
+         << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
+         << timeout_usec << " seconds." << endl;
     int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
     cout << "select returned " << result << endl;
 
@@ -990,7 +994,12 @@ IfaceMgr::receive4(uint32_t timeout) {
     return (pkt);
 }
 
-Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
+Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) {
+    // Sanity check for microsecond timeout.
+    if (timeout_usec >= 1000000) {
+        isc_throw(BadValue, "fractional timeout must be shorter than"
+                  " one million microseconds");
+    }
 
     const SocketInfo* candidate = 0;
     fd_set sockets;
@@ -1030,13 +1039,13 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
         names << session_socket_ << "(session)";
     }
 
-    cout << "Trying to receive data on sockets:" << names.str()
-         << ".Timeout is " << timeout << " seconds." << endl;
+    cout << "Trying to receive data on sockets: " << names.str()
+         << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
+         << timeout_usec << " seconds." << endl;
 
-    /// @todo: implement sub-second precision one day
     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);
 

+ 11 - 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
 // purpose with or without fee is hereby granted, provided that the above
@@ -364,10 +364,13 @@ public:
     /// to not wait infinitely, but rather do something useful
     /// (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)
     ///
+    /// @throw isc::BadValue if timeout_usec is greater than one million
     /// @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.
     ///
@@ -375,10 +378,13 @@ public:
     /// If reception is successful and all information about its sender
     /// 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)
     ///
+    /// @throw isc::BadValue if timeout_usec is greater than one million
     /// @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.
     ///

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

@@ -218,6 +218,106 @@ TEST_F(IfaceMgrTest, getIface) {
 
 }
 
+TEST_F(IfaceMgrTest, receiveTimeout6) {
+    using namespace boost::posix_time;
+    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);
+
+    // Remember when we call receive6().
+    ptime start_time = microsec_clock::universal_time();
+    // Call receive with timeout of 1s + 400000us = 1.4s.
+    Pkt6Ptr pkt;
+    ASSERT_NO_THROW(pkt = ifacemgr->receive6(1, 400000));
+    // Remember when call to receive6() ended.
+    ptime stop_time = microsec_clock::universal_time();
+    // 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().
+    time_duration duration = stop_time - start_time;
+    // We stop the clock when the call completes so it does not
+    // precisely reflect the receive timeout. However the
+    // uncertainity should be low enough to expect that measured
+    // value is in the range <1.4s; 1.7s>.
+    EXPECT_GE(duration.total_microseconds(), 1400000);
+    EXPECT_LE(duration.total_microseconds(), 1700000);
+
+    // Test timeout shorter than 1s.
+    start_time = microsec_clock::universal_time();
+    ASSERT_NO_THROW(pkt = ifacemgr->receive6(0, 500000));
+    stop_time = microsec_clock::universal_time();
+    ASSERT_FALSE(pkt);
+    duration = stop_time - start_time;
+    // Check if measured duration is within <0.5s; 0.8s>.
+    EXPECT_GE(duration.total_microseconds(), 500000);
+    EXPECT_LE(duration.total_microseconds(), 800000);
+
+    // Test with invalid fractional timeout values.
+    EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue);
+    EXPECT_THROW(ifacemgr->receive6(1, 1000010), isc::BadValue);
+}
+
+TEST_F(IfaceMgrTest, receiveTimeout4) {
+    using namespace boost::posix_time;
+    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);
+
+    Pkt4Ptr pkt;
+    // Remember when we call receive4().
+    ptime start_time = microsec_clock::universal_time();
+    // Call receive with timeout of 2s + 300000us = 2.3s.
+    ASSERT_NO_THROW(pkt = ifacemgr->receive4(2, 300000));
+    // Remember when call to receive4() ended.
+    ptime stop_time = microsec_clock::universal_time();
+    // 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().
+    time_duration duration = stop_time - start_time;
+    // We stop the clock when the call completes so it does not
+    // precisely reflect the receive timeout. However the
+    // uncertainity should be low enough to expect that measured
+    // value is in the range <2.3s; 2.6s>.
+    EXPECT_GE(duration.total_microseconds(), 2300000);
+    EXPECT_LE(duration.total_microseconds(), 2600000);
+
+    // Test timeout shorter than 1s.
+    start_time = microsec_clock::universal_time();
+    ASSERT_NO_THROW(pkt = ifacemgr->receive4(0, 400000));
+    stop_time = microsec_clock::universal_time();
+    ASSERT_FALSE(pkt);
+    duration = stop_time - start_time;
+    // Check if measured duration is within <0.4s; 0.7s>.
+    EXPECT_GE(duration.total_microseconds(), 400000);
+    EXPECT_LE(duration.total_microseconds(), 700000);
+
+    // Test with invalid fractional timeout values.
+    EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue);
+    EXPECT_THROW(ifacemgr->receive6(2, 1000005), isc::BadValue);
+}
+
 TEST_F(IfaceMgrTest, multipleSockets) {
     boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());