]> git.gsnw.org Git - fping.git/commitdiff
Support kernel-timestamping of received packet, fixes #46
authorDavid Schweikert <david@schweikert.ch>
Fri, 13 Jan 2017 14:07:28 +0000 (15:07 +0100)
committerDavid Schweikert <david@schweikert.ch>
Fri, 13 Jan 2017 14:07:28 +0000 (15:07 +0100)
ChangeLog
configure.ac
src/fping.c
src/socket4.c
src/socket6.c

index b85b84f8d94e83803dda87cfe1a08bce87b8d45f..e2b967af8eab801fe1062d54df2459be0ce463c1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,5 @@
 Unreleased
+  * (feature) Support kernel-timestamping of received packets (#46)
   * (bugfix) Fix compatibility issue with AIX (#69, @blentzgh)
   * (bugfix) Fix -q not suppressing some ICMP error messages (#83)
 
index 9b7beca560ebd6ffd3c9c4fbf0bec4d6b4fc7a91..263397345df4122f318f03a5381e3dece5778e7a 100644 (file)
@@ -28,6 +28,17 @@ if test x$ipv4 = xfalse && test x$ipv6 = xfalse; then
   AC_MSG_ERROR([You must enable at least one of IPv4 and IPv6.])
 fi
 
+AC_ARG_ENABLE([timestamp],
+  AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMP)]))
+AS_IF([test "x$enable_timestamp" != "xno"], [
+   AC_CHECK_DECL([SO_TIMESTAMP], [AC_DEFINE(HAVE_SO_TIMESTAMP, [1], [set define])], [have_so_timestamp="no"], [#include <sys/types.h>
+#include <sys/socket.h>])
+])
+dnl Test if --enable-timestamp is explicitely enabled and make an error if this platform doesn't support it
+AS_IF([test "x$enable_timestamp" = "xyes" -a "x$have_so_timestamp" = "xno"], [
+  AC_MSG_ERROR([--enable-timestamp not supported on this platform])
+])
+
 AC_CANONICAL_TARGET
 AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 AM_MAINTAINER_MODE
index 74e5941725cbf810917a315f7e7dd36d1f962f8e..7084034c991be1da58cf4337c37dbb2247f00ca2 100644 (file)
@@ -742,6 +742,15 @@ int main( int argc, char **argv )
         }
     }
 
+#if HAVE_SO_TIMESTAMP 
+    {
+        int opt = 1;
+        if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP,  &opt, sizeof(opt))) {
+            perror("setting SO_TIMESTAMP option");
+        }
+    }
+#endif
+
     /* handle host names supplied on command line or in a file */
     /* if the generate_flag is on, then generate the IP list */
 
@@ -1578,28 +1587,100 @@ int send_ping( int s, HOST_ENTRY *h )
     return(ret);
 }
 
+int wait_on_socket(int socket, struct timeval *timeout)
+{
+    int nfound;
+    fd_set readset, writeset;
+
+select_again:
+    FD_ZERO( &readset );
+    FD_ZERO( &writeset );
+    FD_SET( s, &readset );
 
-/************************************************************
+    nfound = select(socket + 1, &readset, &writeset, NULL, timeout);
+    if(nfound < 0) {
+        if(errno == EINTR) {
+            /* interrupted system call: redo the select */
+            goto select_again;
+        }
+        else {
+            perror("select");
+        }
+    }
 
-  Function: wait_for_reply
+    if(nfound == 0)
+        return 0;
+    else
+        return 1;
+}
 
-*************************************************************
+int receive_reply(int socket,
+                  struct timeval    *timeout,
+                  struct timeval    *reply_timestamp,
+                  struct sockaddr   *reply_src_addr,
+                  size_t            reply_src_addr_len,
+                  char              *reply_buf,
+                  size_t            reply_buf_len)
+{
+    int recv_len;
 
-  Inputs:  void (none)
+    // Wait for input on the socket
+    if(timeout && !wait_on_socket(socket, timeout)) {
+        return 0;   /* timeout */
+    }
 
-  Returns:  int
+    // Receive data
+    {
+        static unsigned char msg_control[40];
+        struct iovec msg_iov = {
+            reply_buf,
+            reply_buf_len
+        };
+        struct msghdr recv_msghdr = {
+            reply_src_addr,
+            reply_src_addr_len,
+            &msg_iov,
+            1,
+            &msg_control,
+            sizeof(msg_control),
+            0
+        };
+        int timestamp_set = 0;
+
+        recv_len = recvmsg(socket, &recv_msghdr, 0);
+        if(recv_len <= 0) {
+            return 0;
+        }
 
-  Description:
-  
+#if HAVE_SO_TIMESTAMP 
+       // ancilliary data
+       struct cmsghdr *cmsg;
+        for(cmsg = CMSG_FIRSTHDR(&recv_msghdr);
+            cmsg != NULL;
+            cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg))
+        {
+            if(cmsg->cmsg_level == SOL_SOCKET &&
+               cmsg->cmsg_type == SCM_TIMESTAMP)
+            {
+                memcpy(reply_timestamp, CMSG_DATA(cmsg), sizeof(*reply_timestamp));
+                timestamp_set = 1;
+            }
+        }
+#endif
 
-************************************************************/
+        if(! timestamp_set) {
+            gettimeofday(reply_timestamp, NULL);
+        }
+    }
+
+    return recv_len;
+}
 
 int wait_for_reply(long wait_time)
 {
     int result;
     static char buffer[4096];
     struct sockaddr_storage response_addr;
-    socklen_t response_addr_len;
     int hlen = 0;
     FPING_ICMPHDR *icp;
     int n, avg;
@@ -1607,17 +1688,40 @@ int wait_for_reply(long wait_time)
     long this_reply;
     int this_count;
     struct timeval *sent_time;
+    struct timeval recv_time;
     SEQMAP_VALUE *seqmap_value;
 #ifndef IPV6
     struct ip *ip;
 #endif
 
-    response_addr_len = sizeof(struct sockaddr_storage);
-    result = recvfrom_wto( s, buffer, sizeof(buffer), (struct sockaddr *) &response_addr, &response_addr_len, wait_time );
+    // receive packet
+    {
+        struct timeval to;
+        if(wait_time) {
+            if(wait_time < 100000) {
+                to.tv_sec = 0;
+                to.tv_usec = wait_time * 10;
+            }
+            else {
+                to.tv_sec = wait_time / 100000 ;
+                to.tv_usec = (wait_time % 100000) * 10 ;
+            }
+        }
+
+        result = receive_reply(s,                            // socket
+                                wait_time ? &to : NULL,       // timeout
+                                &recv_time,                   // reply_timestamp
+                                (struct sockaddr *) &response_addr, // reply_src_addr
+                                sizeof(response_addr),        // reply_src_addr_len
+                                buffer,                       // reply_buf
+                                sizeof(buffer)                // reply_buf_len
+                            );
+
+        if(result <= 0) {
+            return 0;
+        }
+    }
 
-    if( result < 0 )
-        return 0;   /* timeout */
-  
 #if defined( DEBUG ) || defined( _DEBUG )
     if( randomly_lose_flag )
     {
@@ -1646,7 +1750,7 @@ int wait_for_reply(long wait_time)
         if( verbose_flag )
         {
             char buf[INET6_ADDRSTRLEN];
-            getnameinfo((struct sockaddr *)&response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
             printf( "received packet too short for ICMP (%d bytes from %s)\n", result, buf);
         }
         return( 1 ); /* too short */ 
@@ -1662,7 +1766,7 @@ int wait_for_reply(long wait_time)
 #endif
     {
         /* handle some problem */
-        if( handle_random_icmp( icp, (struct sockaddr *)&response_addr, response_addr_len ) )
+        if( handle_random_icmp( icp, (struct sockaddr *)&response_addr, sizeof(response_addr) ) )
             num_othericmprcvd++;
         return 1;
     }/* IF */
@@ -1689,7 +1793,7 @@ int wait_for_reply(long wait_time)
     h = table[n];
     sent_time  = &seqmap_value->ping_ts;
     this_count = seqmap_value->ping_count;
-    this_reply = timeval_diff( &current_time, sent_time );
+    this_reply = timeval_diff( &recv_time, sent_time );
 
     if( loop_flag || h->resp_times[this_count] == RESP_WAITING )
     {
@@ -1739,7 +1843,7 @@ int wait_for_reply(long wait_time)
 
                     if(addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
                         char buf[INET6_ADDRSTRLEN];
-                        getnameinfo((struct sockaddr *)&response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+                        getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
                         fprintf( stderr, " [<- %s]", buf);
                     }
                     fprintf( stderr, "\n" );
@@ -1774,7 +1878,7 @@ int wait_for_reply(long wait_time)
 
             if(addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
                 char buf[INET6_ADDRSTRLEN];
-                getnameinfo((struct sockaddr *)&response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
                 fprintf( stderr, " [<- %s]", buf);
             }
 
@@ -1787,8 +1891,8 @@ int wait_for_reply(long wait_time)
     {
         if(timestamp_flag) {
             printf("[%lu.%06lu] ",
-                 (unsigned long)current_time.tv_sec,
-                 (unsigned long)current_time.tv_usec);
+                 (unsigned long)recv_time.tv_sec,
+                 (unsigned long)recv_time.tv_usec);
         }
         avg = h->total_time / h->num_recv;
         printf( "%s%s : [%d], %d bytes, %s ms",
@@ -1808,7 +1912,7 @@ int wait_for_reply(long wait_time)
 
         if(addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
             char buf[INET6_ADDRSTRLEN];
-            getnameinfo((struct sockaddr *)&response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
+            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
             fprintf( stderr, " [<- %s]", buf);
         }
         
index fe3e3523e8505f8886f8d1a1a8aaa7deb93e013e..e298656f94f491c14c89bbad4ad76a7fb3fe867c 100644 (file)
@@ -42,6 +42,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <fcntl.h>
 
 char *ping_buffer = 0;
 size_t ping_pkt_size;
@@ -65,6 +66,17 @@ int open_ping_socket_ipv4()
         }
     }
 
+    /* Make sure that we use non-blocking IO */
+    {
+        int flags;
+
+        if((flags = fcntl(s, F_GETFL, 0)) < 0)
+            perror("fcntl");
+
+        if(fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
+            perror("fcntl");
+    }
+
     return s;
 }
 
index f08086eda3837f06d89c46c2d8232c0eabb2ebe5..881021c96dc0956ea87e000a2736ecdbcd70929e 100644 (file)
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <fcntl.h>
 
 #include <netinet/icmp6.h>  
 
@@ -64,6 +65,17 @@ int open_ping_socket_ipv6()
         }
     }
 
+    /* Make sure that we use non-blocking IO */
+    {
+        int flags;
+
+        if((flags = fcntl(s, F_GETFL, 0)) < 0)
+            perror("fcntl");
+
+        if(fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
+            perror("fcntl");
+    }
+
     return s;
 }