Merge seqmap branch, fixes #12

pull/67/head
David Schweikert 12 years ago
commit fb5f5779cc

@ -1,8 +1,9 @@
UNRELEASED UNRELEASED
* Fix loop issue after 65536 pings (reported by Peter Folk and GBert, #12)
* Minimum ping data size is now 0
* Removed setsockopt IPV6_CHECKSUM, which shouldn't be set and breaks * Removed setsockopt IPV6_CHECKSUM, which shouldn't be set and breaks
compiling on Solaris (reported by Juergen Arndt) compiling on Solaris (reported by Juergen Arndt)
2013-05-22 David Schweikert <david@schweikert.ch> 2013-05-22 David Schweikert <david@schweikert.ch>
* Version 3.5 * Version 3.5
* Fix sprint_tm buffer size crash (reported by Japheth Cleaver) * Fix sprint_tm buffer size crash (reported by Japheth Cleaver)

@ -9,8 +9,8 @@ endif
sbin_PROGRAMS = ${prog} sbin_PROGRAMS = ${prog}
fping_SOURCES = fping.c options.h fping_SOURCES = fping.c options.h seqmap.h seqmap.c
fping_DEPENDENCIES = ../config.h fping_DEPENDENCIES = ../config.h
fping6_SOURCES = fping.c options.h fping6_SOURCES = fping.c options.h seqmap.h seqmap.c
fping6_DEPENDENCIES = ../config.h fping6_DEPENDENCIES = ../config.h
fping6_CFLAGS = $(AM_CFLAGS) -DIPV6 fping6_CFLAGS = $(AM_CFLAGS) -DIPV6

@ -58,6 +58,7 @@ extern "C"
#include <netinet/in.h> #include <netinet/in.h>
#include "config.h" #include "config.h"
#include "seqmap.h"
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
@ -114,16 +115,7 @@ extern int h_errno;
/*** Ping packet defines ***/ /*** Ping packet defines ***/
/* data added after ICMP header for our nefarious purposes */ #define MIN_PING_DATA 0
typedef struct ping_data
{
int ping_count; /* counts up to -c count or 1 */
struct timeval ping_ts; /* time sent */
} PING_DATA;
#define MIN_PING_DATA sizeof( PING_DATA )
#define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */ #define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */
#define SIZE_IP_HDR 20 #define SIZE_IP_HDR 20
#ifndef IPV6 #ifndef IPV6
@ -292,7 +284,6 @@ double sum_replies = 0;
int max_hostname_len = 0; int max_hostname_len = 0;
int num_jobs = 0; /* number of hosts still to do */ int num_jobs = 0; /* number of hosts still to do */
int num_hosts; /* total number of hosts */ int num_hosts; /* total number of hosts */
int max_seq_sent = 0; /* maximum sequence number sent so far */
int num_alive = 0, /* total number alive */ int num_alive = 0, /* total number alive */
num_unreachable = 0, /* total number unreachable */ num_unreachable = 0, /* total number unreachable */
num_noaddress = 0; /* total number of addresses not found */ num_noaddress = 0; /* total number of addresses not found */
@ -525,7 +516,9 @@ int main( int argc, char **argv )
break; break;
case 'b': case 'b':
if( !( ping_data_size = ( unsigned int )atoi( optarg ) ) ) errno = 0;
ping_data_size = (unsigned int) strtol(optarg, (char **)NULL, 10);
if( errno )
usage(1); usage(1);
break; break;
@ -963,6 +956,8 @@ int main( int argc, char **argv )
srandom( start_time.tv_usec ); srandom( start_time.tv_usec );
#endif /* DEBUG || _DEBUG */ #endif /* DEBUG || _DEBUG */
seqmap_init();
/* main loop */ /* main loop */
main_loop(); main_loop();
@ -1521,7 +1516,6 @@ int send_ping( int s, HOST_ENTRY *h )
{ {
char *buffer; char *buffer;
FPING_ICMPHDR *icp; FPING_ICMPHDR *icp;
PING_DATA *pdp;
int n; int n;
int myseq; int myseq;
@ -1533,8 +1527,7 @@ int send_ping( int s, HOST_ENTRY *h )
icp = ( FPING_ICMPHDR* )buffer; icp = ( FPING_ICMPHDR* )buffer;
gettimeofday( &h->last_send_time, &tz ); gettimeofday( &h->last_send_time, &tz );
myseq = h->num_sent * num_hosts + h->i; myseq = seqmap_add(h->i, h->num_sent, &h->last_send_time);
max_seq_sent = myseq > max_seq_sent ? myseq : max_seq_sent;
#ifndef IPV6 #ifndef IPV6
icp->icmp_type = ICMP_ECHO; icp->icmp_type = ICMP_ECHO;
@ -1543,10 +1536,6 @@ int send_ping( int s, HOST_ENTRY *h )
icp->icmp_seq = htons(myseq); icp->icmp_seq = htons(myseq);
icp->icmp_id = htons(ident); icp->icmp_id = htons(ident);
pdp = ( PING_DATA* )( buffer + SIZE_ICMP_HDR );
pdp->ping_ts = h->last_send_time;
pdp->ping_count = h->num_sent;
icp->icmp_cksum = in_cksum( ( unsigned short* )icp, ping_pkt_size ); icp->icmp_cksum = in_cksum( ( unsigned short* )icp, ping_pkt_size );
#else #else
icp->icmp6_type = ICMP6_ECHO_REQUEST; icp->icmp6_type = ICMP6_ECHO_REQUEST;
@ -1554,10 +1543,6 @@ int send_ping( int s, HOST_ENTRY *h )
icp->icmp6_seq = htons(myseq); icp->icmp6_seq = htons(myseq);
icp->icmp6_id = htons(ident); icp->icmp6_id = htons(ident);
pdp = ( PING_DATA* )( buffer + SIZE_ICMP_HDR );
pdp->ping_ts = h->last_send_time;
pdp->ping_count = h->num_sent;
icp->icmp6_cksum = 0; // The IPv6 stack calculates the checksum for us... icp->icmp6_cksum = 0; // The IPv6 stack calculates the checksum for us...
#endif #endif
#if defined(DEBUG) || defined(_DEBUG) #if defined(DEBUG) || defined(_DEBUG)
@ -1639,7 +1624,8 @@ int wait_for_reply(long wait_time)
HOST_ENTRY *h; HOST_ENTRY *h;
long this_reply; long this_reply;
int this_count; int this_count;
struct timeval sent_time; struct timeval *sent_time;
SEQMAP_VALUE *seqmap_value;
result = recvfrom_wto( s, buffer, sizeof(buffer), &response_addr, wait_time ); result = recvfrom_wto( s, buffer, sizeof(buffer), &response_addr, wait_time );
@ -1685,6 +1671,8 @@ int wait_for_reply(long wait_time)
return( 1 ); /* too short */ return( 1 ); /* too short */
}/* IF */ }/* IF */
gettimeofday( &current_time, &tz );
icp = ( FPING_ICMPHDR* )( buffer + hlen ); icp = ( FPING_ICMPHDR* )( buffer + hlen );
#ifndef IPV6 #ifndef IPV6
if( icp->icmp_type != ICMP_ECHOREPLY ) if( icp->icmp_type != ICMP_ECHOREPLY )
@ -1705,43 +1693,35 @@ int wait_for_reply(long wait_time)
#endif #endif
return 1; /* packet received, but not the one we are looking for! */ return 1; /* packet received, but not the one we are looking for! */
num_pingreceived++;
#ifndef IPV6 #ifndef IPV6
if( ntohs(icp->icmp_seq) > max_seq_sent ) seqmap_value = seqmap_fetch(ntohs(icp->icmp_seq), &current_time);
#else #else
if( ntohs(icp->icmp6_seq) > max_seq_sent ) seqmap_value = seqmap_fetch(ntohs(icp->icmp6_seq), &current_time);
#endif #endif
return( 1 ); /* packet received, don't worry about it anymore */ if(seqmap_value == NULL) {
return 1;
}
#ifndef IPV6 num_pingreceived++;
n = ntohs(icp->icmp_seq) % num_hosts;
#else n = seqmap_value->host_nr;
n = ntohs(icp->icmp6_seq) % num_hosts;
#endif
h = table[n]; h = table[n];
/* received ping is cool, so process it */ /* received ping is cool, so process it */
gettimeofday( &current_time, &tz );
h->waiting = 0; h->waiting = 0;
h->timeout = timeout; h->timeout = timeout;
h->num_recv++; h->num_recv++;
h->num_recv_i++; h->num_recv_i++;
#ifndef IPV6 sent_time = &seqmap_value->ping_ts;
memcpy( &sent_time, icp->icmp_data + offsetof( PING_DATA, ping_ts ), sizeof( sent_time ) ); this_count = seqmap_value->ping_count;
memcpy( &this_count, icp->icmp_data, sizeof( this_count ) );
#else
memcpy( &sent_time, ((char *)icp->icmp6_data32)+4+offsetof(PING_DATA, ping_ts), sizeof( sent_time ) );
memcpy( &this_count, ((char *)icp->icmp6_data32)+4, sizeof( this_count ) );
#endif
#if defined( DEBUG ) || defined( _DEBUG ) #if defined( DEBUG ) || defined( _DEBUG )
if( trace_flag ) if( trace_flag )
printf( "received [%d] from %s\n", this_count, h->host ); printf( "received [%d] from %s\n", this_count, h->host );
#endif /* DEBUG || _DEBUG */ #endif /* DEBUG || _DEBUG */
this_reply = timeval_diff( &current_time, &sent_time ); this_reply = timeval_diff( &current_time, sent_time );
if( this_reply > max_reply ) max_reply = this_reply; if( this_reply > max_reply ) max_reply = this_reply;
if( this_reply < min_reply ) min_reply = this_reply; if( this_reply < min_reply ) min_reply = this_reply;
if( this_reply > h->max_reply ) h->max_reply = this_reply; if( this_reply > h->max_reply ) h->max_reply = this_reply;
@ -1893,6 +1873,7 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr )
FPING_ICMPHDR *sent_icmp; FPING_ICMPHDR *sent_icmp;
unsigned char *c; unsigned char *c;
HOST_ENTRY *h; HOST_ENTRY *h;
SEQMAP_VALUE *seqmap_value;
#ifdef IPV6 #ifdef IPV6
char addr_ascii[INET6_ADDRSTRLEN]; char addr_ascii[INET6_ADDRSTRLEN];
inet_ntop(addr->sin6_family, &addr->sin6_addr, addr_ascii, INET6_ADDRSTRLEN); inet_ntop(addr->sin6_family, &addr->sin6_addr, addr_ascii, INET6_ADDRSTRLEN);
@ -1909,14 +1890,14 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr )
sent_icmp = ( FPING_ICMPHDR* )( c + 28 ); sent_icmp = ( FPING_ICMPHDR* )( c + 28 );
#ifndef IPV6 #ifndef IPV6
sent_icmp = ( struct icmp* )( c + 28 ); seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), &current_time);
if( ( sent_icmp->icmp_type == ICMP_ECHO ) && if( ( sent_icmp->icmp_type == ICMP_ECHO ) &&
( ntohs(sent_icmp->icmp_id) == ident ) && ( ntohs(sent_icmp->icmp_id) == ident ) &&
( ntohs(sent_icmp->icmp_seq) <= ( n_short )max_seq_sent ) ) ( seqmap_value != NULL ) )
{ {
/* this is a response to a ping we sent */ /* this is a response to a ping we sent */
h = table[ntohs(sent_icmp->icmp_seq) % num_hosts]; h = table[seqmap_value->host_nr];
if( p->icmp_code > ICMP_UNREACH_MAXTYPE ) if( p->icmp_code > ICMP_UNREACH_MAXTYPE )
{ {
@ -1924,9 +1905,11 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr )
inet_ntoa( addr->sin_addr ), h->host ); inet_ntoa( addr->sin_addr ), h->host );
#else #else
seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), &current_time);
if( ( sent_icmp->icmp6_type == ICMP_ECHO ) && if( ( sent_icmp->icmp6_type == ICMP_ECHO ) &&
( ntohs(sent_icmp->icmp6_id) == ident ) && ( ntohs(sent_icmp->icmp6_id) == ident ) &&
( ntohs(sent_icmp->icmp6_seq) <= ( n_short )max_seq_sent ) ) ( seqmap_value != NULL ) )
{ {
/* this is a response to a ping we sent */ /* this is a response to a ping we sent */
h = table[ntohs(sent_icmp->icmp6_seq) % num_hosts]; h = table[ntohs(sent_icmp->icmp6_seq) % num_hosts];
@ -1967,21 +1950,25 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr )
case ICMP_PARAMPROB: case ICMP_PARAMPROB:
sent_icmp = ( FPING_ICMPHDR* )( c + 28 ); sent_icmp = ( FPING_ICMPHDR* )( c + 28 );
#ifndef IPV6 #ifndef IPV6
seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), &current_time);
if( ( sent_icmp->icmp_type == ICMP_ECHO ) && if( ( sent_icmp->icmp_type == ICMP_ECHO ) &&
( ntohs(sent_icmp->icmp_id) == ident ) && ( ntohs(sent_icmp->icmp_id) == ident ) &&
( ntohs(sent_icmp->icmp_seq) <= ( n_short )max_seq_sent ) ) ( seqmap_value != NULL ) )
{ {
/* this is a response to a ping we sent */ /* this is a response to a ping we sent */
h = table[ntohs(sent_icmp->icmp_seq) % num_hosts]; h = table[seqmap_value->host_nr];
fprintf( stderr, "%s from %s for ICMP Echo sent to %s", fprintf( stderr, "%s from %s for ICMP Echo sent to %s",
icmp_type_str[p->icmp_type], inet_ntoa( addr->sin_addr ), h->host ); icmp_type_str[p->icmp_type], inet_ntoa( addr->sin_addr ), h->host );
if( inet_addr( h->host ) == -1 ) if( inet_addr( h->host ) == -1 )
fprintf( stderr, " (%s)", inet_ntoa( h->saddr.sin_addr ) ); fprintf( stderr, " (%s)", inet_ntoa( h->saddr.sin_addr ) );
#else #else
seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), &current_time);
if( ( sent_icmp->icmp6_type == ICMP_ECHO ) && if( ( sent_icmp->icmp6_type == ICMP_ECHO ) &&
( ntohs(sent_icmp->icmp6_id) == ident ) && ( ntohs(sent_icmp->icmp6_id) == ident ) &&
( ntohs(sent_icmp->icmp6_seq) <= ( n_short )max_seq_sent ) ) ( seqmap_value != NULL ) )
{ {
/* this is a response to a ping we sent */ /* this is a response to a ping we sent */
h = table[ntohs(sent_icmp->icmp6_seq) % num_hosts]; h = table[ntohs(sent_icmp->icmp6_seq) % num_hosts];

@ -0,0 +1,119 @@
/*
* fping: fast-ping, file-ping, favorite-ping, funky-ping
*
* Ping a list of target hosts in a round robin fashion.
* A better ping overall.
*
* fping website: http://www.fping.org
*
* Current maintainer of fping: David Schweikert
* Please send suggestions and patches to: david@schweikert.ch
*
*
* Original author: Roland Schemers <schemers@stanford.edu>
* IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
* Improved main loop: David Schweikert <david@schweikert.ch>
* Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
* Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
*
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by Stanford University. The name of the University may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* seqmap.c: implementation of a mapping between sequence number and (host, ping_nr)
* we can't just use ping_nr*host_count + host_nr, because it can
* overflow the 16 bit of the icmp header field. See also:
* https://github.com/schweikert/fping/issues/48
*/
#include "seqmap.h"
#include "options.h"
#include "limits.h"
#include <stdlib.h>
#include <stdio.h>
/* description of the data structure used:
*
* - we assume that no more than SEQMAP_MAXSEQ (65000) pings are sent in
* the timeout interval (SEQMAP_TIMEOUT_IN_S)
* - we store the values in an array with SEQMAP_MAXSEQ elements
* - current sequence number % SEQMAP_MAXSEQ gives the current index
* - when entering a value, we check that the current entry is expired
*/
static SEQMAP_VALUE *seqmap_map = NULL;
static unsigned int seqmap_next_id = 0;
static SEQMAP_VALUE *seqmap_free_list;
#define SEQMAP_TIMEOUT_IN_S 10
#define SEQMAP_UNASSIGNED_HOST_NR UINT_MAX
void seqmap_init()
{
unsigned int i;
seqmap_map = calloc(SEQMAP_MAXSEQ, sizeof(SEQMAP_VALUE));
if(seqmap_map == NULL) {
perror("malloc error (can't allocate seqmap_map)");
}
}
unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, struct timeval *now)
{
unsigned int current_id;
SEQMAP_VALUE *next_value;
if(!seqmap_map) {
fprintf(stderr, "fping internal error: seqmap not initialized.\n");
exit(4);
}
/* check if expired (note that unused seqmap values will have fields set to
* 0, so will be seen as expired */
next_value = &seqmap_map[seqmap_next_id];
if(next_value->ping_ts.tv_sec != 0 && (now->tv_sec - next_value->ping_ts.tv_sec) < SEQMAP_TIMEOUT_IN_S) {
fprintf(stderr, "fping error: not enough sequence numbers available! (expire_timeout=%d, host_nr=%d, ping_count=%d, seqmap_next_id=%d)\n",
SEQMAP_TIMEOUT_IN_S, host_nr, ping_count, seqmap_next_id);
exit(4);
}
/* store the value */
next_value->host_nr = host_nr;
next_value->ping_count = ping_count;
next_value->ping_ts.tv_sec = now->tv_sec;
next_value->ping_ts.tv_usec = now->tv_usec;
/* increase next id */
current_id = seqmap_next_id;
seqmap_next_id = (seqmap_next_id + 1) % SEQMAP_MAXSEQ;
return current_id;
}
SEQMAP_VALUE *seqmap_fetch(unsigned int id, struct timeval *now)
{
SEQMAP_VALUE *value;
if(id > SEQMAP_MAXSEQ) {
return NULL;
}
value = &seqmap_map[id];
/* verify that value is not expired */
if(now->tv_sec - value->ping_ts.tv_sec >= SEQMAP_TIMEOUT_IN_S) {
return NULL;
}
return value;
}

@ -0,0 +1,20 @@
#ifndef SEQMAP_H
#define SEQMAP_H
#include <sys/time.h>
typedef struct seqmap_value
{
unsigned int host_nr;
unsigned int ping_count;
struct timeval ping_ts;
} SEQMAP_VALUE;
#define SEQMAP_MAXSEQ 65000
void seqmap_init();
unsigned int seqmap_add(unsigned int host_nr, unsigned int ping_count, struct timeval *now);
SEQMAP_VALUE *seqmap_fetch(unsigned int id, struct timeval *now);
#endif
Loading…
Cancel
Save