diff --git a/Makefile b/Makefile index 8f90785..aded371 100644 --- a/Makefile +++ b/Makefile @@ -27,10 +27,10 @@ ADMINFLAGS := $(ADMINFLAGS) -DWIN_USE_NO_ADMIN_PING GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)" CFLAGS += -DVERSION=\"$(GIT_VERSION)\" -cnping-wingdi.exe : cnping.c ping.c httping.c resources.o +cnping-wingdi.exe : cnping.c ping.c httping.c pinghostlist.c resolve.c resources.o $(MINGW32)gcc -g -fno-ident -mwindows -m32 $(CFLAGS) -o $@ $^ -lgdi32 -lws2_32 -s -D_WIN32_WINNT=0x0600 -DWIN32 -liphlpapi -DMINGW_BUILD $(ADMINFLAGS) -cnping.exe : cnping.c ping.c httping.c resolve.c resources.o +cnping.exe : cnping.c ping.c httping.c pinghostlist.c resolve.c resources.o $(MINGW32)gcc -g -fno-ident -mwindows -m32 -DCNFGOGL $(CFLAGS) -o $@ $^ -lgdi32 -lws2_32 -s -D_WIN32_WINNT=0x0600 -DWIN32 -liphlpapi -lopengl32 -DMINGW_BUILD $(ADMINFLAGS) resources.o : resources.rc @@ -39,13 +39,13 @@ resources.o : resources.rc # Unix -cnping : cnping.c ping.c httping.c resolve.c +cnping : cnping.c ping.c httping.c resolve.c pinghostlist.c $(CC) $(CFLAGS) -o $@ $^ -lX11 -lm -lpthread -lGL $(LDFLAGS) -cnping_ogl : cnping.c ping.c httping.c resolve.c +cnping_ogl : cnping.c ping.c httping.c resolve.c pinghostlist.c $(CC) $(CFLAGS) -o $@ $^ -DCNFGOGL $(CFLAGS) -lX11 -lm -lpthread $(LDFLAGS) -lGL -cnping_mac : cnping.c ping.c httping.c resolve.c +cnping_mac : cnping.c ping.c httping.c resolve.c pinghostlist.c $(CC) -o cnping $^ -x objective-c -framework Cocoa -framework QuartzCore -lm -lpthread -DOSX searchnet : ping.c searchnet.c resolve.c diff --git a/cnping.c b/cnping.c index 54e3379..3375683 100644 --- a/cnping.c +++ b/cnping.c @@ -5,6 +5,7 @@ #include #include #include +#include #if defined( WINDOWS ) || defined( WIN32 ) #ifdef _MSC_VER #define strdup _strdup @@ -33,6 +34,7 @@ #include "ping.h" #include "error_handling.h" #include "httping.h" +#include "pinghostlist.h" // #### Cross-plattform debugging #### // Windows does not print to Console, use DebugView from SysInternals to @@ -54,29 +56,25 @@ do { if (0) fprintf(stderr, __VA_ARGS__); } while (0); #endif -unsigned frames = 0; -unsigned long iframeno = 0; + short screenx, screeny; -const char * pinghost; float GuiYScaleFactor; int GuiYscaleFactorIsConstant; -double globmaxtime, globmintime = 1e20; -double globinterval, globlast; -uint64_t globalrx; -uint64_t globallost; + // Ping Data. Will be overwritten with random bytes when !DEBUG uint8_t pattern[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF}; -#define PINGCYCLEWIDTH 8192 #define TIMEOUT 4 -double PingSendTimes[PINGCYCLEWIDTH]; -double PingRecvTimes[PINGCYCLEWIDTH]; -int current_cycle = 0; +struct PingData * PingData = NULL; + +// count of hosts to ping - represents size of PingData and number of elements in pinghostList +unsigned int pinghostListSize = 0; + int ExtraPingSize; int in_histogram_mode, in_frame_mode = 1; -void HandleGotPacket( int seqno, int timeout ); +void HandleGotPacket( struct PingData * pd, int seqno, int timeout ); #if defined( WINDOWS ) || defined( WIN32 ) WSADATA wsaData; @@ -86,120 +84,128 @@ WSADATA wsaData; #define MAX_HISTO_MARKS (TIMEOUT*10000) uint64_t hist_counts[MAX_HISTO_MARKS]; -void HandleNewPacket( int seqno ) +void HandleNewPacket( struct PingData * pd, int seqno ) { double Now = OGGetAbsoluteTime(); - PingSendTimes[seqno] = Now; - PingRecvTimes[seqno] = 0; + pd->PingSendTimes[seqno] = Now; + pd->PingRecvTimes[seqno] = 0; static int timeoutmark; - while( Now - PingSendTimes[timeoutmark] > TIMEOUT ) + while( Now - pd->PingSendTimes[timeoutmark] > TIMEOUT ) { - if( PingRecvTimes[timeoutmark] < PingSendTimes[timeoutmark] ) + if( pd->PingRecvTimes[timeoutmark] < pd->PingSendTimes[timeoutmark] ) { - HandleGotPacket( timeoutmark, 1 ); + HandleGotPacket( pd, timeoutmark, 1 ); } timeoutmark++; if( timeoutmark >= PINGCYCLEWIDTH ) timeoutmark = 0; } } -void HandleGotPacket( int seqno, int timeout ) +void HandleGotPacket( struct PingData * pd, int seqno, int timeout ) { double Now = OGGetAbsoluteTime(); if( timeout ) { - if( PingRecvTimes[seqno] < -0.5 ) return; + if( pd->PingRecvTimes[seqno] < -0.5 ) return; - globallost++; - PingRecvTimes[seqno] = -1; + pd->globallost++; + pd->PingRecvTimes[seqno] = -1; hist_counts[MAX_HISTO_MARKS-1]++; return; } - if( PingRecvTimes[seqno] >= PingSendTimes[seqno] ) return; - if( PingSendTimes[seqno] < 1 ) return; - if( Now - PingSendTimes[seqno] > TIMEOUT ) return; + if( pd->PingRecvTimes[seqno] >= pd->PingSendTimes[seqno] ) return; + if( pd->PingSendTimes[seqno] < 1 ) return; + if( Now - pd->PingSendTimes[seqno] > TIMEOUT ) return; - PingRecvTimes[seqno] = OGGetAbsoluteTime(); - double Delta = PingRecvTimes[seqno] - PingSendTimes[seqno]; - if( Delta > globmaxtime ) { globmaxtime = Delta; } - if( Delta < globmintime ) { globmintime = Delta; } + pd->PingRecvTimes[seqno] = OGGetAbsoluteTime(); + double Delta = pd->PingRecvTimes[seqno] - pd->PingSendTimes[seqno]; + if( Delta > pd->globmaxtime ) { pd->globmaxtime = Delta; } + if( Delta < pd->globmintime ) { pd->globmintime = Delta; } int slot = Delta * 10000; if( slot >= MAX_HISTO_MARKS ) slot = MAX_HISTO_MARKS-1; if( slot < 0 ) slot = 0; hist_counts[slot]++; - if( globlast > 0.5 ) + if( pd->globlast > 0.5 ) { - if( Now - globlast > globinterval ) globinterval = Now - globlast; + if( Now - pd->globlast > pd->globinterval ) pd->globinterval = Now - pd->globlast; } - globlast = Now; - globalrx++; + pd->globlast = Now; + pd->globalrx++; } - -void HTTPingCallbackStart( int seqno ) +void HTTPingCallbackStart( int seqno, unsigned int pingHostId ) { - current_cycle = seqno; - HandleNewPacket( seqno ); + struct PingData * pd = PingData + pingHostId; + pd->current_cycle = seqno; + HandleNewPacket( pd, seqno ); } -void HTTPingCallbackGot( int seqno ) +void HTTPingCallbackGot( int seqno, unsigned int pingHostId ) { - HandleGotPacket( seqno, 0 ); + struct PingData * pd = PingData + pingHostId; + HandleGotPacket( pd, seqno, 0 ); } -void display(uint8_t *buf, int bytes) +void display( uint8_t *buf, int bytes, unsigned int pingHostId ) { + struct PingData * pd = PingData + pingHostId; int reqid = (buf[0] << 24) | (buf[1]<<16) | (buf[2]<<8) | (buf[3]); debug("Received ping: reqid=%d\n", reqid); reqid &= (PINGCYCLEWIDTH-1); if( memcmp( buf+4, pattern, sizeof(pattern) ) != 0 ) return; debug("Memcmp OK, checked %ld bytes, first values being %x %x %x %x\n", (long int) sizeof(pattern), pattern[0], pattern[1], pattern[2], pattern[3]) - HandleGotPacket( reqid, 0 ); + HandleGotPacket( pd, reqid, 0 ); } -int load_ping_packet( uint8_t * buffer, int bufflen ) +// returns the size of the payload +int load_ping_packet( uint8_t * buffer, int bufflen, struct PingData * pd ) { - buffer[0] = current_cycle >> 24; - buffer[1] = current_cycle >> 16; - buffer[2] = current_cycle >> 8; - buffer[3] = current_cycle >> 0; + static const uint32_t SizeOfPatternAndCycle = 4 + sizeof(pattern); + assert( bufflen >= SizeOfPatternAndCycle + ExtraPingSize ); + + buffer[0] = pd->current_cycle >> 24; + buffer[1] = pd->current_cycle >> 16; + buffer[2] = pd->current_cycle >> 8; + buffer[3] = pd->current_cycle >> 0; memcpy( buffer+4, pattern, sizeof(pattern) ); - if( ping_failed_to_send ) + if( pd->ping_failed_to_send ) { - PingSendTimes[(current_cycle+PINGCYCLEWIDTH-1)&(PINGCYCLEWIDTH-1)] = 0; //Unset ping send. + pd->PingSendTimes[(pd->current_cycle+PINGCYCLEWIDTH-1)&(PINGCYCLEWIDTH-1)] = 0; //Unset ping send. } - HandleNewPacket( current_cycle&(PINGCYCLEWIDTH-1) ); + HandleNewPacket( pd, pd->current_cycle&(PINGCYCLEWIDTH-1) ); + + pd->current_cycle++; - current_cycle++; + // zerofill the extra data + memset( buffer+SizeOfPatternAndCycle, 0, ExtraPingSize ); - return 12 + ExtraPingSize; + return SizeOfPatternAndCycle + ExtraPingSize; } void * PingListen( void * r ) { - listener(); + struct PreparedPing* pp = (struct PreparedPing*) r; + listener( pp ); ERRM( "Fault on listen.\n" ); exit( -2 ); } -void * PingSend( void * r ) +void * PingSend( void* r ) { - do_pinger( ); + struct PreparedPing* pp = (struct PreparedPing*) r; + ping( pp ); ERRM( "Fault on ping.\n" ); exit( -1 ); } - - - void HandleKey( int keycode, int bDown ) { switch( keycode ) @@ -228,12 +234,16 @@ void HandleKey( int keycode, int bDown ) break; case 'c': memset( hist_counts, 0, sizeof( hist_counts ) ); - globmaxtime = 0; - globmintime = 1e20; - globinterval = 0; - globlast = 0; - globalrx = 0; - globallost = 0; + for ( unsigned int i = 0; i < pinghostListSize; ++i ) + { + struct PingData * pd = PingData + i; + pd->globmaxtime = 0; + pd->globmintime = 1e20; + pd->globinterval = 0; + pd->globlast = 0; + pd->globalrx = 0; + pd->globallost = 0; + } break; case 'q': exit(0); @@ -246,16 +256,16 @@ void HandleMotion( int x, int y, int mask ){} void HandleDestroy() { exit(0); } -double GetWindMaxPingTime( void ) +double GetWindMaxPingTime( const struct PingData * pd ) { int i; double maxtime = 0; for( i = 0; i < screenx; i++ ) { - int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1); - double st = PingSendTimes[index]; - double rt = PingRecvTimes[index]; + int index = ((pd->current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1); + double st = pd->PingSendTimes[index]; + double rt = pd->PingRecvTimes[index]; double dt = 0; @@ -270,30 +280,39 @@ double GetWindMaxPingTime( void ) return maxtime; } -void DrawMainText( const char * stbuf ) +void DrawMainText( const char * stbuf, unsigned int yOffset ) { int x, y; CNFGColor( 0x000000ff ); for( x = -1; x < 2; x++ ) for( y = -1; y < 2; y++ ) { - CNFGPenX = 10+x; CNFGPenY = 10+y; + CNFGPenX = 10+x; CNFGPenY = 10+y + yOffset; CNFGDrawText( stbuf, 2 ); } CNFGColor( 0xffffffff ); - CNFGPenX = 10; CNFGPenY = 10; + CNFGPenX = 10; CNFGPenY = 10 + yOffset; CNFGDrawText( stbuf, 2 ); } -void DrawFrameHistogram() +void DrawFrameHistogram( const char * pinghost, unsigned int count, unsigned int pingHostId ) { int i; -// double Now = OGGetAbsoluteTime(); + const struct PingData * pd = PingData + pingHostId; const int colwid = 50; int categories = (screenx-50)/colwid; - int maxpingslot = ( globmaxtime*10000.0 ); - int minpingslot = ( globmintime*10000.0 ); + int maxpingslot = ( pd->globmaxtime*10000.0 ); + int minpingslot = ( pd->globmintime*10000.0 ); int slots = maxpingslot-minpingslot; + unsigned int assignedScreenHeight = screeny / count; + unsigned int heightOffset = assignedScreenHeight * pingHostId; + + // last pingHost gets all the extra pixels + if( pingHostId == pinghostListSize-1 ) + { + assignedScreenHeight += screeny % count; + } + if( categories <= 2 ) { goto nodata; @@ -348,9 +367,9 @@ void DrawFrameHistogram() for( i = 0; i < rslots; i++ ) { CNFGColor( 0x33cc33ff ); - int top = 30; + int top = heightOffset + 30; uint64_t samps = samples[i]; - int bottom = screeny - 50; + int bottom = heightOffset + assignedScreenHeight - 50; int height = samps?(samps * (bottom-top) / highestchart + 1):0; int startx = (i+1) * (screenx-50) / rslots; int endx = (i+2) * (screenx-50) / rslots; @@ -407,23 +426,25 @@ void DrawFrameHistogram() #else snprintf( stt, 1024, "Host: %s\nHistorical max %9.2fms\nBiggest interval%9.2fms\nHistorical packet loss %lu/%lu = %6.3f%%", #endif - pinghost, globmaxtime*1000.0, globinterval*1000.0, globallost, globalrx, globallost*100.0/(globalrx+globallost) ); + pinghost, pd->globmaxtime*1000.0, pd->globinterval*1000.0, pd->globallost, pd->globalrx, pd->globallost*100.0/(pd->globalrx+pd->globallost) ); if( !in_frame_mode ) - DrawMainText( stt ); + DrawMainText( stt, heightOffset ); return; } nodata: - DrawMainText( "No data.\n" ); + DrawMainText( "No data.\n", heightOffset ); return; } -void DrawFrame( void ) +void DrawFrame( const char * pinghost, unsigned int count, unsigned int pingHostId ) { int i; double now = OGGetAbsoluteTime(); + const struct PingData * pd = PingData + pingHostId; + double totaltime = 0; int totalcountok = 0; int totalcountloss = 0; @@ -432,13 +453,29 @@ void DrawFrame( void ) double stddev = 0; double last = -1; double loss = 100.00; - double windmaxtime = GetWindMaxPingTime(); + double windmaxtime = GetWindMaxPingTime( pd ); + + unsigned int assignedScreenHeight = screeny / count; + unsigned int heightOffset = assignedScreenHeight * pingHostId; + + // last pingHost gets all the extra pixels + if( pingHostId == pinghostListSize-1 ) + { + assignedScreenHeight += screeny % count; + } + + unsigned int heightOffsetBottom = heightOffset + assignedScreenHeight; + + if (!GuiYscaleFactorIsConstant) + { + GuiYScaleFactor = (assignedScreenHeight - 50) / windmaxtime; + } for( i = 0; i < screenx; i++ ) { - int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1); - double st = PingSendTimes[index]; - double rt = PingRecvTimes[index]; + int index = ((pd->current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1); + double st = pd->PingSendTimes[index]; + double rt = pd->PingRecvTimes[index]; double dt = 0; @@ -467,15 +504,10 @@ void DrawFrame( void ) dt = 99 * 1000; // assume 99s to fill screen black } - if (!GuiYscaleFactorIsConstant) - { - GuiYScaleFactor = (screeny - 50) / windmaxtime; - } - int h = dt*GuiYScaleFactor; - int top = screeny - h; + int top = assignedScreenHeight - h; if( top < 0 ) top = 0; - CNFGTackSegment( i, screeny-1, i, top ); + CNFGTackSegment( i, heightOffset+assignedScreenHeight-1, i, heightOffset+top ); } double avg = totaltime / totalcountok; @@ -483,9 +515,9 @@ void DrawFrame( void ) for( i = 0; i < screenx; i++ ) { - int index = ((current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1); - double st = PingSendTimes[index]; - double rt = PingRecvTimes[index]; + int index = ((pd->current_cycle - i - 1) + PINGCYCLEWIDTH) & (PINGCYCLEWIDTH-1); + double st = pd->PingSendTimes[index]; + double rt = pd->PingRecvTimes[index]; double dt = 0; if( rt > st ) @@ -503,20 +535,21 @@ void DrawFrame( void ) int avg_gui = avg*GuiYScaleFactor; int stddev_gui = stddev*GuiYScaleFactor; - CNFGColor( 0x00ff00ff ); + CNFGColor( 0x00ff00ff ); + // the 3 green lines int l = avg_gui; - CNFGTackSegment( 0, screeny-l, screenx, screeny - l ); + CNFGTackSegment( 0, heightOffsetBottom-l, screenx, heightOffsetBottom - l ); l = (avg_gui) + (stddev_gui); - CNFGTackSegment( 0, screeny-l, screenx, screeny - l ); + CNFGTackSegment( 0, heightOffsetBottom-l, screenx, heightOffsetBottom - l ); l = (avg_gui) - (stddev_gui); - CNFGTackSegment( 0, screeny-l, screenx, screeny - l ); + CNFGTackSegment( 0, heightOffsetBottom-l, screenx, heightOffsetBottom - l ); char stbuf[2048]; char * sptr = &stbuf[0]; - sptr += sprintf( sptr, + sptr += sprintf( sptr, "Last:%6.2f ms Host: %s\n" "Min :%6.2f ms\n" "Max :%6.2f ms Historical max: %5.2f ms\n" @@ -526,10 +559,10 @@ void DrawFrame( void ) #else "Std :%6.2f ms Historical loss: %lu/%lu %5.3f%%\n" #endif - "Loss:%6.1f %%", last, pinghost, mintime, maxtime, globmaxtime*1000, avg, globinterval*1000.0, stddev, - globallost, globalrx+globallost, globallost*100.0f/(globalrx+globallost), loss ); + "Loss:%6.1f %%", last, pinghost, mintime, maxtime, pd->globmaxtime*1000, avg, pd->globinterval*1000.0, stddev, + pd->globallost, pd->globalrx+pd->globallost, pd->globallost*100.0f/(pd->globalrx+pd->globallost), loss ); - DrawMainText( stbuf ); + DrawMainText( stbuf, heightOffset ); OGUSleep( 1000 ); } @@ -641,6 +674,7 @@ INT_PTR CALLBACK TextEntry( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l return 0; } #endif + int main( int argc, const char ** argv ) { char title[1024]; @@ -652,6 +686,10 @@ int main( int argc, const char ** argv ) double frameperiodseconds; const char * device = NULL; + // linked list of all hosts that should be pinged + struct PingHost * pinghostList = NULL; + pinghostListSize = 0; + #ifdef WIN32 ShowWindow (GetConsoleWindow(), SW_HIDE); #endif @@ -697,7 +735,7 @@ int main( int argc, const char ** argv ) //Parameter-based field. switch( thisargv[1] ) { - case 'h': pinghost = nextargv; break; + case 'h': appendPingHost( &pinghostList, &pinghostListSize, nextargv ); break; case 'p': pingperiodseconds = atof( nextargv ); break; case 's': ExtraPingSize = atoi( nextargv ); break; case 'y': GuiYScaleFactor = atof( nextargv ); break; @@ -712,7 +750,7 @@ int main( int argc, const char ** argv ) //Unmarked fields switch( argcunmarked++ ) { - case 1: pinghost = thisargv; break; + case 1: appendPingHost( &pinghostList, &pinghostListSize, thisargv ); break; case 2: pingperiodseconds = atof( thisargv ); break; case 3: ExtraPingSize = atoi( thisargv ); break; case 4: GuiYScaleFactor = atof( thisargv ); break; @@ -722,17 +760,12 @@ int main( int argc, const char ** argv ) } } - if( title[0] == 0 ) - { - sprintf( title, "%s - cnping "VERSION, pinghost ); - } - if( GuiYScaleFactor > 0 ) { GuiYscaleFactorIsConstant = 1; } - if( !pinghost ) + if( !pinghostList ) { displayhelp = 1; } @@ -746,52 +779,88 @@ int main( int argc, const char ** argv ) " (-y) [const y-axis scaling] -- use a fixed scaling factor instead of auto scaling (optional)\n" " (-t) [window title] -- the title of the window (optional)\n" " (-I) [interface] -- Sets source interface (i.e. eth0)\n"); + + freePingHostList( &pinghostList, &pinghostListSize ); return -1; } + if( title[0] == 0 ) + { + sprintf( title, "%s - cnping "VERSION, pinghostList->host ); + } + #if defined( WIN32 ) || defined( WINDOWS ) if(device) { ERRM("Error: Device option is not implemented on your platform. PRs welcome.\n"); + freePingHostList( &pinghostList, &pinghostListSize ); exit( -1 ); } - + if( WSAStartup(MAKEWORD(2,2), &wsaData) ) { ERRM( "Fault in WSAStartup\n" ); + freePingHostList( &pinghostList, &pinghostListSize ); exit( -2 ); } - CNFGSetup( title, 320, 155 ); + CNFGSetup( title, 320, 155 * pinghostListSize ); #else - CNFGSetupWMClass( title, 320, 155, "cnping", "cnping" ); + CNFGSetupWMClass( title, 320, 155 * pinghostListSize, "cnping", "cnping" ); #endif - - if( memcmp( pinghost, "http://", 7 ) == 0 ) + // allocate pingdata; calloc gurantees zero filled memory + PingData = calloc( pinghostListSize, sizeof(struct PingData) ); + for( unsigned int i = 0; i < pinghostListSize; ++i ) { - StartHTTPing( pinghost+7, pingperiodseconds, device ); + PingData[i].globmaxtime = 0; + PingData[i].globmintime = 1e20; + PingData[i].pp = NULL; } - else + + // iterate over all ping hosts and create ping threads for them + unsigned int pingHostId = 0; + struct PingHost * current = pinghostList; + for ( ; current ; current = current->next ) { - char* protoEnd = strstr( pinghost, "://" ); - if ( protoEnd ) + if( memcmp( current->host, "http://", 7 ) == 0 ) { - int protoSize = protoEnd - pinghost; - char protoBuffer[protoSize + 1]; - memcpy( protoBuffer, pinghost, protoSize ); - protoBuffer[protoSize] = '\0'; - ERRM( "Protocol \"%s\" is not supported\n", protoBuffer ); - exit( -1 ); + StartHTTPing( current->host+7, pingperiodseconds, device, pingHostId ); } + else + { + char* protoEnd = strstr( current->host, "://" ); + if ( protoEnd ) + { + int protoSize = protoEnd - current->host; + char protoBuffer[protoSize + 1]; + memcpy( protoBuffer, current->host, protoSize ); + protoBuffer[protoSize] = '\0'; + ERRM( "Protocol \"%s\" is not supported\n", protoBuffer ); - ping_setup( pinghost, device ); - OGCreateThread( PingSend, 0 ); - OGCreateThread( PingListen, 0 ); + // could this lead to a crash? another thread might already access memory from the list + freePingHostList( &pinghostList, &pinghostListSize ); + + exit( -1 ); + } + + struct PreparedPing* pp = ping_setup( current->host, device ); + if (pp) + { + pp->pingHostId = pingHostId; + OGCreateThread( PingSend, pp ); + OGCreateThread( PingListen, pp ); + PingData[pingHostId].pp = pp; + } + } + + pingHostId++; } - frameperiodseconds = fmin(.2, fmax(.03, pingperiodseconds)); + frameperiodseconds = fmin(.2, fmax(.03, pingperiodseconds) ); + unsigned frames = 0; + unsigned long iframeno = 0; while(1) { iframeno++; @@ -801,14 +870,22 @@ int main( int argc, const char ** argv ) CNFGColor( 0xffffffff ); CNFGGetDimensions( &screenx, &screeny ); - if( in_frame_mode ) + // iterate over all ping hosts and create ping threads for them + unsigned int pingHostId = 0; + struct PingHost * current = pinghostList; + for ( ; current ; current = current->next ) { - DrawFrame(); - } + if( in_frame_mode ) + { + DrawFrame( current->host, pinghostListSize, pingHostId ); + } - if( in_histogram_mode ) - { - DrawFrameHistogram(); + if( in_histogram_mode ) + { + DrawFrameHistogram( current->host, pinghostListSize, pingHostId ); + } + + pingHostId++; } CNFGPenX = 100; CNFGPenY = 100; @@ -833,6 +910,18 @@ int main( int argc, const char ** argv ) OGUSleep( (int)( SecToWait * 1000000 ) ); } + freePingHostList( &pinghostList, &pinghostListSize ); + + // free the PingData array + for( int pingHostId = 0; pingHostId < pinghostListSize; ++pingHostId ) + { + if(PingData[pingHostId].pp) + { + free(PingData[pingHostId].pp); + } + } + free( PingData ); + return(0); } diff --git a/httping.c b/httping.c index 14b569b..2149719 100644 --- a/httping.c +++ b/httping.c @@ -35,12 +35,8 @@ #define HTTPTIMEOUT 3.0 -//Callbacks (when started/received) -void HTTPingCallbackStart( int seqno ); -void HTTPingCallbackGot( int seqno ); - //Don't dynamically allocate resources here, since execution may be stopped arbitrarily. -void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile double * timeouttime, int * socketptr, volatile int * getting_host_by_name, const char * device) +void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile double * timeouttime, int * socketptr, volatile int * getting_host_by_name, const char * device, unsigned int pingHostId ) { #if defined(WIN32) || defined(WINDOWS) (void) device; // option is not available for windows. Suppress unused warning. @@ -138,7 +134,7 @@ void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile do int n = sprintf( buf, "HEAD %s HTTP/1.1\r\nConnection: keep-alive\r\nHost: %s\r\n\r\n", eurl?eurl:"/favicon.ico", hostname ); (*seqnoptr) ++; - HTTPingCallbackStart( *seqnoptr ); + HTTPingCallbackStart( *seqnoptr, pingHostId ); int rs = send( httpsock, buf, n, MSG_NOSIGNAL ); double starttime = *timeouttime = OGGetAbsoluteTime(); @@ -173,7 +169,7 @@ void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile do } *timeouttime = OGGetAbsoluteTime(); - HTTPingCallbackGot( *seqnoptr ); + HTTPingCallbackGot( *seqnoptr, pingHostId ); double delay_time = minperiod - (*timeouttime - starttime); if( delay_time > 0 ) @@ -194,10 +190,13 @@ void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile do struct HTTPPingLaunch { + // arguments const char * addy; double minperiod; const char * device; + unsigned int pingHostId; + // internal volatile double timeout_time; volatile int failed; int seqno; @@ -210,7 +209,7 @@ static void * DeployPing( void * v ) struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v; hpl->socket = 0; hpl->getting_host_by_name = 0; - DoHTTPing( hpl->addy, hpl->minperiod, &hpl->seqno, &hpl->timeout_time, &hpl->socket, &hpl->getting_host_by_name, hpl->device ); + DoHTTPing( hpl->addy, hpl->minperiod, &hpl->seqno, &hpl->timeout_time, &hpl->socket, &hpl->getting_host_by_name, hpl->device, hpl->pingHostId ); hpl->failed = 1; return 0; } @@ -250,15 +249,14 @@ static void * PingRunner( void * v ) return 0; } -int StartHTTPing( const char * addy, double minperiod, const char * device) +int StartHTTPing( const char * addy, double minperiod, const char * device, unsigned int pingHostId ) { struct HTTPPingLaunch *hpl = malloc( sizeof( struct HTTPPingLaunch ) ); hpl->addy = addy; hpl->minperiod = minperiod; hpl->device = device; + hpl->pingHostId = pingHostId; OGCreateThread( PingRunner, hpl ); return 0; } - - diff --git a/httping.h b/httping.h index 8e65f87..0b9e35b 100644 --- a/httping.h +++ b/httping.h @@ -2,11 +2,11 @@ #define _HTTPING_H //Callbacks (when started/received) -void HTTPingCallbackStart( int seqno ); -void HTTPingCallbackGot( int seqno ); +void HTTPingCallbackStart( int seqno, unsigned int pingHostId ); +void HTTPingCallbackGot( int seqno, unsigned int pingHostId ); //addy should be google.com/blah or something like that. Do not include prefixing http://. Port numbers OK. -int StartHTTPing( const char * addy, double minperiod, const char * device); +int StartHTTPing( const char * addy, double minperiod, const char * device, unsigned int pingHostId ); #endif diff --git a/ping.c b/ping.c index b7cebe8..23274d5 100644 --- a/ping.c +++ b/ping.c @@ -17,11 +17,8 @@ #include "tccheader.h" #endif -int ping_failed_to_send; float pingperiodseconds; int precise_ping; -struct sockaddr_in6 psaddr; -socklen_t psaddr_len; int using_regular_ping; #ifdef WIN_USE_NO_ADMIN_PING @@ -38,6 +35,12 @@ int using_regular_ping; #include "rawdraw/os_generic.h" +// used to pack arguments for windows ping threads +struct WindowsPingArgs +{ + struct PreparedPing* pp; + HANDLE icmpHandle; +}; #define MAX_PING_SIZE 16384 @@ -48,30 +51,29 @@ int using_regular_ping; #pragma comment(lib, "iphlpapi.lib") #endif -static og_sema_t s_disp; -static og_sema_t s_ping; - -void ping_setup(const char * strhost, const char * device) +struct PreparedPing* ping_setup(const char * strhost, const char * device) { + struct PreparedPing* pp = (struct PreparedPing*) malloc(sizeof(struct PreparedPing)); + + memset(&pp->psaddr, 0, sizeof(pp->psaddr)); + pp->psaddr_len = sizeof(pp->psaddr); + // resolve host - psaddr_len = sizeof(psaddr); if( strhost ) - resolveName((struct sockaddr*) &psaddr, &psaddr_len, strhost, AF_INET); // only resolve ipv4 on windows + resolveName((struct sockaddr*) &pp->psaddr, &pp->psaddr_len, strhost, AF_INET); // only resolve ipv4 on windows else - psaddr.sin6_family = AF_INET; + pp->psaddr.sin6_family = AF_INET; - s_disp = OGCreateSema(); - s_ping = OGCreateSema(); + pp->s_disp = OGCreateSema(); + pp->s_ping = OGCreateSema(); //This function is executed first. + + return pp; } -void listener() +void listener( struct PreparedPing* pp ) { - static uint8_t listth; - if( listth ) return; - listth = 1; - - OGUnlockSema( s_disp ); + OGUnlockSema( pp->s_disp ); //Normally needs to call display(buf + 28, bytes - 28 ); on successful ping. //This function is executed as a thread after setup. //Really, we just use the s_disp semaphore to make sure we only launch disp's at correct times. @@ -80,19 +82,21 @@ void listener() return; } -static HANDLE pinghandles[PINGTHREADS]; - static void * pingerthread( void * v ) { uint8_t ping_payload[MAX_PING_SIZE]; - HANDLE ih = *((HANDLE*)v); + // copy arguments and free struct + struct WindowsPingArgs* args = (struct WindowsPingArgs*) v; + struct PreparedPing* pp = args->pp; + HANDLE ih = args->icmpHandle; + free(args); int timeout_ms = pingperiodseconds * (PINGTHREADS-1) * 1000; while(1) { - OGLockSema( s_ping ); - int rl = load_ping_packet( ping_payload, sizeof( ping_payload ) ); + OGLockSema( pp->s_ping ); + int rl = load_ping_packet( ping_payload, sizeof( ping_payload ), PingData + pp->pingHostId ); struct repl_t { ICMP_ECHO_REPLY rply; @@ -100,12 +104,12 @@ static void * pingerthread( void * v ) } repl; DWORD res = IcmpSendEcho( ih, - ((struct sockaddr_in*) &psaddr)->sin_addr.s_addr, ping_payload, rl, + ((struct sockaddr_in*) &pp->psaddr)->sin_addr.s_addr, ping_payload, rl, 0, &repl, sizeof( repl ), timeout_ms ); int err; if( !res ) err = GetLastError(); - OGLockSema( s_disp ); + OGLockSema( pp->s_disp ); if( !res ) { @@ -120,90 +124,78 @@ static void * pingerthread( void * v ) } if( res ) { - display( repl.rply.Data, rl ); + display( repl.rply.Data, rl, pp->pingHostId ); } - OGUnlockSema( s_disp ); + OGUnlockSema( pp->s_disp ); } return 0; } -void singleping(struct sockaddr *addr, socklen_t addr_len ) +// create PINGTHREADS amount of pingerthread +void createThreads( struct PreparedPing* pp ) { - int i; - (void) addr; - (void) addr_len; + for( int i = 0; i < PINGTHREADS; i++ ) + { + HANDLE ih = IcmpCreateFile(); + if( ih == INVALID_HANDLE_VALUE ) + { + ERRM( "Cannot create ICMP thread %d\n", i ); + exit( 0 ); + } + + struct WindowsPingArgs* args = (struct WindowsPingArgs*) malloc(sizeof(struct WindowsPingArgs)); + args->pp = pp; + args->icmpHandle = ih; + OGCreateThread( pingerthread, args ); + } +} - if( psaddr.sin6_family != AF_INET ) +// ipv6 ping is not supported on windows -> exit if ipv6 is required +void checkIPv6( int family ) +{ + if( family != AF_INET ) { - // ipv6 ICMP Ping is not supported on windows + // ipv6 ICMP Ping is not implemented on windows - PRs welcome ERRM( "ERROR: ipv6 ICMP Ping is not supported on windows\n" ); exit( -1 ); } +} + +void singleping( struct PreparedPing* pp ) +{ + checkIPv6( pp->psaddr.sin6_family ); static int did_init_threads = 0; if( !did_init_threads ) { did_init_threads = 1; //Launch pinger threads - for( i = 0; i < PINGTHREADS; i++ ) - { - HANDLE ih = pinghandles[i] = IcmpCreateFile(); - if( ih == INVALID_HANDLE_VALUE ) - { - ERRM( "Cannot create ICMP thread %d\n", i ); - exit( 0 ); - } - - OGCreateThread( pingerthread, &pinghandles[i] ); - } + createThreads( pp ); } //This function is executed as a thread after setup. - if( i >= PINGTHREADS-1 ) i = 0; - else i++; - OGUnlockSema( s_ping ); + OGUnlockSema( pp->s_ping ); } -void ping(struct sockaddr *addr, socklen_t addr_len ) +void ping( struct PreparedPing* pp ) { - int i; - (void) addr; - (void) addr_len; - using_regular_ping = 1; - if( psaddr.sin6_family != AF_INET ) - { - // ipv6 ICMP Ping is not supported on windows - ERRM( "ERROR: ipv6 ICMP Ping is not supported on windows\n" ); - exit( -1 ); - } + checkIPv6( pp->psaddr.sin6_family ); //Launch pinger threads - for( i = 0; i < PINGTHREADS; i++ ) - { - HANDLE ih = pinghandles[i] = IcmpCreateFile(); - if( ih == INVALID_HANDLE_VALUE ) - { - ERRM( "Cannot create ICMP thread %d\n", i ); - exit( 0 ); - } + createThreads( pp ); - OGCreateThread( pingerthread, &pinghandles[i] ); - } //This function is executed as a thread after setup. while(1) { - if( i >= PINGTHREADS-1 ) i = 0; - else i++; - OGUnlockSema( s_ping ); + OGUnlockSema( pp->s_ping ); OGUSleep( (int)(pingperiodseconds * 1000000) ); } } - #else // ! WIN_USE_NO_ADMIN_PING //The normal way to do it - only problem is Windows needs admin privs. @@ -264,6 +256,7 @@ struct icmphdr } frag; } un; }; +#undef min #endif @@ -280,9 +273,13 @@ struct packet #endif }; -int sd; int pid=-1; +int min( int a, int b ) +{ + return a < b ? a : b; +} + uint16_t checksum( const unsigned char * start, uint16_t len ) { uint16_t i; @@ -298,34 +295,79 @@ uint16_t checksum( const unsigned char * start, uint16_t len ) return ~csum; } + +#ifdef WIN32 +void setTTL( int sock, int family ) +{ + (void) sock; + (void) family; + // nop on win +} + +#else // setsockopt TTL to 255 -void setTTL(int sock) +void setTTL( int sock, int family ) { - const int val=255; + static const int val=255; - assert(psaddr.sin6_family == AF_INET || psaddr.sin6_family == AF_INET6); + assert(family == AF_INET || family == AF_INET6); - if ( setsockopt(sd, (psaddr.sin6_family == AF_INET) ? SOL_IP : SOL_IPV6, IP_TTL, &val, sizeof(val)) != 0) + if ( setsockopt( sock, (family == AF_INET) ? SOL_IP : SOL_IPV6, IP_TTL, &val, sizeof(val)) != 0) { ERRM("Error: Failed to set TTL option. Are you root? Or can do sock_raw sockets?\n"); exit( -1 ); } } +#endif + +void setNoBlock( int sock ) +{ +#ifdef WIN32 + { + //Setup windows socket for nonblocking io. + unsigned long iMode = 1; + ioctlsocket(sock, FIONBIO, &iMode); + } +#else + if ( fcntl(sock, F_SETFL, O_NONBLOCK) != 0 ) + ERRM("Warning: Request nonblocking I/O failed."); +#endif +} + +// returns 1 = success; 0 = failed +int bindDevice( int sock, const char* device ) +{ +#ifdef WIN32 + (void) sock; + (void) device; + +#else + if(device) + { + if( setsockopt( sock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) +1 ) != 0 ) + { + return 0; + } + } +#endif + return 1; +} // 0 = failed, 1 = this is a ICMP Response -int isICMPResponse(unsigned char* buf, int bytes) +int isICMPResponse( int family, unsigned char* buf, int bytes ) { - assert(psaddr.sin6_family == AF_INET || psaddr.sin6_family == AF_INET6); + if( family != AF_INET && family != AF_INET6 ) return 0; - if( bytes == -1 ) return 0; + if( bytes < 1 ) return 0; - if( psaddr.sin6_family == AF_INET ) // ipv4 compare + if( family == AF_INET ) // ipv4 compare { + if( bytes < 21 ) return 0; if( buf[9] != IPPROTO_ICMP ) return 0; if( buf[20] != ICMP_ECHOREPLY ) return 0; } - else if( psaddr.sin6_family == AF_INET6 ) // ipv6 compare + else if( family == AF_INET6 ) // ipv6 compare { if( buf[0] != ICMP6_ECHO_REPLY ) return 0; } @@ -333,56 +375,68 @@ int isICMPResponse(unsigned char* buf, int bytes) return 1; } -int createSocket() +int createSocket( int family ) { - if( psaddr.sin6_family == AF_INET ) +#ifdef WIN32 + // no ipv6 support for windows + int fd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, WSA_FLAG_OVERLAPPED); + { + static const int lttl = 0xff; + if (setsockopt(fd, IPPROTO_IP, IP_TTL, (const char*)<tl, sizeof(lttl)) == SOCKET_ERROR) + { + printf( "Warning: No IP_TTL.\n" ); + } + } + return fd; +#else + if( family == AF_INET ) { return socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); } - else if( psaddr.sin6_family == AF_INET6 ) + else if( family == AF_INET6 ) { return socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); } // invalid af_family return -1; +#endif } -void listener() +void listener( struct PreparedPing* pp ) { -#ifndef WIN32 - int sd = createSocket(); + int listenSock = createSocket( pp->psaddr.sin6_family ); - setTTL(sd); -#endif + setTTL( listenSock, pp->psaddr.sin6_family ); - struct sockaddr_in6 addr; + struct sockaddr_in6 recvFromAddr; unsigned char buf[66000]; #ifdef WIN32 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); #endif for (;;) { - socklen_t addrlenval=sizeof(addr); - int bytes; + socklen_t recvFromAddrLen = sizeof(recvFromAddr); #ifdef WIN32 WSAPOLLFD fda[1]; - fda[0].fd = sd; + fda[0].fd = listenSock; fda[0].events = POLLIN; WSAPoll(fda, 1, 10); #endif - keep_retry_quick: - bytes = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlenval ); - if( !isICMPResponse(buf, bytes) ) continue; + int bytes; + +keep_retry_quick: + bytes = recvfrom( listenSock, (void*) buf, sizeof(buf), 0, (struct sockaddr*)&recvFromAddr, &recvFromAddrLen ); + if( !isICMPResponse( recvFromAddr.sin6_family, buf, bytes) ) continue; // compare the sender - if( using_regular_ping && memcmp(&addr, &psaddr, addrlenval) != 0 ) continue; + if( using_regular_ping && memcmp(&recvFromAddr, &pp->psaddr, min(recvFromAddrLen, pp->psaddr_len) ) != 0 ) continue; // sizeof(packet.hdr) + 20 int offset = 0; - if(addr.sin6_family == AF_INET) // ipv4 + if( recvFromAddr.sin6_family == AF_INET ) // ipv4 { #ifdef __FreeBSD__ offset = 48; @@ -396,7 +450,7 @@ void listener() } if ( bytes > 0 ) - display(buf + offset, bytes - offset ); + display(buf + offset, bytes - offset, pp->pingHostId ); else { ERRM("Error: recvfrom failed"); @@ -409,110 +463,76 @@ void listener() exit( 0 ); } -void singleping(struct sockaddr *addr, socklen_t addr_len ) +// fill a packet with data ready to be written to a raw socket +// returns the size of the packet +int constructPack(struct packet* pckt, int family, int pingHostId, int cnt) { - int cnt=1; + int rsize = load_ping_packet( pckt->msg, sizeof( pckt->msg ), PingData + pingHostId ); -#ifdef WIN32 - { - //Setup windows socket for nonblocking io. - unsigned long iMode = 1; - ioctlsocket(sd, FIONBIO, &iMode); - } -#else - if ( fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ) - ERRM("Warning: Request nonblocking I/O failed."); -#endif + // add the size of the header + rsize += sizeof( pckt->hdr ); - double stime = OGGetAbsoluteTime(); - - struct packet pckt; + // checksum is calculated, with zerofilled checksum field + pckt->hdr.checksum = 0; - { - int rsize = load_ping_packet( pckt.msg, sizeof( pckt.msg ) ); - memset( &pckt.hdr, 0, sizeof( pckt.hdr ) ); //This needs to be here, but I don't know why, since I think the struct is fully populated. + const uint8_t icmp_type = (family == AF_INET) ? ICMP_ECHO : ICMP6_ECHO_REQUEST; #ifdef __FreeBSD__ - pckt.hdr.icmp_code = 0; - pckt.hdr.icmp_type = ICMP_ECHO; - pckt.hdr.icmp_id = pid; - pckt.hdr.icmp_seq = cnt++; - pckt.hdr.icmp_cksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize ); + pckt->hdr.icmp_code = 0; + pckt->hdr.icmp_type = icmp_type; + pckt->hdr.icmp_id = pid; + pckt->hdr.icmp_seq = cnt; + pckt->hdr.icmp_cksum = checksum((const unsigned char *) pckt, rsize); #else - pckt.hdr.code = 0; - pckt.hdr.type = (psaddr.sin6_family == AF_INET) ? ICMP_ECHO : ICMP6_ECHO_REQUEST; - pckt.hdr.un.echo.id = pid; - pckt.hdr.un.echo.sequence = cnt++; - pckt.hdr.checksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize ); + pckt->hdr.code = 0; + pckt->hdr.type = icmp_type; + pckt->hdr.un.echo.id = pid; + pckt->hdr.un.echo.sequence = cnt; + pckt->hdr.checksum = checksum((const unsigned char *) pckt, rsize); #endif + return rsize; +} - int sr = sendto(sd, (char*)&pckt, sizeof( pckt.hdr ) + rsize , 0, addr, addr_len); +void sendOnePing( struct PreparedPing* pp , int count ) +{ + struct packet pckt; - if( sr <= 0 ) - { - ping_failed_to_send = 1; - if( using_regular_ping ) - { - ERRMB("Ping send failed:\n%s (%d)\n", strerror(errno), errno); - } - } - else + int rsize = constructPack( &pckt, pp->psaddr.sin6_family, pp->pingHostId, count ); + int sr = sendto(pp->fd, (char*)&pckt, rsize , 0, (const struct sockaddr*) &pp->psaddr, pp->psaddr_len); + + struct PingData* pd = PingData + pp->pingHostId; + if( sr <= 0 ) + { + pd->ping_failed_to_send = 1; + if( using_regular_ping ) { - ping_failed_to_send = 0; + ERRMB("Ping send failed:\n%s (%d)\n", strerror(errno), errno); } - } + } + else + { + pd->ping_failed_to_send = 0; + } } -void ping(struct sockaddr *addr, socklen_t addr_len ) +void singleping( struct PreparedPing* pp ) +{ + setNoBlock( pp->fd ); + + sendOnePing( pp, 1 ); +} + +void ping( struct PreparedPing* pp ) { int cnt=1; using_regular_ping = 1; -#ifdef WIN32 - { - //Setup windows socket for nonblocking io. - unsigned long iMode = 1; - ioctlsocket(sd, FIONBIO, &iMode); - } -#else - if ( fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ) - ERRM("Warning: Request nonblocking I/O failed."); -#endif + setNoBlock( pp->fd ); double stime = OGGetAbsoluteTime(); - struct packet pckt; do { - int rsize = load_ping_packet( pckt.msg, sizeof( pckt.msg ) ); - memset( &pckt.hdr, 0, sizeof( pckt.hdr ) ); //This needs to be here, but I don't know why, since I think the struct is fully populated. -#ifdef __FreeBSD__ - pckt.hdr.icmp_code = 0; - pckt.hdr.icmp_type = ICMP_ECHO; - pckt.hdr.icmp_id = pid; - pckt.hdr.icmp_seq = cnt++; - pckt.hdr.icmp_cksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize ); -#else - pckt.hdr.code = 0; - pckt.hdr.type = (psaddr.sin6_family == AF_INET) ? ICMP_ECHO : ICMP6_ECHO_REQUEST; - pckt.hdr.un.echo.id = pid; - pckt.hdr.un.echo.sequence = cnt++; - pckt.hdr.checksum = checksum((const unsigned char *)&pckt, sizeof( pckt.hdr ) + rsize ); -#endif - - int sr = sendto(sd, (char*)&pckt, sizeof( pckt.hdr ) + rsize , 0, addr, addr_len); - - if( sr <= 0 ) - { - ping_failed_to_send = 1; - if( using_regular_ping ) - { - ERRMB("Ping send failed:\n%s (%d)\n", strerror(errno), errno); - } - } - else - { - ping_failed_to_send = 0; - } + sendOnePing( pp, cnt++ ); if( precise_ping ) { @@ -532,73 +552,48 @@ void ping(struct sockaddr *addr, socklen_t addr_len ) OGUSleep( dlw ); } } - } while( pingperiodseconds >= 0 ); + } while( pingperiodseconds >= 0 ); //close( sd ); //Hacky, we don't close here because SD doesn't come from here, rather from ping_setup. We may want to run this multiple times. } -void ping_setup(const char * strhost, const char * device) +struct PreparedPing* ping_setup(const char * strhost, const char * device) { pid = getpid(); -#ifdef WIN32 - WSADATA wsaData; - int r = WSAStartup(MAKEWORD(2,2), &wsaData); - if( r ) - { - ERRM( "Fault in WSAStartup\n" ); - exit( -2 ); - } -#endif + struct PreparedPing* pp = (struct PreparedPing*) malloc(sizeof(struct PreparedPing)); + memset(&pp->psaddr, 0, sizeof(pp->psaddr)); + pp->psaddr_len = sizeof(pp->psaddr); + pp->psaddr.sin6_family = AF_INET; -#ifdef WIN32 - sd = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 0, WSA_FLAG_OVERLAPPED); - { - int lttl = 0xff; - if (setsockopt(sd, IPPROTO_IP, IP_TTL, (const char*)<tl, sizeof(lttl)) == SOCKET_ERROR) - { - printf( "Warning: No IP_TTL.\n" ); - } - } -#else // resolve host - psaddr_len = sizeof(psaddr); - if( strhost ) - resolveName((struct sockaddr*) &psaddr, &psaddr_len, strhost, AF_UNSPEC); - else - psaddr.sin6_family = AF_INET; - - sd = createSocket(); + { + resolveName((struct sockaddr*) &pp->psaddr, &pp->psaddr_len, strhost, AF_UNSPEC); + } - setTTL(sd); + pp->fd = createSocket( pp->psaddr.sin6_family ); + setTTL(pp->fd, pp->psaddr.sin6_family); - if(device) + if ( pp->fd < 0 ) { - if( setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) +1) != 0) - { - ERRM("Error: Failed to set Device option. Are you root? Or can do sock_raw sockets?\n"); - exit( -1 ); - } + ERRM("Error: Could not create raw socket\n"); + free(pp); + exit( -2 ); } -#endif - if ( sd < 0 ) + if( 0 == bindDevice( pp->fd, device ) ) { - ERRM("Error: Could not create raw socket\n"); - exit(0); + ERRM("Error: Failed to set Device option. Are you root? Or can do sock_raw sockets?\n"); + free( pp ); + exit( -1 ); } + return pp; } #endif // WIN_USE_NO_ADMIN_PING - -void do_pinger( ) -{ - ping((struct sockaddr*) &psaddr, psaddr_len ); -} - // used by the ERRMB makro from error_handling.h char errbuffer[1024]; diff --git a/ping.h b/ping.h index 2975353..5ae6ea7 100644 --- a/ping.h +++ b/ping.h @@ -5,30 +5,87 @@ #ifdef WIN32 typedef int socklen_t; struct sockaddr; + #include + #include + + // ipv6 icmp ping is not supported on windows anyways + // this is just needed so it compiles + #define ICMP6_ECHO_REQUEST 128 + #define ICMP6_ECHO_REPLY 129 + + #define ICMP_ECHOREPLY ICMP_ECHO #else #include + #include +#endif + +#ifdef WIN_USE_NO_ADMIN_PING + #include "rawdraw/os_generic.h" #endif +// ping data of one host +#define PINGCYCLEWIDTH 8192 + +struct PreparedPing +{ + // socket for sending the pings + int fd; + + // the number of this host -> used to access the PingData array + int pingHostId; + + // address to send the pings to + struct sockaddr_in6 psaddr; + // size of psaddr + socklen_t psaddr_len; + +#ifdef WIN_USE_NO_ADMIN_PING + og_sema_t s_disp; + og_sema_t s_ping; +#endif +}; + +// meassured ping data of one host. This is the meassured data cnping displays +struct PingData +{ + double PingSendTimes [PINGCYCLEWIDTH]; + double PingRecvTimes [PINGCYCLEWIDTH]; + int current_cycle; + double globmaxtime, globmintime; + double globinterval, globlast; + uint64_t globalrx; + uint64_t globallost; + int ping_failed_to_send; + + // pointer to the related prepared ping so it can be freed at exit + struct PreparedPing* pp; +}; + +// captured ping data - This is an array that get allocated with the size of pinghostListSize +// each Ping thread should use pingHostId as offset into this array +extern struct PingData * PingData; + unsigned short checksum(const unsigned char *b, uint16_t len); +int createSocket( int family ); + //Callback (when received) -void display(uint8_t *buf, int bytes); +void display( uint8_t *buf, int bytes, unsigned int pingHostId ); //Callback (before sending) //return value = # of bytes to send in ping message. -int load_ping_packet( uint8_t * buffer, int buffersize ); +int load_ping_packet( uint8_t * buffer, int buffersize, struct PingData * pd ); -void listener(); -void ping(struct sockaddr *addr, socklen_t addr_len ); -void do_pinger( ); +void listener( struct PreparedPing* pp ); +void ping( struct PreparedPing* pp ); -void singleping(struct sockaddr *addr, socklen_t addr_len ); // If using this, must call ping_setup( 0, 0); to begin. +void singleping( struct PreparedPing* pp ); // If using this, must call ping_setup( 0, 0); to begin. //If pingperiodseconds = -1, run ping/do_pinger once and exit. extern float pingperiodseconds; -extern int precise_ping; //if 0, use minimal CPU, but ping send-outs are only approximate, if 1, spinlock until precise time for ping is hit. -extern int ping_failed_to_send; -void ping_setup(const char * strhost, const char * device); + +// resolvs the hostname and prepares the socket. Might return NULL on error +struct PreparedPing* ping_setup(const char * strhost, const char * device); #endif diff --git a/pinghostlist.c b/pinghostlist.c new file mode 100644 index 0000000..bac0588 --- /dev/null +++ b/pinghostlist.c @@ -0,0 +1,48 @@ +#include "pinghostlist.h" + +#include +#include + +void appendPingHost( struct PingHost ** list, unsigned int * listSize, const char * newEntryValue ) +{ + // find last entry + struct PingHost * current = *list; + struct PingHost * last = current; + while( current ) + { + struct PingHost * next = current->next; + last = current; + current = next; + } + + struct PingHost* newEntry = malloc( sizeof(struct PingHost) ); + newEntry->host = newEntryValue; + newEntry->next = NULL; + + if ( last ) + { + last->next = newEntry; + } + else + { + *list = newEntry; + } + + (*listSize) ++; +} + +void freePingHostList( struct PingHost ** list, unsigned int * listSize ) +{ + struct PingHost * current = *list; + while( current ) + { + struct PingHost * next = current->next; + free( current ); + current = next; + } + *list = NULL; + if(listSize) + { + *listSize = 0; + } +} diff --git a/pinghostlist.h b/pinghostlist.h new file mode 100644 index 0000000..448f1a8 --- /dev/null +++ b/pinghostlist.h @@ -0,0 +1,19 @@ +#ifndef PING_HOST_LIST_H +#define PING_HOST_LIST_H + + +// linked list of hosts to ping +struct PingHost +{ + const char * host; + struct PingHost* next; +}; + +// add a new entry to the list +void appendPingHost( struct PingHost ** list, unsigned int * listSize, const char * newEntryValue ); + +// delete the list +// *list and *listsize will be set to 0 +void freePingHostList( struct PingHost ** list, unsigned int * listSize ); + +#endif diff --git a/resolve.c b/resolve.c index ca7cd8b..b8a7430 100644 --- a/resolve.c +++ b/resolve.c @@ -17,6 +17,14 @@ int resolveName(struct sockaddr* addr, socklen_t* addr_len, const char* hostname, int family) { +#ifdef WIN32 + // force ipv4 only on windows -> ipv6 ping is not supprted + if(family == AF_UNSPEC) + { + family = AF_INET; + } +#endif + int result; struct addrinfo* res = NULL; diff --git a/searchnet.c b/searchnet.c index 551edbc..5d25a3b 100644 --- a/searchnet.c +++ b/searchnet.c @@ -12,16 +12,20 @@ uint32_t my_random_key; uint8_t send_id[4]; extern float pingperiodseconds; +struct PingData * PingData = NULL; void * PingListen( void * r ) { - listener(); + struct PreparedPing* pp = (struct PreparedPing*) r; + listener( pp ); printf( "Fault on listen.\n" ); exit( -2 ); } -void display(uint8_t *buf, int bytes) +void display(uint8_t *buf, int bytes, unsigned int pingHostId) { + (void) pingHostId; + uint32_t reqid = ((uint32_t)buf[0+1] << 24) | (buf[1+1]<<16) | (buf[2+1]<<8) | (buf[3+1]); if( reqid != my_random_key ) return; @@ -29,8 +33,10 @@ void display(uint8_t *buf, int bytes) printf( "%d.%d.%d.%d\n", buf[4+1], buf[5+1], buf[6+1], buf[7+1] ); } -int load_ping_packet( uint8_t * buffer, int bufflen ) +int load_ping_packet( uint8_t * buffer, int bufflen, struct PingData* pd ) { + (void) pd; + buffer[0+1] = my_random_key >> 24; buffer[1+1] = my_random_key >> 16; buffer[2+1] = my_random_key >> 8; @@ -53,8 +59,13 @@ int main( int argc, char ** argv ) char dispip[32]; float speed; - ping_setup( 0, 0); - OGCreateThread( PingListen, 0 ); + struct PreparedPing* pp = ping_setup( 0, 0); + if(!pp) + { + return -1; + } + + OGCreateThread( PingListen, pp ); srand( ((int)(OGGetAbsoluteTime()*10000)) ); my_random_key = rand(); @@ -68,6 +79,9 @@ int main( int argc, char ** argv ) mask = 1<<(32-atoi(argv[2])); speed = atof(argv[3]); + struct PingData localPingData; + PingData = &localPingData; + base &= ~(mask-1); printf( "Base: %08x / Mask: %08x\n", base, mask ); for( offset = 0; offset < mask; offset++ ) @@ -81,14 +95,18 @@ int main( int argc, char ** argv ) // printf( "Pinging: %s\n", dispip ); pingperiodseconds = -1; - struct sockaddr_in6 psaddr; - socklen_t psaddr_len = sizeof(psaddr); - resolveName((struct sockaddr*) &psaddr, &psaddr_len, dispip, AF_UNSPEC); - singleping((struct sockaddr*) &psaddr, psaddr_len ); + pp->psaddr_len = sizeof(pp->psaddr); + pp->psaddr.sin6_family = AF_INET; + resolveName((struct sockaddr*) &pp->psaddr, &pp->psaddr_len, dispip, AF_UNSPEC); + pp->pingHostId = 0; + + singleping( pp ); OGUSleep( (int)(speed * 1000000) ); } + free(pp); + return 0; }