Support kernel-timestamping of received packet, fixes #46

version3
David Schweikert 8 years ago
parent 926c104e90
commit 2cf801fdfb

@ -1,4 +1,5 @@
Unreleased Unreleased
* (feature) Support kernel-timestamping of received packets (#46)
* (bugfix) Fix compatibility issue with AIX (#69, @blentzgh) * (bugfix) Fix compatibility issue with AIX (#69, @blentzgh)
* (bugfix) Fix -q not suppressing some ICMP error messages (#83) * (bugfix) Fix -q not suppressing some ICMP error messages (#83)

@ -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.]) AC_MSG_ERROR([You must enable at least one of IPv4 and IPv6.])
fi 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 AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AM_MAINTAINER_MODE AM_MAINTAINER_MODE

@ -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 */ /* handle host names supplied on command line or in a file */
/* if the generate_flag is on, then generate the IP list */ /* 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); 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 );
Function: wait_for_reply 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");
}
}
************************************************************* if(nfound == 0)
return 0;
else
return 1;
}
Inputs: void (none) 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;
Returns: int // Wait for input on the socket
if(timeout && !wait_on_socket(socket, timeout)) {
return 0; /* timeout */
}
Description: // 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;
}
#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 wait_for_reply(long wait_time)
{ {
int result; int result;
static char buffer[4096]; static char buffer[4096];
struct sockaddr_storage response_addr; struct sockaddr_storage response_addr;
socklen_t response_addr_len;
int hlen = 0; int hlen = 0;
FPING_ICMPHDR *icp; FPING_ICMPHDR *icp;
int n, avg; int n, avg;
@ -1607,16 +1688,39 @@ int wait_for_reply(long wait_time)
long this_reply; long this_reply;
int this_count; int this_count;
struct timeval *sent_time; struct timeval *sent_time;
struct timeval recv_time;
SEQMAP_VALUE *seqmap_value; SEQMAP_VALUE *seqmap_value;
#ifndef IPV6 #ifndef IPV6
struct ip *ip; struct ip *ip;
#endif #endif
response_addr_len = sizeof(struct sockaddr_storage); // receive packet
result = recvfrom_wto( s, buffer, sizeof(buffer), (struct sockaddr *) &response_addr, &response_addr_len, wait_time ); {
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 ;
}
}
if( result < 0 ) result = receive_reply(s, // socket
return 0; /* timeout */ 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 defined( DEBUG ) || defined( _DEBUG ) #if defined( DEBUG ) || defined( _DEBUG )
if( randomly_lose_flag ) if( randomly_lose_flag )
@ -1646,7 +1750,7 @@ int wait_for_reply(long wait_time)
if( verbose_flag ) if( verbose_flag )
{ {
char buf[INET6_ADDRSTRLEN]; 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); printf( "received packet too short for ICMP (%d bytes from %s)\n", result, buf);
} }
return( 1 ); /* too short */ return( 1 ); /* too short */
@ -1662,7 +1766,7 @@ int wait_for_reply(long wait_time)
#endif #endif
{ {
/* handle some problem */ /* 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++; num_othericmprcvd++;
return 1; return 1;
}/* IF */ }/* IF */
@ -1689,7 +1793,7 @@ int wait_for_reply(long wait_time)
h = table[n]; h = table[n];
sent_time = &seqmap_value->ping_ts; sent_time = &seqmap_value->ping_ts;
this_count = seqmap_value->ping_count; 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 ) 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)) { if(addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
char buf[INET6_ADDRSTRLEN]; 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, " [<- %s]", buf);
} }
fprintf( stderr, "\n" ); 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)) { if(addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
char buf[INET6_ADDRSTRLEN]; 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, " [<- %s]", buf);
} }
@ -1787,8 +1891,8 @@ int wait_for_reply(long wait_time)
{ {
if(timestamp_flag) { if(timestamp_flag) {
printf("[%lu.%06lu] ", printf("[%lu.%06lu] ",
(unsigned long)current_time.tv_sec, (unsigned long)recv_time.tv_sec,
(unsigned long)current_time.tv_usec); (unsigned long)recv_time.tv_usec);
} }
avg = h->total_time / h->num_recv; avg = h->total_time / h->num_recv;
printf( "%s%s : [%d], %d bytes, %s ms", 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)) { if(addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
char buf[INET6_ADDRSTRLEN]; 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, " [<- %s]", buf);
} }

@ -42,6 +42,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h>
char *ping_buffer = 0; char *ping_buffer = 0;
size_t ping_pkt_size; 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; return s;
} }

@ -39,6 +39,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h>
#include <netinet/icmp6.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; return s;
} }

Loading…
Cancel
Save