]> sjero.net Git - iperf/blob - src/Reporter.c
TCP Congestion Control Module via options
[iperf] / src / Reporter.c
1 /*--------------------------------------------------------------- 
2  * Copyright (c) 1999,2000,2001,2002,2003                              
3  * The Board of Trustees of the University of Illinois            
4  * All Rights Reserved.                                           
5  *--------------------------------------------------------------- 
6  * Permission is hereby granted, free of charge, to any person    
7  * obtaining a copy of this software (Iperf) and associated       
8  * documentation files (the "Software"), to deal in the Software  
9  * without restriction, including without limitation the          
10  * rights to use, copy, modify, merge, publish, distribute,        
11  * sublicense, and/or sell copies of the Software, and to permit     
12  * persons to whom the Software is furnished to do
13  * so, subject to the following conditions: 
14  *
15  *     
16  * Redistributions of source code must retain the above 
17  * copyright notice, this list of conditions and 
18  * the following disclaimers. 
19  *
20  *     
21  * Redistributions in binary form must reproduce the above 
22  * copyright notice, this list of conditions and the following 
23  * disclaimers in the documentation and/or other materials 
24  * provided with the distribution. 
25  * 
26  *     
27  * Neither the names of the University of Illinois, NCSA, 
28  * nor the names of its contributors may be used to endorse 
29  * or promote products derived from this Software without
30  * specific prior written permission. 
31  * 
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
33  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
34  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
35  * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT 
36  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
37  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
38  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
40  * ________________________________________________________________
41  * National Laboratory for Applied Network Research 
42  * National Center for Supercomputing Applications 
43  * University of Illinois at Urbana-Champaign 
44  * http://www.ncsa.uiuc.edu
45  * ________________________________________________________________ 
46  *
47  * Reporter.c
48  * by Kevin Gibbs <kgibbs@nlanr.net>
49  *
50  * ________________________________________________________________ */
51
52 #include "headers.h"
53 #include "Settings.hpp"
54 #include "util.h"
55 #include "Reporter.h"
56 #include "Thread.h"
57 #include "Locale.h"
58 #include "PerfSocket.hpp"
59 #include "SocketAddr.h"
60
61 #ifdef __cplusplus
62 extern "C" {
63 #endif
64
65 /*
66   The following 4 functions are provided for Reporting
67   styles that do not have all the reporting formats. For
68   instance the provided CSV format does not have a settings
69   report so it uses settings_notimpl.
70   */
71 void* connection_notimpl( Connection_Info * nused, int nuse ) { 
72     return NULL; 
73 }
74 void settings_notimpl( ReporterData * nused ) { }
75 void statistics_notimpl( Transfer_Info * nused ) { }
76 void serverstatistics_notimpl( Connection_Info *nused1, Transfer_Info *nused2 ) { }
77
78 // To add a reporting style include its header here.
79 #include "report_default.h"
80 #include "report_CSV.h"
81
82 // The following array of report structs contains the
83 // pointers required for reporting in different reporting
84 // styles. To add a reporting style add a report struct
85 // below.
86 report_connection connection_reports[kReport_MAXIMUM] = {
87     reporter_reportpeer,
88     CSV_peer
89 };
90
91 report_settings settings_reports[kReport_MAXIMUM] = {
92     reporter_reportsettings,
93     settings_notimpl
94 };
95
96 report_statistics statistics_reports[kReport_MAXIMUM] = {
97     reporter_printstats,
98     CSV_stats
99 };
100
101 report_serverstatistics serverstatistics_reports[kReport_MAXIMUM] = {
102     reporter_serverstats,
103     CSV_serverstats
104 };
105
106 report_statistics multiple_reports[kReport_MAXIMUM] = {
107     reporter_multistats,
108     CSV_stats
109 };
110
111 char buffer[64]; // Buffer for printing
112 ReportHeader *ReportRoot = NULL;
113 extern Condition ReportCond;
114 extern Condition ReportDoneCond;
115 int reporter_process_report ( ReportHeader *report );
116 void process_report ( ReportHeader *report );
117 int reporter_handle_packet( ReportHeader *report );
118 int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force );
119 int reporter_print( ReporterData *stats, int type, int end );
120 void PrintMSS( ReporterData *stats );
121
122 MultiHeader* InitMulti( thread_Settings *agent, int inID ) {
123     MultiHeader *multihdr = NULL;
124     if ( agent->mThreads > 1 || agent->mThreadMode == kMode_Server ) {
125         if ( isMultipleReport( agent ) ) {
126             multihdr = malloc(sizeof(MultiHeader) +  sizeof(ReporterData) +
127                               NUM_MULTI_SLOTS * sizeof(Transfer_Info));
128         } else {
129             multihdr = malloc(sizeof(MultiHeader));
130         }
131         if ( multihdr != NULL ) {
132             memset( multihdr, 0, sizeof(MultiHeader) );
133             Condition_Initialize( &multihdr->barrier );
134             multihdr->groupID = inID;
135             multihdr->threads = agent->mThreads;
136             if ( isMultipleReport( agent ) ) {
137                 int i;
138                 ReporterData *data = NULL;
139                 multihdr->report = (ReporterData*)(multihdr + 1);
140                 memset(multihdr->report, 0, sizeof(ReporterData));
141                 multihdr->data = (Transfer_Info*)(multihdr->report + 1);
142                 data = multihdr->report;
143                 for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
144                     multihdr->data[i].startTime = -1;
145                     multihdr->data[i].transferID = inID;
146                     multihdr->data[i].groupID = -2;
147                 }
148                 data->type = TRANSFER_REPORT;
149                 if ( agent->mInterval != 0.0 ) {
150                     struct timeval *interval = &data->intervalTime;
151                     interval->tv_sec = (long) agent->mInterval;
152                     interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) 
153                                                 * rMillion);
154                 }
155                 data->mHost = agent->mHost;
156                 data->mLocalhost = agent->mLocalhost;
157                 data->mBufLen = agent->mBufLen;
158                 data->mMSS = agent->mMSS;
159                 data->mWinSize = agent->mWinSize;
160                 data->flags = agent->flags;
161                 data->mProtocol = agent->mProtocol;
162                 data->mThreadMode = agent->mThreadMode;
163                 data->mode = agent->mReportMode;
164                 data->info.mFormat = agent->mFormat;
165                 data->info.mTTL = agent->mTTL;
166                 if ( isPacketOriented( agent ) ) {
167                     multihdr->report->info.mUDP = (char)agent->mThreadMode;
168                 }
169                 if ( isConnectionReport( agent ) ) {
170                     data->type |= CONNECTION_REPORT;
171                     data->connection.peer = agent->peer;
172                     data->connection.local = agent->local;
173                 }
174             }
175         } else {
176             FAIL(1, "Out of Memory!!\n", agent);
177         }
178     }
179     return multihdr;
180 }
181
182 /*
183  * BarrierClient allows for multiple stream clients to be syncronized
184  */
185 void BarrierClient( ReportHeader *agent ) {
186     Condition_Lock(agent->multireport->barrier);
187     agent->multireport->threads--;
188     if ( agent->multireport->threads == 0 ) {
189         // last one set time and wake up everyone
190         gettimeofday( &(agent->multireport->startTime), NULL );
191         Condition_Broadcast( &agent->multireport->barrier );
192     } else {
193         Condition_Wait( &agent->multireport->barrier );
194     }
195     agent->multireport->threads++;
196     Condition_Unlock( agent->multireport->barrier );
197     agent->report.startTime = agent->multireport->startTime;
198     agent->report.nextTime = agent->report.startTime;
199     TimeAdd( agent->report.nextTime, agent->report.intervalTime );
200 }
201
202 /*
203  * InitReport is called by a transfer agent (client or
204  * server) to setup the needed structures to communicate
205  * traffic.
206  */
207 ReportHeader* InitReport( thread_Settings *agent ) {
208     ReportHeader *reporthdr = NULL;
209     ReporterData *data = NULL;
210     if ( isDataReport( agent ) ) {
211         /*
212          * Create in one big chunk
213          */
214         reporthdr = malloc( sizeof(ReportHeader) +
215                             NUM_REPORT_STRUCTS * sizeof(ReportStruct) );
216         if ( reporthdr != NULL ) {
217             // Only need to make sure the headers are clean
218             memset( reporthdr, 0, sizeof(ReportHeader));
219             reporthdr->data = (ReportStruct*)(reporthdr+1);
220             reporthdr->multireport = agent->multihdr;
221             data = &reporthdr->report;
222             reporthdr->reporterindex = NUM_REPORT_STRUCTS - 1;
223             data->info.transferID = agent->mSock;
224             data->info.groupID = (agent->multihdr != NULL ? agent->multihdr->groupID 
225                                                           : -1);
226             data->info.congAlgo = agent->congAlgo;
227             data->type = TRANSFER_REPORT;
228             if ( agent->mInterval != 0.0 ) {
229                 struct timeval *interval = &data->intervalTime;
230                 interval->tv_sec = (long) agent->mInterval;
231                 interval->tv_usec = (long) ((agent->mInterval - interval->tv_sec) 
232                                             * rMillion);
233             }
234             data->mHost = agent->mHost;
235             data->mLocalhost = agent->mLocalhost;
236             data->mBufLen = agent->mBufLen;
237             data->mMSS = agent->mMSS;
238             data->mWinSize = agent->mWinSize;
239             data->flags = agent->flags;
240             data->mProtocol = agent->mProtocol;
241             data->mThreadMode = agent->mThreadMode;
242             data->mode = agent->mReportMode;
243             data->info.mFormat = agent->mFormat;
244             data->info.mTTL = agent->mTTL;
245             if ( isPacketOriented( agent ) ) {
246                 reporthdr->report.info.mUDP = (char)agent->mThreadMode;
247             }
248         } else {
249             FAIL(1, "Out of Memory!!\n", agent);
250         }
251     }
252     if ( isConnectionReport( agent ) ) {
253         if ( reporthdr == NULL ) {
254             /*
255              * Create in one big chunk
256              */
257             reporthdr = malloc( sizeof(ReportHeader) );
258             if ( reporthdr != NULL ) {
259                 // Only need to make sure the headers are clean
260                 memset( reporthdr, 0, sizeof(ReportHeader));
261                 data = &reporthdr->report;
262                 data->info.transferID = agent->mSock;
263                 data->info.groupID = -1;
264                 data->info.congAlgo = agent->congAlgo;
265             } else {
266                 FAIL(1, "Out of Memory!!\n", agent);
267             }
268         }
269         if ( reporthdr != NULL ) {
270             data->type |= CONNECTION_REPORT;
271             data->connection.peer = agent->peer;
272             data->connection.local = agent->local;
273         } else {
274             FAIL(1, "Out of Memory!!\n", agent);
275         }
276     }
277     if ( isConnectionReport( agent ) || isDataReport( agent ) ) {
278
279 #ifdef HAVE_THREAD
280         /*
281          * Update the ReportRoot to include this report.
282          */
283         if ( reporthdr->report.mThreadMode == kMode_Client &&
284              reporthdr->multireport != NULL ) {
285             // syncronize watches on my mark......
286             BarrierClient( reporthdr );
287         } else {
288             if ( reporthdr->multireport != NULL && isMultipleReport( agent )) {
289                 reporthdr->multireport->threads++;
290                 if ( reporthdr->multireport->report->startTime.tv_sec == 0 ) {
291                     gettimeofday( &(reporthdr->multireport->report->startTime), NULL );
292                 }
293                 reporthdr->report.startTime = reporthdr->multireport->report->startTime;
294             } else {
295                 // set start time
296                 gettimeofday( &(reporthdr->report.startTime), NULL );
297             }
298             reporthdr->report.nextTime = reporthdr->report.startTime;
299             TimeAdd( reporthdr->report.nextTime, reporthdr->report.intervalTime );
300         }
301         Condition_Lock( ReportCond );
302         reporthdr->next = ReportRoot;
303         ReportRoot = reporthdr;
304         Condition_Signal( &ReportCond );
305         Condition_Unlock( ReportCond );
306 #else
307         // set start time
308         gettimeofday( &(reporthdr->report.startTime), NULL );
309         /*
310          * Process the report in this thread
311          */
312         reporthdr->next = NULL;
313         process_report ( reporthdr );
314 #endif 
315     }
316     if ( !isDataReport( agent ) ) {
317         reporthdr = NULL;
318     }
319     return reporthdr;
320 }
321
322 /*
323  * ReportPacket is called by a transfer agent to record
324  * the arrival or departure of a "packet" (for TCP it 
325  * will actually represent many packets). This needs to
326  * be as simple and fast as possible as it gets called for
327  * every "packet".
328  */
329 void ReportPacket( ReportHeader* agent, ReportStruct *packet ) {
330     if ( agent != NULL ) {
331         int index = agent->reporterindex;
332         /*
333          * First find the appropriate place to put the information
334          */
335         if ( agent->agentindex == NUM_REPORT_STRUCTS ) {
336             // Just need to make sure that reporter is not on the first
337             // item
338             while ( index == 0 ) {
339                 Condition_Signal( &ReportCond );
340                 Condition_Wait( &ReportDoneCond );
341                 index = agent->reporterindex;
342             }
343             agent->agentindex = 0;
344         }
345         // Need to make sure that reporter is not about to be "lapped"
346         while ( index - 1 == agent->agentindex ) {
347             Condition_Signal( &ReportCond );
348             Condition_Wait( &ReportDoneCond );
349             index = agent->reporterindex;
350         }
351         
352         // Put the information there
353         memcpy( agent->data + agent->agentindex, packet, sizeof(ReportStruct) );
354         
355         // Updating agentindex MUST be the last thing done
356         agent->agentindex++;
357 #ifndef HAVE_THREAD
358         /*
359          * Process the report in this thread
360          */
361         process_report ( agent );
362 #endif 
363     }
364 }
365
366 /*
367  * CloseReport is called by a transfer agent to finalize
368  * the report and signal transfer is over.
369  */
370 void CloseReport( ReportHeader *agent, ReportStruct *packet ) {
371     if ( agent != NULL) {
372         /*
373          * Using PacketID of -1 ends reporting
374          */
375         packet->packetID = -1;
376         packet->packetLen = 0;
377         ReportPacket( agent, packet );
378         packet->packetID = agent->report.cntDatagrams;
379     }
380 }
381
382 /*
383  * EndReport signifies the agent no longer is interested
384  * in the report. Calls to GetReport will no longer be
385  * filled
386  */
387 void EndReport( ReportHeader *agent ) {
388     if ( agent != NULL ) {
389         int index = agent->reporterindex;
390         while ( index != -1 ) {
391             thread_rest();
392             index = agent->reporterindex;
393         }
394         agent->agentindex = -1;
395 #ifndef HAVE_THREAD
396         /*
397          * Process the report in this thread
398          */
399         process_report ( agent );
400 #endif
401     }
402 }
403
404 /*
405  * GetReport is called by the agent after a CloseReport
406  * but before an EndReport to get the stats generated
407  * by the reporter thread.
408  */
409 Transfer_Info *GetReport( ReportHeader *agent ) {
410     int index = agent->reporterindex;
411     while ( index != -1 ) {
412         thread_rest();
413         index = agent->reporterindex;
414     }
415     return &agent->report.info;
416 }
417
418 /*
419  * ReportSettings will generate a summary report for
420  * settings being used with Listeners or Clients
421  */
422 void ReportSettings( thread_Settings *agent ) {
423     if ( isSettingsReport( agent ) ) {
424         /*
425          * Create in one big chunk
426          */
427         ReportHeader *reporthdr = malloc( sizeof(ReportHeader) );
428     
429         if ( reporthdr != NULL ) {
430             ReporterData *data = &reporthdr->report;
431             data->info.transferID = agent->mSock;
432             data->info.groupID = -1;
433             reporthdr->agentindex = -1;
434             reporthdr->reporterindex = -1;
435         
436             data->mHost = agent->mHost;
437             data->mLocalhost = agent->mLocalhost;
438             data->mode = agent->mReportMode;
439             data->type = SETTINGS_REPORT;
440             data->mBufLen = agent->mBufLen;
441             data->mMSS = agent->mMSS;
442             data->mWinSize = agent->mWinSize;
443             data->flags = agent->flags;
444             data->mProtocol = agent->mProtocol;
445             data->mThreadMode = agent->mThreadMode;
446             data->mPort = agent->mPort;
447             data->mMcastIface = agent->mMcastIface;
448             data->info.mFormat = agent->mFormat;
449             data->info.mTTL = agent->mTTL;
450             data->info.congAlgo = agent->congAlgo;
451             data->connection.peer = agent->peer;
452             data->connection.local = agent->local;
453     
454     #ifdef HAVE_THREAD
455             /*
456              * Update the ReportRoot to include this report.
457              */
458             Condition_Lock( ReportCond );
459             reporthdr->next = ReportRoot;
460             ReportRoot = reporthdr;
461             Condition_Signal( &ReportCond );
462             Condition_Unlock( ReportCond );
463     #else
464             /*
465              * Process the report in this thread
466              */
467             reporthdr->next = NULL;
468             process_report ( reporthdr );
469     #endif 
470         } else {
471             FAIL(1, "Out of Memory!!\n", agent);
472         }
473     }
474 }
475
476 /*
477  * ReportServerUDP will generate a report of the UDP
478  * statistics as reported by the server on the client
479  * side.
480  */
481 void ReportServerUDP( thread_Settings *agent, server_hdr *server ) {
482     if ( (ntohl(server->flags) & HEADER_VERSION1) != 0 &&
483          isServerReport( agent ) ) {
484         /*
485          * Create in one big chunk
486          */
487         ReportHeader *reporthdr = malloc( sizeof(ReportHeader) );
488         Transfer_Info *stats = &reporthdr->report.info;
489
490         if ( reporthdr != NULL ) {
491             stats->transferID = agent->mSock;
492             stats->groupID = (agent->multihdr != NULL ? agent->multihdr->groupID 
493                                                       : -1);
494             reporthdr->agentindex = -1;
495             reporthdr->reporterindex = -1;
496
497             reporthdr->report.type = SERVER_RELAY_REPORT;
498             reporthdr->report.mode = agent->mReportMode;
499             stats->mFormat = agent->mFormat;
500             stats->jitter = ntohl( server->jitter1 );
501             stats->jitter += ntohl( server->jitter2 ) / (double)rMillion;
502             stats->TotalLen = (((max_size_t) ntohl( server->total_len1 )) << 32) +
503                                   ntohl( server->total_len2 ); 
504             stats->startTime = 0;
505             stats->endTime = ntohl( server->stop_sec );
506             stats->endTime += ntohl( server->stop_usec ) / (double)rMillion;
507             stats->cntError = ntohl( server->error_cnt );
508             stats->cntOutofOrder = ntohl( server->outorder_cnt );
509             stats->cntDatagrams = ntohl( server->datagrams );
510             stats->mUDP = (char)kMode_Server;
511             reporthdr->report.connection.peer = agent->local;
512             reporthdr->report.connection.local = agent->peer;
513             
514 #ifdef HAVE_THREAD
515             /*
516              * Update the ReportRoot to include this report.
517              */
518             Condition_Lock( ReportCond );
519             reporthdr->next = ReportRoot;
520             ReportRoot = reporthdr;
521             Condition_Signal( &ReportCond );
522             Condition_Unlock( ReportCond );
523 #else
524             /*
525              * Process the report in this thread
526              */
527             reporthdr->next = NULL;
528             process_report ( reporthdr );
529 #endif 
530         } else {
531             FAIL(1, "Out of Memory!!\n", agent);
532         }
533     }
534 }
535
536 /*
537  * This function is called only when the reporter thread
538  * This function is the loop that the reporter thread processes
539  */
540 void reporter_spawn( thread_Settings *thread ) {
541     do {
542         // This section allows for safe exiting with Ctrl-C
543         Condition_Lock ( ReportCond );
544         if ( ReportRoot == NULL ) {
545             // Allow main thread to exit if Ctrl-C is received
546             thread_setignore();
547             Condition_Wait ( &ReportCond );
548             // Stop main thread from exiting until done with all reports
549             thread_unsetignore();
550         }
551         Condition_Unlock ( ReportCond );
552
553 again:
554         if ( ReportRoot != NULL ) {
555             ReportHeader *temp = ReportRoot;
556             //Condition_Unlock ( ReportCond );
557             if ( reporter_process_report ( temp ) ) {
558                 // This section allows for more reports to be added while
559                 // the reporter is processing reports without needing to
560                 // stop the reporter or immediately notify it
561                 Condition_Lock ( ReportCond );
562                 if ( temp == ReportRoot ) {
563                     // no new reports
564                     ReportRoot = temp->next;
565                 } else {
566                     // new reports added
567                     ReportHeader *itr = ReportRoot;
568                     while ( itr->next != temp ) {
569                         itr = itr->next;
570                     }
571                     itr->next = temp->next;
572                 }
573                 // finished with report so free it
574                 free( temp );
575                 Condition_Unlock ( ReportCond );
576                 Condition_Signal( &ReportDoneCond );
577                 if (ReportRoot)
578                     goto again;
579             }
580             Condition_Signal( &ReportDoneCond );
581             usleep(10000);
582         } else {
583             //Condition_Unlock ( ReportCond );
584         }
585     } while ( 1 );
586 }
587
588 /*
589  * Used for single threaded reporting
590  */
591 void process_report ( ReportHeader *report ) {
592     if ( report != NULL ) {
593         if ( reporter_process_report( report ) ) {
594             free( report );
595         }
596     }
597 }
598
599 /*
600  * Process reports starting with "reporthdr"
601  */
602 int reporter_process_report ( ReportHeader *reporthdr ) {
603     int need_free = 0;
604
605     // Recursively process reports
606     if ( reporthdr->next != NULL ) {
607         if ( reporter_process_report( reporthdr->next ) ) {
608             // If we are done with this report then free it
609             ReportHeader *temp = reporthdr->next;
610             reporthdr->next = reporthdr->next->next;
611             free( temp );
612         }
613     }
614
615     if ( (reporthdr->report.type & SETTINGS_REPORT) != 0 ) {
616         reporthdr->report.type &= ~SETTINGS_REPORT;
617         return reporter_print( &reporthdr->report, SETTINGS_REPORT, 1 );
618     } else if ( (reporthdr->report.type & CONNECTION_REPORT) != 0 ) {
619         reporthdr->report.type &= ~CONNECTION_REPORT;
620         reporter_print( &reporthdr->report, CONNECTION_REPORT,
621                                (reporthdr->report.type == 0 ? 1 : 0) );
622         if ( reporthdr->multireport != NULL && isMultipleReport( (&reporthdr->report) )) {
623             if ( (reporthdr->multireport->report->type & CONNECTION_REPORT) != 0 ) {
624                 reporthdr->multireport->report->type &= ~CONNECTION_REPORT;
625                 reporter_print( reporthdr->multireport->report, CONNECTION_REPORT,
626                                 (reporthdr->report.type == 0 ? 1 : 0) );
627             }
628         }
629     } else if ( (reporthdr->report.type & SERVER_RELAY_REPORT) != 0 ) {
630         reporthdr->report.type &= ~SERVER_RELAY_REPORT;
631         return reporter_print( &reporthdr->report, SERVER_RELAY_REPORT, 1 );
632     }
633     if ( (reporthdr->report.type & TRANSFER_REPORT) != 0 ) {
634         // If there are more packets to process then handle them
635         if ( reporthdr->reporterindex >= 0 ) {
636             // Need to make sure we do not pass the "agent"
637             while ( reporthdr->reporterindex != reporthdr->agentindex - 1 ) {
638                 if ( reporthdr->reporterindex == NUM_REPORT_STRUCTS - 1 ) {
639                     if ( reporthdr->agentindex == 0 ) {
640                         break;
641                     } else {
642                         reporthdr->reporterindex = 0;
643                     }
644                 } else {
645                     reporthdr->reporterindex++;
646                 }
647                 if ( reporter_handle_packet( reporthdr ) ) {
648                     // No more packets to process
649                     reporthdr->reporterindex = -1;
650                     break;
651                 }
652             }
653         }
654         // If the agent is done with the report then free it
655         if ( reporthdr->agentindex == -1 ) {
656             need_free = 1;
657         }
658     }
659     return need_free;
660 }
661
662 /*
663  * Updates connection stats
664  */
665 int reporter_handle_packet( ReportHeader *reporthdr ) {
666     ReportStruct *packet = &reporthdr->data[reporthdr->reporterindex];
667     ReporterData *data = &reporthdr->report;
668     Transfer_Info *stats = &reporthdr->report.info;
669     int finished = 0;
670
671     // update received amount and time
672     data->TotalLen  += packet->packetLen;
673     data->packetTime = packet->packetTime;
674     data->cntDatagrams++;
675
676     if (packet->packetID < 0) {
677         // This is the last packet (FIN)
678         finished = 1;
679         if (isConnectionLess(&reporthdr->report)) {
680             // connectionless protocols don't count the last payload
681             if (reporthdr->report.mThreadMode == kMode_Client)
682                 data->TotalLen -= packet->packetLen;
683         } else {
684             // connection-oriented protocols dont't count the FIN
685             data->cntDatagrams--;
686         }
687     } else if ( packet->packetID != 0 ) {
688             // UDP or DCCP packet
689             double transit, deltaTransit;
690
691             // from RFC 1889, Real Time Protocol (RTP) 
692             // J = J + ( | D(i-1,i) | - J ) / 16 
693             transit = TimeDifference( packet->packetTime, packet->sentTime );
694             if ( data->lastTransit != 0.0 ) {
695                 deltaTransit = transit - data->lastTransit;
696                 if ( deltaTransit < 0.0 ) {
697                     deltaTransit = -deltaTransit;
698                 }
699                 stats->jitter += (deltaTransit - stats->jitter) / (16.0);
700             }
701             data->lastTransit = transit;
702
703             // packet loss occured if the datagram numbers aren't sequential 
704             if ( packet->packetID != data->PacketID + 1 ) {
705                 if ( packet->packetID < data->PacketID + 1 ) {
706                     data->cntOutofOrder++;
707                 } else {
708                     data->cntError += packet->packetID - data->PacketID - 1;
709                 }
710             }
711             // never decrease datagramID (e.g. if we get an out-of-order packet) 
712             if ( packet->packetID > data->PacketID ) {
713                 data->PacketID = packet->packetID;
714             }
715     }
716     // Print a report if appropriate
717     return reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished );
718 }
719
720 /*
721  * Handles summing of threads
722  */
723 void reporter_handle_multiple_reports( MultiHeader *reporthdr, Transfer_Info *stats, int force ) {
724     if ( reporthdr != NULL ) {
725         if ( reporthdr->threads > 1 ) {
726             int i;
727             Transfer_Info *current = NULL;
728             // Search for start Time
729             for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
730                 current = &reporthdr->data[i];
731                 if ( current->startTime == stats->startTime ) {
732                     break;
733                 }
734             }
735             if ( current->startTime != stats->startTime ) {
736                 // Find first available
737                 for ( i = 0; i < NUM_MULTI_SLOTS; i++ ) {
738                     current = &reporthdr->data[i];
739                     if ( current->startTime < 0 ) {
740                         break;
741                     }
742                 }
743                 current->cntDatagrams = stats->cntDatagrams;
744                 current->cntError = stats->cntError;
745                 current->cntOutofOrder = stats->cntOutofOrder;
746                 current->TotalLen = stats->TotalLen;
747                 current->mFormat = stats->mFormat;
748                 current->endTime = stats->endTime;
749                 current->jitter = stats->jitter;
750                 current->startTime = stats->startTime;
751                 current->free = 1;
752             } else {
753                 current->cntDatagrams += stats->cntDatagrams;
754                 current->cntError += stats->cntError;
755                 current->cntOutofOrder += stats->cntOutofOrder;
756                 current->TotalLen += stats->TotalLen;
757                 current->mFormat = stats->mFormat;
758                 if ( current->endTime < stats->endTime ) {
759                     current->endTime = stats->endTime;
760                 }
761                 if ( current->jitter < stats->jitter ) {
762                     current->jitter = stats->jitter;
763                 }
764                 current->free++;
765                 if ( current->free == reporthdr->threads ) {
766                     void *reserved = reporthdr->report->info.reserved_delay;
767                     current->free = force;
768                     memcpy( &reporthdr->report->info, current, sizeof(Transfer_Info) );
769                     current->startTime = -1;
770                     reporthdr->report->info.reserved_delay = reserved;
771                     reporter_print( reporthdr->report, MULTIPLE_REPORT, force );
772                 }
773             }
774         }
775     }
776 }
777
778 /*
779  * Prints reports conditionally
780  */
781 int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int force ) {
782     if ( force != 0 ) {
783         stats->info.cntOutofOrder = stats->cntOutofOrder;
784         // assume most of the time out-of-order packets are not
785         // duplicate packets, so conditionally subtract them from the lost packets.
786         stats->info.cntError = stats->cntError;
787         if ( stats->info.cntError > stats->info.cntOutofOrder ) {
788             stats->info.cntError -= stats->info.cntOutofOrder;
789         }
790         if (isConnectionLess(stats))
791                 stats->info.cntDatagrams = stats->PacketID;
792         else
793                 stats->info.cntDatagrams = stats->cntDatagrams;
794         stats->info.TotalLen  = stats->TotalLen;
795         stats->info.startTime = 0;
796         stats->info.endTime   = TimeDifference( stats->packetTime, stats->startTime );
797         stats->info.free      = 1;
798         reporter_print( stats, TRANSFER_REPORT, force );
799         if ( isMultipleReport(stats) ) {
800             reporter_handle_multiple_reports( multireport, &stats->info, force );
801         }
802     } else while ((stats->intervalTime.tv_sec != 0 || 
803                    stats->intervalTime.tv_usec != 0) && 
804                   TimeDifference( stats->nextTime, 
805                                   stats->packetTime ) < 0 ) {
806         stats->info.cntOutofOrder = stats->cntOutofOrder - stats->lastOutofOrder;
807         stats->lastOutofOrder = stats->cntOutofOrder;
808         // assume most of the time out-of-order packets are not
809         // duplicate packets, so conditionally subtract them from the lost packets.
810         stats->info.cntError = stats->cntError - stats->lastError;
811         if ( stats->info.cntError > stats->info.cntOutofOrder ) {
812             stats->info.cntError -= stats->info.cntOutofOrder;
813         }
814         stats->lastError = stats->cntError;
815         if (isConnectionLess(stats)) {
816                 stats->info.cntDatagrams = stats->PacketID - stats->lastDatagrams;
817                 stats->lastDatagrams     = stats->PacketID;
818         } else {
819                 stats->info.cntDatagrams = stats->cntDatagrams - stats->lastDatagrams;
820                 stats->lastDatagrams     = stats->cntDatagrams;
821         }
822         stats->info.TotalLen = stats->TotalLen - stats->lastTotal;
823         stats->lastTotal = stats->TotalLen;
824         stats->info.startTime = stats->info.endTime;
825         stats->info.endTime = TimeDifference( stats->nextTime, stats->startTime );
826         TimeAdd( stats->nextTime, stats->intervalTime );
827         stats->info.free = 0;
828         reporter_print( stats, TRANSFER_REPORT, force );
829         if ( isMultipleReport(stats) ) {
830             reporter_handle_multiple_reports( multireport, &stats->info, force );
831         }
832     }
833     return force;
834 }
835
836 /*
837  * This function handles multiple format printing by sending to the
838  * appropriate dispatch function
839  */
840 int reporter_print( ReporterData *stats, int type, int end ) {
841     switch ( type ) {
842         case TRANSFER_REPORT:
843             statistics_reports[stats->mode]( &stats->info );
844             if ( end != 0 && isPrintMSS( stats ) && !isConnectionLess( stats ) ) {
845                 PrintMSS( stats );
846             }
847             break;
848         case SERVER_RELAY_REPORT:
849             serverstatistics_reports[stats->mode]( &stats->connection, &stats->info );
850             break;
851         case SETTINGS_REPORT:
852             settings_reports[stats->mode]( stats );
853             break;
854         case CONNECTION_REPORT:
855             stats->info.reserved_delay = connection_reports[stats->mode]( 
856                                                &stats->connection,
857                                                stats->info.transferID );
858             break;
859         case MULTIPLE_REPORT:
860             multiple_reports[stats->mode]( &stats->info );
861             break;
862         default:
863             fprintf( stderr, "Printing type not implemented! No Output\n" );
864     }
865     fflush( stdout );
866     return end;
867 }
868
869 /* -------------------------------------------------------------------
870  * Report the MSS and MTU, given the MSS (or a guess thereof)
871  * This works for connection-oriented protocols only: it expects
872  * the protocol to be either TCP or DCCP and will give error otherwise
873  * ------------------------------------------------------------------- */
874
875 // compare the MSS against the (MTU - 40) to (MTU - 80) bytes.
876 // 40 byte IP header and somewhat arbitrarily, 40 more bytes of IP options.
877
878 #define checkMSS_MTU( inMSS, inMTU ) (inMTU-40) >= inMSS  &&  inMSS >= (inMTU-80)
879
880 void PrintMSS( ReporterData *stats )
881 {
882     int inMSS = stats->mProtocol == kProto_TCP
883               ?   getsock_tcp_mss( stats->info.transferID )
884               :   getsock_dccp_mps( stats->info.transferID );
885
886     if ( inMSS <= 0 ) {
887         printf( report_mss_unsupported, stats->info.transferID );
888     } else {
889         char* net;
890         int mtu = 0;
891
892         if ( checkMSS_MTU( inMSS, 1500 ) ) {
893             net = "ethernet";
894             mtu = 1500;
895         } else if ( checkMSS_MTU( inMSS, 4352 ) ) {
896             net = "FDDI";
897             mtu = 4352;
898         } else if ( checkMSS_MTU( inMSS, 9180 ) ) {
899             net = "ATM";
900             mtu = 9180;
901         } else if ( checkMSS_MTU( inMSS, 65280 ) ) {
902             net = "HIPPI";
903             mtu = 65280;
904         } else if ( checkMSS_MTU( inMSS, 576 ) ) {
905             net = "minimum";
906             mtu = 576;
907             printf( warn_no_pathmtu );
908         } else {
909             mtu = inMSS + 40;
910             net = "unknown interface";
911         }
912
913         printf( report_mss,
914                 stats->info.transferID, inMSS, mtu, net );
915     }
916 }
917 // end ReportMSS
918
919 #ifdef __cplusplus
920 } /* end extern "C" */
921 #endif