From b7343ed2562822dc9eee9deaa9680c56f7263916 Mon Sep 17 00:00:00 2001 From: David Schweikert Date: Tue, 23 Jul 2013 00:25:53 +0200 Subject: [PATCH 1/3] start work on seqmap --- src/Makefile.am | 4 +- src/seqmap.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ src/seqmap.h | 16 +++++++ 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 src/seqmap.c create mode 100644 src/seqmap.h diff --git a/src/Makefile.am b/src/Makefile.am index 93d108e..5607a00 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,8 +9,8 @@ endif sbin_PROGRAMS = ${prog} -fping_SOURCES = fping.c options.h +fping_SOURCES = fping.c options.h seqmap.h seqmap.c 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_CFLAGS = $(AM_CFLAGS) -DIPV6 diff --git a/src/seqmap.c b/src/seqmap.c new file mode 100644 index 0000000..2e6c030 --- /dev/null +++ b/src/seqmap.c @@ -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 + * IPv6 Support: Jeroen Massar + * Improved main loop: David Schweikert + * Debian Merge, TOS settings: Tobi Oetiker + * 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 +#include + +/* 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; +} diff --git a/src/seqmap.h b/src/seqmap.h new file mode 100644 index 0000000..0887e68 --- /dev/null +++ b/src/seqmap.h @@ -0,0 +1,16 @@ +#ifndef SEQMAP_H +#define SEQMAP_H + +#include + +typedef struct seqmap_value +{ + unsigned int host_nr; + unsigned int ping_count; + struct timeval ping_ts; + +} SEQMAP_VALUE; + +#define SEQMAP_MAXSEQ 65000 + +#endif From 8daad08e1d141ccad2213a83b2c4c11360a6aefd Mon Sep 17 00:00:00 2001 From: David Schweikert Date: Tue, 23 Jul 2013 23:24:04 +0200 Subject: [PATCH 2/3] work on seqmap --- src/fping.c | 83 +++++++++++++++++++++------------------------------- src/seqmap.h | 4 +++ 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/fping.c b/src/fping.c index 4215215..c2bfb3c 100644 --- a/src/fping.c +++ b/src/fping.c @@ -58,6 +58,7 @@ extern "C" #include #include "config.h" +#include "seqmap.h" #ifdef HAVE_UNISTD_H #include @@ -114,16 +115,7 @@ extern int h_errno; /*** Ping packet defines ***/ -/* data added after ICMP header for our nefarious purposes */ - -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 MIN_PING_DATA 0 #define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */ #define SIZE_IP_HDR 20 #ifndef IPV6 @@ -292,7 +284,6 @@ double sum_replies = 0; int max_hostname_len = 0; int num_jobs = 0; /* number of hosts still to do */ 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 */ num_unreachable = 0, /* total number unreachable */ num_noaddress = 0; /* total number of addresses not found */ @@ -973,6 +964,8 @@ int main( int argc, char **argv ) srandom( start_time.tv_usec ); #endif /* DEBUG || _DEBUG */ + seqmap_init(); + /* main loop */ main_loop(); @@ -1531,7 +1524,6 @@ int send_ping( int s, HOST_ENTRY *h ) { char *buffer; FPING_ICMPHDR *icp; - PING_DATA *pdp; int n; int myseq; @@ -1543,8 +1535,7 @@ int send_ping( int s, HOST_ENTRY *h ) icp = ( FPING_ICMPHDR* )buffer; gettimeofday( &h->last_send_time, &tz ); - myseq = h->num_sent * num_hosts + h->i; - max_seq_sent = myseq > max_seq_sent ? myseq : max_seq_sent; + myseq = seqmap_add(h->i, h->num_sent, &h->last_send_time); #ifndef IPV6 icp->icmp_type = ICMP_ECHO; @@ -1553,10 +1544,6 @@ int send_ping( int s, HOST_ENTRY *h ) icp->icmp_seq = htons(myseq); 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 ); #else icp->icmp6_type = ICMP6_ECHO_REQUEST; @@ -1564,10 +1551,6 @@ int send_ping( int s, HOST_ENTRY *h ) icp->icmp6_seq = htons(myseq); 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... #endif #if defined(DEBUG) || defined(_DEBUG) @@ -1649,7 +1632,8 @@ int wait_for_reply(long wait_time) HOST_ENTRY *h; long this_reply; 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 ); @@ -1695,6 +1679,8 @@ int wait_for_reply(long wait_time) return( 1 ); /* too short */ }/* IF */ + gettimeofday( ¤t_time, &tz ); + icp = ( FPING_ICMPHDR* )( buffer + hlen ); #ifndef IPV6 if( icp->icmp_type != ICMP_ECHOREPLY ) @@ -1715,43 +1701,35 @@ int wait_for_reply(long wait_time) #endif return 1; /* packet received, but not the one we are looking for! */ - num_pingreceived++; - #ifndef IPV6 - if( ntohs(icp->icmp_seq) > max_seq_sent ) + seqmap_value = seqmap_fetch(ntohs(icp->icmp_seq), ¤t_time); #else - if( ntohs(icp->icmp6_seq) > max_seq_sent ) + seqmap_value = seqmap_fetch(ntohs(icp->icmp6_seq), ¤t_time); #endif - return( 1 ); /* packet received, don't worry about it anymore */ + if(seqmap_value == NULL) { + return 1; + } -#ifndef IPV6 - n = ntohs(icp->icmp_seq) % num_hosts; -#else - n = ntohs(icp->icmp6_seq) % num_hosts; -#endif + num_pingreceived++; + + n = seqmap_value->host_nr; h = table[n]; /* received ping is cool, so process it */ - gettimeofday( ¤t_time, &tz ); h->waiting = 0; h->timeout = timeout; h->num_recv++; h->num_recv_i++; -#ifndef IPV6 - memcpy( &sent_time, icp->icmp_data + offsetof( PING_DATA, ping_ts ), sizeof( sent_time ) ); - 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 + sent_time = &seqmap_value->ping_ts; + this_count = seqmap_value->ping_count; #if defined( DEBUG ) || defined( _DEBUG ) if( trace_flag ) printf( "received [%d] from %s\n", this_count, h->host ); #endif /* DEBUG || _DEBUG */ - this_reply = timeval_diff( ¤t_time, &sent_time ); + this_reply = timeval_diff( ¤t_time, sent_time ); if( this_reply > max_reply ) max_reply = this_reply; if( this_reply < min_reply ) min_reply = this_reply; if( this_reply > h->max_reply ) h->max_reply = this_reply; @@ -1903,6 +1881,7 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr ) FPING_ICMPHDR *sent_icmp; unsigned char *c; HOST_ENTRY *h; + SEQMAP_VALUE *seqmap_value; #ifdef IPV6 char addr_ascii[INET6_ADDRSTRLEN]; inet_ntop(addr->sin6_family, &addr->sin6_addr, addr_ascii, INET6_ADDRSTRLEN); @@ -1919,14 +1898,14 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr ) sent_icmp = ( FPING_ICMPHDR* )( c + 28 ); #ifndef IPV6 - sent_icmp = ( struct icmp* )( c + 28 ); + seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), ¤t_time); if( ( sent_icmp->icmp_type == ICMP_ECHO ) && ( 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 */ - h = table[ntohs(sent_icmp->icmp_seq) % num_hosts]; + h = table[seqmap_value->host_nr]; if( p->icmp_code > ICMP_UNREACH_MAXTYPE ) { @@ -1934,9 +1913,11 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr ) inet_ntoa( addr->sin_addr ), h->host ); #else + seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), ¤t_time); + if( ( sent_icmp->icmp6_type == ICMP_ECHO ) && ( 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 */ h = table[ntohs(sent_icmp->icmp6_seq) % num_hosts]; @@ -1977,21 +1958,25 @@ int handle_random_icmp( FPING_ICMPHDR *p, int psize, FPING_SOCKADDR *addr ) case ICMP_PARAMPROB: sent_icmp = ( FPING_ICMPHDR* )( c + 28 ); #ifndef IPV6 + seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), ¤t_time); + if( ( sent_icmp->icmp_type == ICMP_ECHO ) && ( 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 */ - 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", icmp_type_str[p->icmp_type], inet_ntoa( addr->sin_addr ), h->host ); if( inet_addr( h->host ) == -1 ) fprintf( stderr, " (%s)", inet_ntoa( h->saddr.sin_addr ) ); #else + seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), ¤t_time); + if( ( sent_icmp->icmp6_type == ICMP_ECHO ) && ( 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 */ h = table[ntohs(sent_icmp->icmp6_seq) % num_hosts]; diff --git a/src/seqmap.h b/src/seqmap.h index 0887e68..f85050d 100644 --- a/src/seqmap.h +++ b/src/seqmap.h @@ -13,4 +13,8 @@ typedef struct 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 From 7e55f0b0739eae9e53e3bca52af16ac4b6538dec Mon Sep 17 00:00:00 2001 From: David Schweikert Date: Tue, 23 Jul 2013 23:40:17 +0200 Subject: [PATCH 3/3] Fix loop issue after 65536 pings (reported by Peter Folk and GBert, #12), Minimum ping data size is now 0 --- ChangeLog | 4 ++++ src/fping.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e4695dc..caf7968 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +UNRELEASED + * Fix loop issue after 65536 pings (reported by Peter Folk and GBert, #12) + * Minimum ping data size is now 0 + 2013-05-22 David Schweikert * Version 3.5 * Fix sprint_tm buffer size crash (reported by Japheth Cleaver) diff --git a/src/fping.c b/src/fping.c index c2bfb3c..f9a5978 100644 --- a/src/fping.c +++ b/src/fping.c @@ -526,7 +526,9 @@ int main( int argc, char **argv ) break; 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); break;