/*--------------------------------------------------------------- * Copyright (c) 1999,2000,2001,2002,2003 * The Board of Trustees of the University of Illinois * All Rights Reserved. *--------------------------------------------------------------- * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software (Iperf) and associated * documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do * so, subject to the following conditions: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and * the following disclaimers. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimers in the documentation and/or other materials * provided with the distribution. * * * Neither the names of the University of Illinois, NCSA, * nor the names of its contributors may be used to endorse * or promote products derived from this Software without * specific prior written permission. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ________________________________________________________________ * National Laboratory for Applied Network Research * National Center for Supercomputing Applications * University of Illinois at Urbana-Champaign * http://www.ncsa.uiuc.edu * ________________________________________________________________ * * Reporter.c * by Kevin Gibbs * * ________________________________________________________________ */ #include "headers.h" #include "Settings.hpp" #include "util.h" #include "Reporter.h" #include "Thread.h" #include "Locale.h" #include "PerfSocket.hpp" #include "SocketAddr.h" #ifdef __cplusplus extern "C" { #endif /* The following 4 functions are provided for Reporting styles that do not have all the reporting formats. For instance the provided CSV format does not have a settings report so it uses settings_notimpl. */ void* connection_notimpl( Connection_Info * nused, int nuse ) { return NULL; } void settings_notimpl( ReporterData * nused ) { } void statistics_notimpl( Transfer_Info * nused ) { } void serverstatistics_notimpl( Connection_Info *nused1, Transfer_Info *nused2 ) { } // To add a reporting style include its header here. #include "report_default.h" #include "report_CSV.h" // The following array of report structs contains the // pointers required for reporting in different reporting // styles. To add a reporting style add a report struct // below. report_connection connection_reports[kReport_MAXIMUM] = { reporter_reportpeer, CSV_peer }; report_settings settings_reports[kReport_MAXIMUM] = { reporter_reportsettings, settings_notimpl }; report_statistics statistics_reports[kReport_MAXIMUM] = { reporter_printstats, CSV_stats }; report_serverstatistics serverstatistics_reports[kReport_MAXIMUM] = { reporter_serverstats, CSV_serverstats }; report_statistics multiple_reports[kReport_MAXIMUM] = { reporter_multistats, CSV_stats }; char buffer[64]; // Buffer for printing ReportHeader *ReportRoot = NULL; int threadWait = 0; int threadSleeping = 0; extern Condition ReportCond; int reporter_process_report ( ReportHeader *report ); void process_report ( ReportHeader *report ); int reporter_handle_packet( ReportHeader *report ); int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force ); int reporter_print( ReporterData *stats, int type, int end ); void PrintMSS( ReporterData *stats ); MultiHeader* InitMulti( thread_Settings *agent, int inID ) { MultiHeader *multihdr = NULL; if ( agent->mThreads > 1 || agent->mThreadMode == kMode_Server ) { if ( isMultipleReport( agent ) ) { multihdr = malloc(sizeof(MultiHeader) + sizeof(ReporterData) + NUM_MULTI_SLOTS * sizeof(Transfer_Info)); } else { multihdr = malloc(sizeof(MultiHeader)); } if ( multihdr != NULL ) { memset( multihdr, 0, sizeof(MultiHeader) ); Condition_Initialize( &multihdr->barrier ); multihdr->groupID = inID; multihdr->threads = agent->mThreads; if ( isMultipleReport( agent ) ) { int i; ReporterData *data = NULL; multihdr->report = (ReporterData*)(multihdr + 1); memset(multihdr->report, 0, sizeof(ReporterData)); multihdr->data = (Transfer_Info*)(multihdr->report + 1); data = multihdr->report; for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) { multihdr->data[i].startTime = -1; multihdr->data[i].transferID = inID; multihdr->data[i].groupID = -2; } data->type = TRANSFER_REPORT; if ( agent->mInterval != 0.0 ) { struct timeval *interval = &data->intervalTime; interval->tv_sec = (long) agent->mInterval; interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) * rMillion); } data->mHost = agent->mHost; data->mLocalhost = agent->mLocalhost; data->mBufLen = agent->mBufLen; data->mMSS = agent->mMSS; data->mTCPWin = agent->mTCPWin; data->flags = agent->flags; data->mThreadMode = agent->mThreadMode; data->mode = agent->mReportMode; data->info.mFormat = agent->mFormat; data->info.mTTL = agent->mTTL; if ( isUDP( agent ) ) { multihdr->report->info.mUDP = (char)agent->mThreadMode; } if ( isConnectionReport( agent ) ) { data->type |= CONNECTION_REPORT; data->connection.peer = agent->peer; data->connection.size_peer = agent->size_peer; SockAddr_setPortAny( &data->connection.peer ); data->connection.local = agent->local; data->connection.size_local = agent->size_local; SockAddr_setPortAny( &data->connection.local ); } } } else { FAIL(1, "Out of Memory!!\n", agent); } } return multihdr; } /* * BarrierClient allows for multiple stream clients to be syncronized */ void BarrierClient( ReportHeader *agent ) { Condition_Lock(agent->multireport->barrier); agent->multireport->threads--; if ( agent->multireport->threads == 0 ) { // last one set time and wake up everyone gettimeofday( &(agent->multireport->startTime), NULL ); Condition_Broadcast( &agent->multireport->barrier ); } else { Condition_Wait( &agent->multireport->barrier ); } agent->multireport->threads++; Condition_Unlock( agent->multireport->barrier ); agent->report.startTime = agent->multireport->startTime; agent->report.nextTime = agent->report.startTime; TimeAdd( agent->report.nextTime, agent->report.intervalTime ); } /* * InitReport is called by a transfer agent (client or * server) to setup the needed structures to communicate * traffic. */ ReportHeader* InitReport( thread_Settings *agent ) { ReportHeader *reporthdr = NULL; ReporterData *data = NULL; if ( isDataReport( agent ) ) { /* * Create in one big chunk */ reporthdr = malloc( sizeof(ReportHeader) + NUM_REPORT_STRUCTS * sizeof(ReportStruct) ); if ( reporthdr != NULL ) { // Only need to make sure the headers are clean memset( reporthdr, 0, sizeof(ReportHeader)); reporthdr->data = (ReportStruct*)(reporthdr+1); reporthdr->multireport = agent->multihdr; data = &reporthdr->report; reporthdr->reporterindex = NUM_REPORT_STRUCTS - 1; data->info.transferID = agent->mSock; data->info.groupID = (agent->multihdr != NULL ? agent->multihdr->groupID : -1); data->type = TRANSFER_REPORT; if ( agent->mInterval != 0.0 ) { struct timeval *interval = &data->intervalTime; interval->tv_sec = (long) agent->mInterval; interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) * rMillion); } data->mHost = agent->mHost; data->mLocalhost = agent->mLocalhost; data->mBufLen = agent->mBufLen; data->mMSS = agent->mMSS; data->mTCPWin = agent->mTCPWin; data->flags = agent->flags; data->mThreadMode = agent->mThreadMode; data->mode = agent->mReportMode; data->info.mFormat = agent->mFormat; data->info.mTTL = agent->mTTL; if ( isUDP( agent ) ) { reporthdr->report.info.mUDP = (char)agent->mThreadMode; } } else { FAIL(1, "Out of Memory!!\n", agent); } } if ( isConnectionReport( agent ) ) { if ( reporthdr == NULL ) { /* * Create in one big chunk */ reporthdr = malloc( sizeof(ReportHeader) ); if ( reporthdr != NULL ) { // Only need to make sure the headers are clean memset( reporthdr, 0, sizeof(ReportHeader)); data = &reporthdr->report; data->info.transferID = agent->mSock; data->info.groupID = -1; } else { FAIL(1, "Out of Memory!!\n", agent); } } if ( reporthdr != NULL ) { data->type |= CONNECTION_REPORT; data->connection.peer = agent->peer; data->connection.size_peer = agent->size_peer; data->connection.local = agent->local; data->connection.size_local = agent->size_local; } else { FAIL(1, "Out of Memory!!\n", agent); } } if ( isConnectionReport( agent ) || isDataReport( agent ) ) { #ifdef HAVE_THREAD /* * Update the ReportRoot to include this report. */ if ( reporthdr->report.mThreadMode == kMode_Client && reporthdr->multireport != NULL ) { // syncronize watches on my mark...... BarrierClient( reporthdr ); } else { if ( reporthdr->multireport != NULL && isMultipleReport( agent )) { reporthdr->multireport->threads++; if ( reporthdr->multireport->report->startTime.tv_sec == 0 ) { gettimeofday( &(reporthdr->multireport->report->startTime), NULL ); } reporthdr->report.startTime = reporthdr->multireport->report->startTime; } else { // set start time gettimeofday( &(reporthdr->report.startTime), NULL ); } reporthdr->report.nextTime = reporthdr->report.startTime; TimeAdd( reporthdr->report.nextTime, reporthdr->report.intervalTime ); } Condition_Lock( ReportCond ); reporthdr->next = ReportRoot; ReportRoot = reporthdr; Condition_Signal( &ReportCond ); Condition_Unlock( ReportCond ); #else // set start time gettimeofday( &(reporthdr->report.startTime), NULL ); /* * Process the report in this thread */ reporthdr->next = NULL; process_report ( reporthdr ); #endif } if ( !isDataReport( agent ) ) { reporthdr = NULL; } return reporthdr; } /* * ReportPacket is called by a transfer agent to record * the arrival or departure of a "packet" (for TCP it * will actually represent many packets). This needs to * be as simple and fast as possible as it gets called for * every "packet". */ void ReportPacket( ReportHeader* agent, ReportStruct *packet ) { if ( agent != NULL ) { int index = agent->reporterindex; /* * First find the appropriate place to put the information */ if ( agent->agentindex == NUM_REPORT_STRUCTS ) { // Just need to make sure that reporter is not on the first // item while ( index == 0 ) { Condition_Signal( &ReportCond ); thread_rest(); index = agent->reporterindex; } agent->agentindex = 0; } // Need to make sure that reporter is not about to be "lapped" while ( index - 1 == agent->agentindex ) { Condition_Signal( &ReportCond ); thread_rest(); index = agent->reporterindex; } if (threadSleeping) Condition_Signal( &ReportCond ); // Put the information there memcpy( agent->data + agent->agentindex, packet, sizeof(ReportStruct) ); // Updating agentindex MUST be the last thing done agent->agentindex++; #ifndef HAVE_THREAD /* * Process the report in this thread */ process_report ( agent ); #endif } } /* * CloseReport is called by a transfer agent to finalize * the report and signal transfer is over. */ void CloseReport( ReportHeader *agent, ReportStruct *packet ) { if ( agent != NULL) { /* * Using PacketID of -1 ends reporting */ packet->packetID = -1; packet->packetLen = 0; ReportPacket( agent, packet ); packet->packetID = agent->report.cntDatagrams; if (threadSleeping) Condition_Signal( &ReportCond ); } } /* * EndReport signifies the agent no longer is interested * in the report. Calls to GetReport will no longer be * filled */ void EndReport( ReportHeader *agent ) { if ( agent != NULL ) { int index = agent->reporterindex; if (threadSleeping) Condition_Signal( &ReportCond ); while ( index != -1 ) { thread_rest(); index = agent->reporterindex; } agent->agentindex = -1; #ifndef HAVE_THREAD /* * Process the report in this thread */ process_report ( agent ); #endif } } /* * GetReport is called by the agent after a CloseReport * but before an EndReport to get the stats generated * by the reporter thread. */ Transfer_Info *GetReport( ReportHeader *agent ) { int index = agent->reporterindex; while ( index != -1 ) { thread_rest(); index = agent->reporterindex; } return &agent->report.info; } /* * ReportSettings will generate a summary report for * settings being used with Listeners or Clients */ void ReportSettings( thread_Settings *agent ) { if ( isSettingsReport( agent ) ) { /* * Create in one big chunk */ ReportHeader *reporthdr = malloc( sizeof(ReportHeader) ); if ( reporthdr != NULL ) { ReporterData *data = &reporthdr->report; data->info.transferID = agent->mSock; data->info.groupID = -1; reporthdr->agentindex = -1; reporthdr->reporterindex = -1; data->mHost = agent->mHost; data->mLocalhost = agent->mLocalhost; data->mode = agent->mReportMode; data->type = SETTINGS_REPORT; data->mBufLen = agent->mBufLen; data->mMSS = agent->mMSS; data->mTCPWin = agent->mTCPWin; data->flags = agent->flags; data->mThreadMode = agent->mThreadMode; data->mPort = agent->mPort; data->info.mFormat = agent->mFormat; data->info.mTTL = agent->mTTL; data->connection.peer = agent->peer; data->connection.size_peer = agent->size_peer; data->connection.local = agent->local; data->connection.size_local = agent->size_local; #ifdef HAVE_THREAD /* * Update the ReportRoot to include this report. */ Condition_Lock( ReportCond ); if ( isUDP(agent) ) threadWait = 0; else threadWait = 1; reporthdr->next = ReportRoot; ReportRoot = reporthdr; Condition_Signal( &ReportCond ); Condition_Unlock( ReportCond ); #else /* * Process the report in this thread */ reporthdr->next = NULL; process_report ( reporthdr ); #endif } else { FAIL(1, "Out of Memory!!\n", agent); } } } /* * ReportServerUDP will generate a report of the UDP * statistics as reported by the server on the client * side. */ void ReportServerUDP( thread_Settings *agent, server_hdr *server ) { if ( (ntohl(server->flags) & HEADER_VERSION1) != 0 && isServerReport( agent ) ) { /* * Create in one big chunk */ ReportHeader *reporthdr = malloc( sizeof(ReportHeader) ); Transfer_Info *stats = &reporthdr->report.info; if ( reporthdr != NULL ) { stats->transferID = agent->mSock; stats->groupID = (agent->multihdr != NULL ? agent->multihdr->groupID : -1); reporthdr->agentindex = -1; reporthdr->reporterindex = -1; reporthdr->report.type = SERVER_RELAY_REPORT; reporthdr->report.mode = agent->mReportMode; stats->mFormat = agent->mFormat; stats->jitter = ntohl( server->jitter1 ); stats->jitter += ntohl( server->jitter2 ) / (double)rMillion; stats->TotalLen = (((max_size_t) ntohl( server->total_len1 )) << 32) + ntohl( server->total_len2 ); stats->startTime = 0; stats->endTime = ntohl( server->stop_sec ); stats->endTime += ntohl( server->stop_usec ) / (double)rMillion; stats->cntError = ntohl( server->error_cnt ); stats->cntOutofOrder = ntohl( server->outorder_cnt ); stats->cntDatagrams = ntohl( server->datagrams ); stats->mUDP = (char)kMode_Server; reporthdr->report.connection.peer = agent->local; reporthdr->report.connection.size_peer = agent->size_local; reporthdr->report.connection.local = agent->peer; reporthdr->report.connection.size_local = agent->size_peer; #ifdef HAVE_THREAD /* * Update the ReportRoot to include this report. */ Condition_Lock( ReportCond ); reporthdr->next = ReportRoot; ReportRoot = reporthdr; Condition_Signal( &ReportCond ); Condition_Unlock( ReportCond ); #else /* * Process the report in this thread */ reporthdr->next = NULL; process_report ( reporthdr ); #endif } else { FAIL(1, "Out of Memory!!\n", agent); } } } /* * This function is called only when the reporter thread * This function is the loop that the reporter thread processes */ void reporter_spawn( thread_Settings *thread ) { do { // This section allows for safe exiting with Ctrl-C Condition_Lock ( ReportCond ); if ( ReportRoot == NULL ) { // Allow main thread to exit if Ctrl-C is received thread_setignore(); Condition_Wait ( &ReportCond ); // Stop main thread from exiting until done with all reports thread_unsetignore(); } Condition_Unlock ( ReportCond ); if ( ReportRoot != NULL ) { ReportHeader *temp = ReportRoot; //Condition_Unlock ( ReportCond ); if ( reporter_process_report ( temp ) ) { // This section allows for more reports to be added while // the reporter is processing reports without needing to // stop the reporter or immediately notify it Condition_Lock ( ReportCond ); if ( temp == ReportRoot ) { // no new reports ReportRoot = temp->next; } else { // new reports added ReportHeader *itr = ReportRoot; while ( itr->next != temp ) { itr = itr->next; } itr->next = temp->next; } // finished with report so free it free( temp ); Condition_Unlock ( ReportCond ); } // yield control of CPU is another thread is waiting // sleep on a condition variable, as it is much cheaper // on most platforms than issuing schedyield or usleep // syscalls Condition_Lock ( ReportCond ); if ( threadWait && ReportRoot != NULL) { threadSleeping = 1; Condition_TimedWait (& ReportCond, 1 ); threadSleeping = 0; } Condition_Unlock ( ReportCond ); } else { //Condition_Unlock ( ReportCond ); } } while ( 1 ); } /* * Used for single threaded reporting */ void process_report ( ReportHeader *report ) { if ( report != NULL ) { if ( reporter_process_report( report ) ) { free( report ); } } } /* * Process reports starting with "reporthdr" */ int reporter_process_report ( ReportHeader *reporthdr ) { int need_free = 0; // Recursively process reports if ( reporthdr->next != NULL ) { if ( reporter_process_report( reporthdr->next ) ) { // If we are done with this report then free it ReportHeader *temp = reporthdr->next; reporthdr->next = reporthdr->next->next; free( temp ); } } if ( (reporthdr->report.type & SETTINGS_REPORT) != 0 ) { reporthdr->report.type &= ~SETTINGS_REPORT; return reporter_print( &reporthdr->report, SETTINGS_REPORT, 1 ); } else if ( (reporthdr->report.type & CONNECTION_REPORT) != 0 ) { reporthdr->report.type &= ~CONNECTION_REPORT; reporter_print( &reporthdr->report, CONNECTION_REPORT, (reporthdr->report.type == 0 ? 1 : 0) ); if ( reporthdr->multireport != NULL && isMultipleReport( (&reporthdr->report) )) { if ( (reporthdr->multireport->report->type & CONNECTION_REPORT) != 0 ) { reporthdr->multireport->report->type &= ~CONNECTION_REPORT; reporter_print( reporthdr->multireport->report, CONNECTION_REPORT, (reporthdr->report.type == 0 ? 1 : 0) ); } } } else if ( (reporthdr->report.type & SERVER_RELAY_REPORT) != 0 ) { reporthdr->report.type &= ~SERVER_RELAY_REPORT; return reporter_print( &reporthdr->report, SERVER_RELAY_REPORT, 1 ); } if ( (reporthdr->report.type & TRANSFER_REPORT) != 0 ) { // If there are more packets to process then handle them if ( reporthdr->reporterindex >= 0 ) { // Need to make sure we do not pass the "agent" while ( reporthdr->reporterindex != reporthdr->agentindex - 1 ) { if ( reporthdr->reporterindex == NUM_REPORT_STRUCTS - 1 ) { if ( reporthdr->agentindex == 0 ) { break; } else { reporthdr->reporterindex = 0; } } else { reporthdr->reporterindex++; } if ( reporter_handle_packet( reporthdr ) ) { // No more packets to process reporthdr->reporterindex = -1; break; } } } // If the agent is done with the report then free it if ( reporthdr->agentindex == -1 ) { need_free = 1; } } return need_free; } /* * Updates connection stats */ int reporter_handle_packet( ReportHeader *reporthdr ) { ReportStruct *packet = &reporthdr->data[reporthdr->reporterindex]; ReporterData *data = &reporthdr->report; Transfer_Info *stats = &reporthdr->report.info; int finished = 0; data->cntDatagrams++; // If this is the last packet set the endTime if ( packet->packetID < 0 ) { data->packetTime = packet->packetTime; finished = 1; if ( reporthdr->report.mThreadMode != kMode_Client ) { data->TotalLen += packet->packetLen; } } else { // update recieved amount and time data->packetTime = packet->packetTime; reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished ); data->TotalLen += packet->packetLen; if ( packet->packetID != 0 ) { // UDP packet double transit; double deltaTransit; // from RFC 1889, Real Time Protocol (RTP) // J = J + ( | D(i-1,i) | - J ) / 16 transit = TimeDifference( packet->packetTime, packet->sentTime ); if ( data->lastTransit != 0.0 ) { deltaTransit = transit - data->lastTransit; if ( deltaTransit < 0.0 ) { deltaTransit = -deltaTransit; } stats->jitter += (deltaTransit - stats->jitter) / (16.0); } data->lastTransit = transit; // packet loss occured if the datagram numbers aren't sequential if ( packet->packetID != data->PacketID + 1 ) { if ( packet->packetID < data->PacketID + 1 ) { data->cntOutofOrder++; } else { data->cntError += packet->packetID - data->PacketID - 1; } } // never decrease datagramID (e.g. if we get an out-of-order packet) if ( packet->packetID > data->PacketID ) { data->PacketID = packet->packetID; } } } // Print a report if appropriate return reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished ); } /* * Handles summing of threads */ void reporter_handle_multiple_reports( MultiHeader *reporthdr, Transfer_Info *stats, int force ) { if ( reporthdr != NULL ) { if ( reporthdr->threads > 1 ) { int i; Transfer_Info *current = NULL; // Search for start Time for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) { current = &reporthdr->data[i]; if ( current->startTime == stats->startTime ) { break; } } if ( current->startTime != stats->startTime ) { // Find first available for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) { current = &reporthdr->data[i]; if ( current->startTime < 0 ) { break; } } current->cntDatagrams = stats->cntDatagrams; current->cntError = stats->cntError; current->cntOutofOrder = stats->cntOutofOrder; current->TotalLen = stats->TotalLen; current->mFormat = stats->mFormat; current->endTime = stats->endTime; current->jitter = stats->jitter; current->startTime = stats->startTime; current->free = 1; } else { current->cntDatagrams += stats->cntDatagrams; current->cntError += stats->cntError; current->cntOutofOrder += stats->cntOutofOrder; current->TotalLen += stats->TotalLen; current->mFormat = stats->mFormat; if ( current->endTime < stats->endTime ) { current->endTime = stats->endTime; } if ( current->jitter < stats->jitter ) { current->jitter = stats->jitter; } current->free++; if ( current->free == reporthdr->threads ) { void *reserved = reporthdr->report->info.reserved_delay; current->free = force; memcpy( &reporthdr->report->info, current, sizeof(Transfer_Info) ); current->startTime = -1; reporthdr->report->info.reserved_delay = reserved; reporter_print( reporthdr->report, MULTIPLE_REPORT, force ); } } } } } /* * Prints reports conditionally */ int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force ) { if ( force != 0 ) { stats->info.cntOutofOrder = stats->cntOutofOrder; // assume most of the time out-of-order packets are not // duplicate packets, so conditionally subtract them from the lost packets. stats->info.cntError = stats->cntError; if ( stats->info.cntError > stats->info.cntOutofOrder ) { stats->info.cntError -= stats->info.cntOutofOrder; } stats->info.cntDatagrams = (isUDP(stats) ? stats->PacketID : stats->cntDatagrams); stats->info.TotalLen = stats->TotalLen; stats->info.startTime = 0; stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime ); stats->info.free = 1; reporter_print( stats, TRANSFER_REPORT, force ); if ( isMultipleReport(stats) ) { reporter_handle_multiple_reports( multireport, &stats->info, force ); } } else while ((stats->intervalTime.tv_sec != 0 || stats->intervalTime.tv_usec != 0) && TimeDifference( stats->nextTime, stats->packetTime ) < 0 ) { stats->info.cntOutofOrder = stats->cntOutofOrder - stats->lastOutofOrder; stats->lastOutofOrder = stats->cntOutofOrder; // assume most of the time out-of-order packets are not // duplicate packets, so conditionally subtract them from the lost packets. stats->info.cntError = stats->cntError - stats->lastError; if ( stats->info.cntError > stats->info.cntOutofOrder ) { stats->info.cntError -= stats->info.cntOutofOrder; } stats->lastError = stats->cntError; stats->info.cntDatagrams = (isUDP( stats ) ? stats->PacketID - stats->lastDatagrams : stats->cntDatagrams - stats->lastDatagrams); stats->lastDatagrams = (isUDP( stats ) ? stats->PacketID : stats->cntDatagrams); stats->info.TotalLen = stats->TotalLen - stats->lastTotal; stats->lastTotal = stats->TotalLen; stats->info.startTime = stats->info.endTime; stats->info.endTime = TimeDifference( stats->nextTime, stats->startTime ); TimeAdd( stats->nextTime, stats->intervalTime ); stats->info.free = 0; reporter_print( stats, TRANSFER_REPORT, force ); if ( isMultipleReport(stats) ) { reporter_handle_multiple_reports( multireport, &stats->info, force ); } } return force; } /* * This function handles multiple format printing by sending to the * appropriate dispatch function */ int reporter_print( ReporterData *stats, int type, int end ) { switch ( type ) { case TRANSFER_REPORT: statistics_reports[stats->mode]( &stats->info ); if ( end != 0 && isPrintMSS( stats ) && !isUDP( stats ) ) { PrintMSS( stats ); } break; case SERVER_RELAY_REPORT: serverstatistics_reports[stats->mode]( &stats->connection, &stats->info ); break; case SETTINGS_REPORT: settings_reports[stats->mode]( stats ); break; case CONNECTION_REPORT: stats->info.reserved_delay = connection_reports[stats->mode]( &stats->connection, stats->info.transferID ); break; case MULTIPLE_REPORT: multiple_reports[stats->mode]( &stats->info ); break; default: fprintf( stderr, "Printing type not implemented! No Output\n" ); } fflush( stdout ); return end; } /* ------------------------------------------------------------------- * Report the MSS and MTU, given the MSS (or a guess thereof) * ------------------------------------------------------------------- */ // compare the MSS against the (MTU - 40) to (MTU - 80) bytes. // 40 byte IP header and somewhat arbitrarily, 40 more bytes of IP options. #define checkMSS_MTU( inMSS, inMTU ) (inMTU-40) >= inMSS && inMSS >= (inMTU-80) void PrintMSS( ReporterData *stats ) { int inMSS = getsock_tcp_mss( stats->info.transferID ); if ( inMSS <= 0 ) { printf( report_mss_unsupported, stats->info.transferID ); } else { char* net; int mtu = 0; if ( checkMSS_MTU( inMSS, 1500 ) ) { net = "ethernet"; mtu = 1500; } else if ( checkMSS_MTU( inMSS, 4352 ) ) { net = "FDDI"; mtu = 4352; } else if ( checkMSS_MTU( inMSS, 9180 ) ) { net = "ATM"; mtu = 9180; } else if ( checkMSS_MTU( inMSS, 65280 ) ) { net = "HIPPI"; mtu = 65280; } else if ( checkMSS_MTU( inMSS, 576 ) ) { net = "minimum"; mtu = 576; printf( warn_no_pathmtu ); } else { mtu = inMSS + 40; net = "unknown interface"; } printf( report_mss, stats->info.transferID, inMSS, mtu, net ); } } // end ReportMSS #ifdef __cplusplus } /* end extern "C" */ #endif