1 /*
2 * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
3 * 2002, 2003, 2004
4 * Ohio University.
5 *
6 * ---
7 *
8 * Starting with the release of tcptrace version 6 in 2001, tcptrace
9 * is licensed under the GNU General Public License (GPL). We believe
10 * that, among the available licenses, the GPL will do the best job of
11 * allowing tcptrace to continue to be a valuable, freely-available
12 * and well-maintained tool for the networking community.
13 *
14 * Previous versions of tcptrace were released under a license that
15 * was much less restrictive with respect to how tcptrace could be
16 * used in commercial products. Because of this, I am willing to
17 * consider alternate license arrangements as allowed in Section 10 of
18 * the GNU GPL. Before I would consider licensing tcptrace under an
19 * alternate agreement with a particular individual or company,
20 * however, I would have to be convinced that such an alternative
21 * would be to the greater benefit of the networking community.
22 *
23 * ---
24 *
25 * This file is part of Tcptrace.
26 *
27 * Tcptrace was originally written and continues to be maintained by
28 * Shawn Ostermann with the help of a group of devoted students and
29 * users (see the file 'THANKS'). The work on tcptrace has been made
30 * possible over the years through the generous support of NASA GRC,
31 * the National Science Foundation, and Sun Microsystems.
32 *
33 * Tcptrace is free software; you can redistribute it and/or modify it
34 * under the terms of the GNU General Public License as published by
35 * the Free Software Foundation; either version 2 of the License, or
36 * (at your option) any later version.
37 *
38 * Tcptrace is distributed in the hope that it will be useful, but
39 * WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41 * General Public License for more details.
42 *
43 * You should have received a copy of the GNU General Public License
44 * along with Tcptrace (in the file 'COPYING'); if not, write to the
45 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46 * MA 02111-1307 USA
47 *
48 * Author: Shawn Ostermann
49 * School of Electrical Engineering and Computer Science
50 * Ohio University
51 * Athens, OH
52 * ostermann@cs.ohiou.edu
53 * http://www.tcptrace.org/
54 */
55 #include "tcptrace.h"
56 static char const GCC_UNUSED copyright[] =
57 "@(#)Copyright (c) 2004 -- Ohio University.\n";
58 static char const GCC_UNUSED rcsid[] =
59 "@(#)$Header$";
60
61
62 #include "gcache.h"
63
64
65 /* local routines */
66 static double Average(double, int);
67 static double Stdev(double, double, int);
68 static void StatLineP(char *, char *, char *, void *, void *);
69 static void StatLineI_L(char *, char *, u_long, u_long);
70 #ifdef HAVE_LONG_LONG
71 static void StatLineI_LL(char *, char *, u_llong, u_llong);
72 static void StatLineFieldL(char *, char *, char *, u_llong, int);
73 #endif /* HAVE_LONG_LONG */
74 static void StatLineF(char *, char *, char *, double, double);
75 static void StatLineField(char *, char *, char *, u_long, int);
76 static void StatLineFieldF(char *, char *, char *, double, int);
77 static void StatLineOne(char *, char *, char *);
78 static char *FormatBrief(tcp_pair *ptp);
79 static char *UDPFormatBrief(udp_pair *pup);
80
81 /* locally global variables*/
82 static u_int sv_print_count = 0;
83 static u_int sv_expected_count = 0;
84
85 /* global variables */
86 char *sp; /* Separator used for long output with <SP>-separated-values */
87
88 /* to support some of the counters being long long on some platforms, use this */
89 /* macro... */
90 #ifdef HAVE_LONG_LONG
91 #define StatLineI(label,units,ul1,ul2) \
92 (sizeof((ul1)) == SIZEOF_UNSIGNED_LONG_LONG_INT)?\
93 StatLineI_LL((label),(units),(ul1),(ul2)):\
94 StatLineI_L ((label),(units),(ul1),(ul2))
95 #else /* HAVE_LONG_LONG */
96 #define StatLineI StatLineI_L
97 #endif /* HAVE_LONG_LONG */
98
99 u_int
SynCount(tcp_pair * ptp)100 SynCount(
101 tcp_pair *ptp)
102 {
103 tcb *pab = &ptp->a2b;
104 tcb *pba = &ptp->b2a;
105
106 return(((pab->syn_count >= 1)?1:0) +
107 ((pba->syn_count >= 1)?1:0));
108 }
109
110
111
112 u_int
FinCount(tcp_pair * ptp)113 FinCount(
114 tcp_pair *ptp)
115 {
116 tcb *pab = &ptp->a2b;
117 tcb *pba = &ptp->b2a;
118
119 return(((pab->fin_count >= 1)?1:0) +
120 ((pba->fin_count >= 1)?1:0));
121 }
122
123
124
125 int
ConnComplete(tcp_pair * ptp)126 ConnComplete(
127 tcp_pair *ptp)
128 {
129 return(SynCount(ptp) >= 2 && FinCount(ptp) >= 2);
130 }
131
132
133 int
ConnReset(tcp_pair * ptp)134 ConnReset(
135 tcp_pair *ptp)
136 {
137 return(ptp->a2b.reset_count + ptp->b2a.reset_count != 0);
138 }
139
140
141
142 static double
Average(double sum,int count)143 Average(
144 double sum,
145 int count)
146 {
147 return((double) sum / ((double)count+.0001));
148 }
149
150
151
152 static double
Stdev(double sum,double sum2,int n)153 Stdev(
154 double sum,
155 double sum2,
156 int n)
157 {
158 double term;
159 double term1;
160 double term2;
161 double retval;
162
163 if (n<=2)
164 return(0.0);
165
166 term1 = sum2;
167 term2 = (sum * sum) / (double)n;
168 term = term1-term2;
169 term /= (double)(n-1);
170 retval = sqrt(term);
171
172 /* printf("Stdev(%f,%f,%d) is %f\n", sum,sum2,n,retval); */
173
174 return(retval);
175 }
176
177
178
179
180
181 static char *
FormatBrief(tcp_pair * ptp)182 FormatBrief(
183 tcp_pair *ptp)
184 {
185 tcb *pab = &ptp->a2b;
186 tcb *pba = &ptp->b2a;
187 static char infobuf[100];
188
189 snprintf(infobuf,sizeof(infobuf),"%s - %s (%s2%s)",
190 ptp->a_endpoint, ptp->b_endpoint,
191 pab->host_letter, pba->host_letter);
192 return(infobuf);
193 }
194
195
196
197
198
199 void
PrintTrace(tcp_pair * ptp)200 PrintTrace(
201 tcp_pair *ptp)
202 {
203 double etime;
204 u_long etime_secs;
205 u_long etime_usecs;
206 double etime_data1;
207 double etime_data2;
208 tcb *pab = &ptp->a2b;
209 tcb *pba = &ptp->b2a;
210 char *host1 = pab->host_letter;
211 char *host2 = pba->host_letter;
212 char bufl[40],bufr[40];
213
214 /* counters to use for seq. space wrap around calculations
215 */
216 u_llong stream_length_pab=0, stream_length_pba=0;
217 u_long pab_last, pba_last;
218
219 /* Reset the counter for each connection */
220 sv_print_count = 1; /* The first field (conn_#) gets printed in trace.c */
221
222
223 /* calculate elapsed time */
224 etime = elapsed(ptp->first_time,ptp->last_time);
225 etime_secs = etime / 1000000.0;
226 etime_usecs = 1000000 * (etime/1000000.0 - (double)etime_secs);
227
228 /* Check if comma-separated-values or tab-separated-values
229 * has been requested.
230 */
231 if(csv || tsv || (sv != NULL)) {
232 fprintf(stdout,"%s%s%s%s%s%s%s%s",
233 ptp->a_hostname, sp, ptp->b_hostname, sp,
234 ptp->a_portname, sp, ptp->b_portname, sp);
235 sv_print_count += 4;
236 /* Print the start and end times. In other words,
237 * print the time of the first and the last packet
238 */
239 fprintf(stdout,"%ld.%ld %s %ld.%ld %s",
240 (long)ptp->first_time.tv_sec, (long)ptp->first_time.tv_usec, sp,
241 (long)ptp->last_time.tv_sec, (long)ptp->last_time.tv_usec, sp);
242 sv_print_count += 2;
243 }
244 else {
245 fprintf(stdout,"\thost %-4s %s\n",
246 (snprintf(bufl,sizeof(bufl),"%s:", host1),bufl), ptp->a_endpoint);
247 fprintf(stdout,"\thost %-4s %s\n",
248 (snprintf(bufl,sizeof(bufl),"%s:", host2),bufl), ptp->b_endpoint);
249 fprintf(stdout,"\tcomplete conn: %s",
250 ConnReset(ptp)?"RESET":(
251 ConnComplete(ptp)?"yes":"no"));
252 if (ConnComplete(ptp))
253 fprintf(stdout,"\n");
254 else
255 fprintf(stdout,"\t(SYNs: %u) (FINs: %u)\n",
256 SynCount(ptp), FinCount(ptp));
257
258 fprintf(stdout,"\tfirst packet: %s\n", ts2ascii(&ptp->first_time));
259 fprintf(stdout,"\tlast packet: %s\n", ts2ascii(&ptp->last_time));
260
261 fprintf(stdout,"\telapsed time: %s\n", elapsed2str(etime));
262
263 fprintf(stdout,"\ttotal packets: %" FS_ULL "\n", ptp->packets);
264
265 fprintf(stdout,"\tfilename: %s\n", ptp->filename);
266
267 fprintf(stdout," %s->%s: %s->%s:\n",
268 host1,host2,host2,host1);
269 }
270
271 StatLineI("total packets","", pab->packets, pba->packets);
272 if (pab->reset_count || pba->reset_count || csv || tsv || (sv != NULL))
273 StatLineI("resets sent","", pab->reset_count, pba->reset_count);
274 StatLineI("ack pkts sent","", pab->ack_pkts, pba->ack_pkts);
275 StatLineI("pure acks sent","", pab->pureack_pkts, pba->pureack_pkts);
276 StatLineI("sack pkts sent","", pab->num_sacks, pba->num_sacks);
277 StatLineI("dsack pkts sent","", pab->num_dsacks, pba->num_dsacks);
278 StatLineI("max sack blks/ack","", pab->max_sack_blocks, pba->max_sack_blocks);
279 StatLineI("unique bytes sent","",
280 pab->unique_bytes, pba->unique_bytes);
281 StatLineI("actual data pkts","", pab->data_pkts, pba->data_pkts);
282 StatLineI("actual data bytes","", pab->data_bytes, pba->data_bytes);
283 StatLineI("rexmt data pkts","", pab->rexmit_pkts, pba->rexmit_pkts);
284 StatLineI("rexmt data bytes","",
285 pab->rexmit_bytes, pba->rexmit_bytes);
286 StatLineI("zwnd probe pkts","",
287 pab->num_zwnd_probes, pba->num_zwnd_probes);
288 StatLineI("zwnd probe bytes","",
289 pab->zwnd_probe_bytes, pba->zwnd_probe_bytes);
290 StatLineI("outoforder pkts","",
291 pab->out_order_pkts, pba->out_order_pkts);
292 StatLineI("pushed data pkts","", pab->data_pkts_push, pba->data_pkts_push);
293 StatLineP("SYN/FIN pkts sent","","%s",
294 (snprintf(bufl,sizeof(bufl),"%d/%d",
295 pab->syn_count, pab->fin_count),bufl),
296 (snprintf(bufr,sizeof(bufr),"%d/%d",
297 pba->syn_count, pba->fin_count),bufr));
298 if (pab->f1323_ws || pba->f1323_ws || pab->f1323_ts || pba->f1323_ts || csv || tsv || (sv != NULL)) {
299 StatLineP("req 1323 ws/ts","","%s",
300 (snprintf(bufl,sizeof(bufl),"%c/%c",
301 pab->f1323_ws?'Y':'N',pab->f1323_ts?'Y':'N'),bufl),
302 (snprintf(bufr,sizeof(bufr),"%c/%c",
303 pba->f1323_ws?'Y':'N',pba->f1323_ts?'Y':'N'),bufr));
304 }
305 if (pab->f1323_ws || pba->f1323_ws || csv || tsv || (sv != NULL)) {
306 StatLineI("adv wind scale","",
307 (u_long)pab->window_scale, (u_long)pba->window_scale);
308 }
309 if (pab->fsack_req || pba->fsack_req || csv || tsv || (sv != NULL)) {
310 StatLineP("req sack","","%s",
311 pab->fsack_req?"Y":"N",
312 pba->fsack_req?"Y":"N");
313 StatLineI("sacks sent","",
314 pab->sacks_sent,
315 pba->sacks_sent);
316 }
317 StatLineI("urgent data pkts", "pkts",
318 pab->urg_data_pkts,
319 pba->urg_data_pkts);
320 StatLineI("urgent data bytes", "bytes",
321 pab->urg_data_bytes,
322 pba->urg_data_bytes);
323 StatLineI("mss requested","bytes", pab->mss, pba->mss);
324 StatLineI("max segm size","bytes",
325 pab->max_seg_size,
326 pba->max_seg_size);
327 StatLineI("min segm size","bytes",
328 pab->min_seg_size,
329 pba->min_seg_size);
330 StatLineI("avg segm size","bytes",
331 (int)((double)pab->data_bytes / ((double)pab->data_pkts+.001)),
332 (int)((double)pba->data_bytes / ((double)pba->data_pkts+.001)));
333 StatLineI("max win adv","bytes", pab->win_max, pba->win_max);
334 StatLineI("min win adv","bytes", pab->win_min, pba->win_min);
335 StatLineI("zero win adv","times",
336 pab->win_zero_ct, pba->win_zero_ct);
337 // Average window advertisement is calculated only for window scaled pkts
338 // if we have seen this connection using window scaling.
339 // Otherwise, it is just the regular way of dividing the sum of
340 // all window advertisements by the total number of packets.
341
342 if (pab->window_stats_updated_for_scaling &&
343 pba->window_stats_updated_for_scaling)
344 StatLineI("avg win adv","bytes",
345 pab->win_scaled_pkts==0?0:
346 (pab->win_tot/pab->win_scaled_pkts),
347 pba->win_scaled_pkts==0?0:
348 (pba->win_tot/pba->win_scaled_pkts));
349 else
350 StatLineI("avg win adv","bytes",
351 pab->packets==0?0:pab->win_tot/pab->packets,
352 pba->packets==0?0:pba->win_tot/pba->packets);
353 if (print_owin) {
354 StatLineI("max owin","bytes", pab->owin_max, pba->owin_max);
355 StatLineI("min non-zero owin","bytes", pab->owin_min, pba->owin_min);
356 StatLineI("avg owin","bytes",
357 pab->owin_count==0?0:pab->owin_tot/pab->owin_count,
358 pba->owin_count==0?0:pba->owin_tot/pba->owin_count);
359 if (etime == 0.0) {
360 StatLineP("wavg owin", "", "%s", "NA", "NA");
361 }
362 else {
363 StatLineI("wavg owin","bytes",
364 (u_llong)(pab->owin_wavg/((double)etime/1000000)),
365 (u_llong)(pba->owin_wavg/((double)etime/1000000)));
366 }
367 }
368 StatLineI("initial window","bytes",
369 pab->initialwin_bytes, pba->initialwin_bytes);
370 StatLineI("initial window","pkts",
371 pab->initialwin_segs, pba->initialwin_segs);
372
373 /* compare to theoretical length of the stream (not just what
374 we saw) using the SYN and FIN
375 * Seq. Space wrap around calculations:
376 * Calculate stream length using last_seq_num seen, first_seq_num
377 * seen and wrap_count.
378 * first_seq_num = syn
379 * If reset_set, last_seq_num = latest_seq
380 * else last_seq_num = fin
381 */
382
383 pab_last = (pab->reset_count>0)?pab->latest_seq:pab->fin;
384
385 pba_last = (pba->reset_count>0)?pba->latest_seq:pba->fin;
386
387 /* calculating stream length for direction pab */
388 if ((pab->syn_count > 0) && (pab->fin_count > 0)) {
389 if (pab->seq_wrap_count > 0) {
390 if (pab_last > pab->syn) {
391 stream_length_pab = pab_last + (MAX_32 * pab->seq_wrap_count) - pab->syn - 1;
392 }
393 else {
394 stream_length_pab = pab_last + (MAX_32 * (pab->seq_wrap_count+1)) - pab->syn - 1;
395 }
396 }
397 else {
398 if (pab_last > pab->syn) {
399 stream_length_pab = pab_last - pab->syn - 1;
400 }
401 else {
402 stream_length_pab = MAX_32 + pab_last - pab->syn - 1;
403 }
404 }
405 }
406
407 /* calculating stream length for direction pba */
408 if ((pba->syn_count > 0) && (pba->fin_count > 0)) {
409 if (pba->seq_wrap_count > 0) {
410 if (pba_last > pba->syn) {
411 stream_length_pba = pba_last + (MAX_32 * pba->seq_wrap_count) - pba->syn - 1;
412 }
413 else {
414 stream_length_pba = pba_last + (MAX_32 * (pba->seq_wrap_count+1)) - pba->syn - 1;
415 }
416 }
417 else {
418 if (pba_last > pba->syn) {
419 stream_length_pba = pba_last - pba->syn - 1;
420 }
421 else {
422 stream_length_pba = MAX_32 + pba_last - pba->syn - 1;
423 }
424 }
425 }
426
427 /* print out values */
428 if ((pab->fin_count > 0) && (pab->syn_count > 0)) {
429 char *format = "%8" FS_ULL;
430 StatLineFieldL("ttl stream length", "bytes", format, stream_length_pab, 0);
431 }
432 else {
433 StatLineField("ttl stream length", "", "%s", (u_long)"NA", 0);
434 }
435 if ((pba->fin_count > 0) && (pba->syn_count > 0)) {
436 char *format = "%8" FS_ULL;
437 StatLineFieldL("ttl stream length", "bytes", format, stream_length_pba, 1);
438 }
439 else {
440 StatLineField("ttl stream length", "", "%s", (u_long)"NA", 1);
441 }
442
443 if ((pab->fin_count > 0) && (pab->syn_count > 0)) {
444 char *format = "%8" FS_ULL;
445 StatLineFieldL("missed data", "bytes", format, (stream_length_pab - pab->unique_bytes), 0);
446 }
447 else {
448 StatLineField("missed data", "", "%s", (u_long)"NA", 0);
449 }
450 if ((pba->fin_count > 0) && (pba->syn_count > 0)) {
451 char *format = "%8" FS_ULL;
452 StatLineFieldL("missed data", "bytes", format, (stream_length_pba - pba->unique_bytes), 1);
453 }
454 else {
455 StatLineField("missed data", "", "%s", (u_long)"NA", 1);
456 }
457
458 /* tell how much data was NOT captured in the files */
459 StatLineI("truncated data","bytes",
460 pab->trunc_bytes, pba->trunc_bytes);
461 StatLineI("truncated packets","pkts",
462 pab->trunc_segs, pba->trunc_segs);
463
464 /* stats on just the data */
465 etime_data1 = elapsed(pab->first_data_time,
466 pab->last_data_time); /* in usecs */
467 etime_data2 = elapsed(pba->first_data_time,
468 pba->last_data_time); /* in usecs */
469 /* fix from Rob Austein */
470 StatLineF("data xmit time","secs","%7.3f",
471 etime_data1 / 1000000.0,
472 etime_data2 / 1000000.0);
473 StatLineP("idletime max","ms","%s",
474 ZERO_TIME(&pab->last_time)?"NA":
475 (snprintf(bufl,sizeof(bufl),"%8.1f",(double)pab->idle_max/1000.0),bufl),
476 ZERO_TIME(&pba->last_time)?"NA":
477 (snprintf(bufr,sizeof(bufr),"%8.1f",(double)pba->idle_max/1000.0),bufr));
478
479 if ((pab->num_hardware_dups != 0) || (pba->num_hardware_dups != 0) || csv || tsv || (sv != NULL)) {
480 StatLineI("hardware dups","segs",
481 pab->num_hardware_dups, pba->num_hardware_dups);
482
483 if(!(csv || tsv || (sv != NULL)))
484 fprintf(stdout,
485 " ** WARNING: presence of hardware duplicates makes these figures suspect!\n");
486 }
487
488 /* do the throughput calcs */
489 etime /= 1000000.0; /* convert to seconds */
490 if (etime == 0.0)
491 StatLineP("throughput","","%s","NA","NA");
492 else
493 StatLineF("throughput","Bps","%8.0f",
494 (double) (pab->unique_bytes) / etime,
495 (double) (pba->unique_bytes) / etime);
496
497 if (print_rtt) {
498 if(!(csv || tsv || (sv != NULL)))
499 fprintf(stdout,"\n");
500 StatLineI("RTT samples","", pab->rtt_count, pba->rtt_count);
501 StatLineF("RTT min","ms","%8.1f",
502 (double)pab->rtt_min/1000.0,
503 (double)pba->rtt_min/1000.0);
504 StatLineF("RTT max","ms","%8.1f",
505 (double)pab->rtt_max/1000.0,
506 (double)pba->rtt_max/1000.0);
507 StatLineF("RTT avg","ms","%8.1f",
508 Average(pab->rtt_sum, pab->rtt_count) / 1000.0,
509 Average(pba->rtt_sum, pba->rtt_count) / 1000.0);
510 StatLineF("RTT stdev","ms","%8.1f",
511 Stdev(pab->rtt_sum, pab->rtt_sum2, pab->rtt_count) / 1000.0,
512 Stdev(pba->rtt_sum, pba->rtt_sum2, pba->rtt_count) / 1000.0);
513 if(!(csv || tsv || (sv != NULL)))
514 fprintf(stdout,"\n");
515 StatLineF("RTT from 3WHS","ms","%8.1f",
516 (double)pab->rtt_3WHS/1000.0,
517 (double)pba->rtt_3WHS/1000.0);
518 if(!(csv || tsv || (sv != NULL)))
519 fprintf(stdout,"\n");
520 StatLineI("RTT full_sz smpls","",
521 pab->rtt_full_count, pba->rtt_full_count);
522 StatLineF("RTT full_sz min","ms","%8.1f",
523 (double)pab->rtt_full_min/1000.0,
524 (double)pba->rtt_full_min/1000.0);
525 StatLineF("RTT full_sz max","ms","%8.1f",
526 (double)pab->rtt_full_max/1000.0,
527 (double)pba->rtt_full_max/1000.0);
528 StatLineF("RTT full_sz avg","ms","%8.1f",
529 Average(pab->rtt_full_sum, pab->rtt_full_count) / 1000.0,
530 Average(pba->rtt_full_sum, pba->rtt_full_count) / 1000.0);
531 StatLineF("RTT full_sz stdev","ms","%8.1f",
532 Stdev(pab->rtt_full_sum, pab->rtt_full_sum2, pab->rtt_full_count) / 1000.0,
533 Stdev(pba->rtt_full_sum, pba->rtt_full_sum2, pba->rtt_full_count) / 1000.0);
534 if(!(csv || tsv || (sv != NULL)))
535 fprintf(stdout,"\n");
536 StatLineI("post-loss acks","",
537 pab->rtt_nosample, pba->rtt_nosample);
538 if (pab->rtt_amback || pba->rtt_amback || csv || tsv || (sv != NULL)) {
539 if(!(csv || tsv || (sv != NULL)))
540 fprintf(stdout, "\
541 \t For the following 5 RTT statistics, only ACKs for\n\
542 \t multiply-transmitted segments (ambiguous ACKs) were\n\
543 \t considered. Times are taken from the last instance\n\
544 \t of a segment.\n\
545 ");
546 StatLineI("ambiguous acks","",
547 pab->rtt_amback, pba->rtt_amback);
548 StatLineF("RTT min (last)","ms","%8.1f",
549 (double)pab->rtt_min_last/1000.0,
550 (double)pba->rtt_min_last/1000.0);
551 StatLineF("RTT max (last)","ms","%8.1f",
552 (double)pab->rtt_max_last/1000.0,
553 (double)pba->rtt_max_last/1000.0);
554 StatLineF("RTT avg (last)","ms","%8.1f",
555 Average(pab->rtt_sum_last, pab->rtt_count_last) / 1000.0,
556 Average(pba->rtt_sum_last, pba->rtt_count_last) / 1000.0);
557 StatLineF("RTT sdv (last)","ms","%8.1f",
558 Stdev(pab->rtt_sum_last, pab->rtt_sum2_last, pab->rtt_count_last) / 1000.0,
559 Stdev(pba->rtt_sum_last, pba->rtt_sum2_last, pba->rtt_count_last) / 1000.0);
560
561 }
562 StatLineI("segs cum acked","",
563 pab->rtt_cumack, pba->rtt_cumack);
564 StatLineI("duplicate acks","",
565 pab->rtt_dupack, pba->rtt_dupack);
566 StatLineI("triple dupacks","",
567 pab->rtt_triple_dupack, pba->rtt_triple_dupack);
568 if (debug)
569 StatLineI("unknown acks:","",
570 pab->rtt_unkack, pba->rtt_unkack);
571 StatLineI("max # retrans","",
572 pab->retr_max, pba->retr_max);
573 StatLineF("min retr time","ms","%8.1f",
574 (double)((double)pab->retr_min_tm/1000.0),
575 (double)((double)pba->retr_min_tm/1000.0));
576 StatLineF("max retr time","ms","%8.1f",
577 (double)((double)pab->retr_max_tm/1000.0),
578 (double)((double)pba->retr_max_tm/1000.0));
579 StatLineF("avg retr time","ms","%8.1f",
580 Average(pab->retr_tm_sum, pab->retr_tm_count) / 1000.0,
581 Average(pba->retr_tm_sum, pba->retr_tm_count) / 1000.0);
582 StatLineF("sdv retr time","ms","%8.1f",
583 Stdev(pab->retr_tm_sum, pab->retr_tm_sum2,
584 pab->retr_tm_count) / 1000.0,
585 Stdev(pba->retr_tm_sum, pba->retr_tm_sum2,
586 pba->retr_tm_count) / 1000.0);
587 }
588
589 if(csv || tsv || (sv != NULL)) {
590 printf("\n");
591 /* Error checking: print an error message if the count of printed fields
592 * doesn't correspond to the actual fields expected.
593 */
594 if(sv_print_count != sv_expected_count) {
595 fprintf(stderr, "output.c: Count of printed fields does not correspond to count of header fields for long output with comma/tab/<SP>-separated values.\n");
596 fprintf(stderr,"sv_print_count=%u, sv_expected_count=%u\n",
597 sv_print_count, sv_expected_count);
598 exit(-1);
599 }
600 }
601 }
602
603 void
PrintBrief(tcp_pair * ptp)604 PrintBrief(
605 tcp_pair *ptp)
606 {
607 tcb *pab = &ptp->a2b;
608 tcb *pba = &ptp->b2a;
609 static int max_width = -1;
610
611 /* determine the maximum connection name width to make it nice */
612 if (max_width == -1) {
613 int ix;
614 int len;
615 tcp_pair *tmp_ptp;
616
617 for (ix = 0; ix <= num_tcp_pairs; ++ix) {
618 tmp_ptp = ttp[ix];
619 if (tmp_ptp->ignore_pair)
620 continue;
621
622 len = strlen(FormatBrief(tmp_ptp));
623 if (len > max_width)
624 max_width = len;
625 }
626 if (debug > 2)
627 fprintf(stderr,"Max name width: %d\n", max_width);
628 }
629
630
631 if (TRUE) {
632 /* new version */
633 fprintf(stdout,"%*s", -max_width, FormatBrief(ptp));
634
635 fprintf(stdout," %4" FS_ULL ">", pab->packets);
636 fprintf(stdout," %4" FS_ULL "<", pba->packets);
637
638 } else {
639 /* old version */
640 fprintf(stdout,"%s <==> %s",
641 ptp->a_endpoint,
642 ptp->b_endpoint);
643
644 fprintf(stdout," %s2%s:%"FS_ULL,
645 pab->host_letter,
646 pba->host_letter,
647 pab->packets);
648
649 fprintf(stdout," %s2%s:%"FS_ULL,
650 pba->host_letter,
651 pab->host_letter,
652 pba->packets);
653 }
654 if (ConnComplete(ptp))
655 fprintf(stdout," (complete)");
656 if (ConnReset(ptp))
657 fprintf(stdout," (reset)");
658 if ((ptp->a2b.packets == 0) || (ptp->b2a.packets == 0))
659 fprintf(stdout," (unidirectional)");
660
661 fprintf(stdout,"\n");
662
663 /* warning for hardware duplicates */
664 if (pab->num_hardware_dups != 0) {
665 fprintf(stdout,
666 " ** Warning, %s2%s: detected %lu hardware duplicate(s) (same seq # and IP ID)\n",
667 pab->host_letter, pba->host_letter,
668 pab->num_hardware_dups);
669 }
670 if (pba->num_hardware_dups != 0) {
671 fprintf(stdout,
672 " ** Warning, %s2%s: detected %lu hardware duplicate(s) (same seq # and IP ID)\n",
673 pba->host_letter, pab->host_letter,
674 pba->num_hardware_dups);
675 }
676 }
677
678
679
680
681 void
UDPPrintTrace(udp_pair * pup)682 UDPPrintTrace(
683 udp_pair *pup)
684 {
685 double etime;
686 u_long etime_secs;
687 u_long etime_usecs;
688 ucb *pab = &pup->a2b;
689 ucb *pba = &pup->b2a;
690 char *host1 = pab->host_letter;
691 char *host2 = pba->host_letter;
692 char bufl[40];
693
694 fprintf(stdout,"\thost %-4s %s\n",
695 (snprintf(bufl,sizeof(bufl),"%s:", host1),bufl), pup->a_endpoint);
696 fprintf(stdout,"\thost %-4s %s\n",
697 (snprintf(bufl,sizeof(bufl),"%s:", host2),bufl), pup->b_endpoint);
698 fprintf(stdout,"\n");
699
700 fprintf(stdout,"\tfirst packet: %s\n", ts2ascii(&pup->first_time));
701 fprintf(stdout,"\tlast packet: %s\n", ts2ascii(&pup->last_time));
702
703 etime = elapsed(pup->first_time,pup->last_time);
704 etime_secs = etime / 1000000.0;
705 etime_usecs = 1000000 * (etime/1000000.0 - (double)etime_secs);
706 fprintf(stdout,"\telapsed time: %s\n", elapsed2str(etime));
707
708 fprintf(stdout,"\ttotal packets: %" FS_ULL "\n", pup->packets);
709
710 fprintf(stdout,"\tfilename: %s\n", pup->filename);
711
712 fprintf(stdout," %s->%s: %s->%s:\n",
713 host1,host2,host2,host1);
714
715 StatLineI("total packets","", pab->packets, pba->packets);
716 StatLineI("data bytes sent","",
717 pab->data_bytes, pba->data_bytes);
718
719 /* do the throughput calcs */
720 etime /= 1000000.0; /* convert to seconds */
721 if (etime == 0.0)
722 StatLineP("throughput","","%s","NA","NA");
723 else
724 StatLineF("throughput","Bps","%8.0f",
725 (double) (pab->data_bytes) / etime,
726 (double) (pba->data_bytes) / etime);
727 }
728
729
730 /* with pointer args */
731 static void
StatLineP(char * label,char * units,char * format,void * argleft,void * argright)732 StatLineP(
733 char *label,
734 char *units,
735 char *format,
736 void *argleft,
737 void *argright)
738 {
739 StatLineField(label,units,format,(u_long)argleft,0);
740 StatLineField(label,units,format,(u_long)argright,1);
741 }
742
743
744 /* with u_long args */
745 static void
StatLineI_L(char * label,char * units,u_long argleft,u_long argright)746 StatLineI_L(
747 char *label,
748 char *units,
749 u_long argleft,
750 u_long argright)
751 {
752 char *format = "%8lu";
753 StatLineField(label,units,format,argleft,0);
754 StatLineField(label,units,format,argright,1);
755 }
756
757
758 #ifdef HAVE_LONG_LONG
759 /* with u_llong (long long) args, if supported */
760 static void
StatLineI_LL(char * label,char * units,u_llong argleft,u_llong argright)761 StatLineI_LL(
762 char *label,
763 char *units,
764 u_llong argleft,
765 u_llong argright)
766 {
767 char *format = "%8" FS_ULL;
768 StatLineFieldL(label,units,format,argleft,0);
769 StatLineFieldL(label,units,format,argright,1);
770 }
771
772 static void
StatLineFieldL(char * label,char * units,char * format,u_llong arg,int f_rightside)773 StatLineFieldL(
774 char *label,
775 char *units,
776 char *format,
777 u_llong arg,
778 int f_rightside)
779 {
780 /* bug fix: Theo Snelleman */
781 /* "The biggest number possible is 18446744073709551615 (20 digits) and
782 is too big for valbuf[20] ('\0' is the 21th character)." */
783 /* it was originally an array of [20] */
784 char valbuf[21];
785
786 /* determine the value to print */
787 snprintf(valbuf,sizeof(valbuf),format,arg);
788
789 /* print the field */
790 if(!(csv || tsv || (sv != NULL)))
791 printf(" ");
792 StatLineOne(label, units, valbuf);
793 if (f_rightside && !(csv || tsv || (sv != NULL)))
794 printf("\n");
795 }
796 #endif /* HAVE_LONG_LONG */
797
798
799 static void
StatLineF(char * label,char * units,char * format,double argleft,double argright)800 StatLineF(
801 char *label,
802 char *units,
803 char *format,
804 double argleft,
805 double argright)
806 {
807 StatLineFieldF(label,units,format,argleft,0);
808 StatLineFieldF(label,units,format,argright,1);
809 }
810
811
812
813
814 static void
StatLineField(char * label,char * units,char * format,u_long arg,int f_rightside)815 StatLineField(
816 char *label,
817 char *units,
818 char *format,
819 u_long arg,
820 int f_rightside)
821 {
822 char valbuf[20];
823
824 /* determine the value to print */
825 snprintf(valbuf,sizeof(valbuf),format,arg);
826
827 /* print the field */
828 if(!(csv || tsv || (sv != NULL)))
829 printf(" ");
830 StatLineOne(label, units, valbuf);
831 if (f_rightside && !(csv || tsv || (sv != NULL)))
832 printf("\n");
833 }
834
835
836
837 static void
StatLineFieldF(char * label,char * units,char * format,double arg,int f_rightside)838 StatLineFieldF(
839 char *label,
840 char *units,
841 char *format,
842 double arg,
843 int f_rightside)
844 {
845 int printable;
846 char valbuf[20];
847
848 /* see if the float argument is printable */
849 printable = finite(arg);
850
851 /* determine the value to print */
852 if (printable)
853 snprintf(valbuf,sizeof(valbuf),format,arg);
854
855 /* print the field */
856 if(!(csv || tsv || (sv != NULL)))
857 printf(" ");
858 if (printable)
859 StatLineOne(label, units, valbuf);
860 else
861 StatLineOne(label, "", "NA");
862 if (f_rightside && !(csv || tsv || (sv != NULL)))
863 printf("\n");
864 }
865
866
867 static void
StatLineOne(char * label,char * units,char * value)868 StatLineOne(
869 char *label,
870 char *units,
871 char *value)
872 {
873 char labbuf[20];
874
875 /* format the label */
876 snprintf(labbuf,sizeof(labbuf), "%s:", label);
877
878 /* print the field */
879 if(csv || tsv || (sv != NULL)) {
880 printf("%15s%s", value, sp);
881 /* Count the fields printed until this point. Used as a guard with the
882 * <SP>-separated-values option to ensure correct alignment of headers
883 * and field values.
884 */
885 sv_print_count++;
886 }
887 else
888 printf("%-18s %9s %-5s", labbuf, value, units);
889 }
890
891
892 char *
elapsed2str(double etime)893 elapsed2str(
894 double etime)
895 {
896 static char buf[80];
897 u_long etime_secs;
898 u_long etime_usecs;
899
900 etime_secs = etime / 1000000.0;
901 etime_usecs = 1000000 * (etime/1000000.0 - (double)etime_secs);
902 snprintf(buf,sizeof(buf),"%lu:%02lu:%02lu.%06lu",
903 etime_secs / (60 * 60),
904 etime_secs % (60 * 60) / 60,
905 (etime_secs % (60 * 60)) % 60,
906 etime_usecs);
907 return(buf);
908 }
909
910
911 void
UDPPrintBrief(udp_pair * pup)912 UDPPrintBrief(
913 udp_pair *pup)
914 {
915 ucb *pab = &pup->a2b;
916 ucb *pba = &pup->b2a;
917 static int max_width = -1;
918
919 /* determine the maximum connection name width to make it nice */
920 if (max_width == -1) {
921 int ix;
922 int len;
923 udp_pair *tmp_pup;
924
925 for (ix = 0; ix <= num_udp_pairs; ++ix) {
926 tmp_pup = utp[ix];
927
928 len = strlen(UDPFormatBrief(tmp_pup));
929 if (len > max_width)
930 max_width = len;
931 }
932 if (debug > 2)
933 fprintf(stderr,"Max name width: %d\n", max_width);
934 }
935
936
937 /* new version */
938 fprintf(stdout,"%*s", -max_width, UDPFormatBrief(pup));
939
940 fprintf(stdout," %4" FS_ULL ">", pab->packets);
941 fprintf(stdout," %4" FS_ULL "<", pba->packets);
942
943 fprintf(stdout,"\n");
944 }
945
946
947 static char *
UDPFormatBrief(udp_pair * pup)948 UDPFormatBrief(
949 udp_pair *pup)
950 {
951 ucb *pab = &pup->a2b;
952 ucb *pba = &pup->b2a;
953 static char infobuf[100];
954
955 snprintf(infobuf,sizeof(infobuf),"%s - %s (%s2%s)",
956 pup->a_endpoint, pup->b_endpoint,
957 pab->host_letter, pba->host_letter);
958 return(infobuf);
959 }
960
961
962 /* Print the header if comma-separated-values or tab-separated-values
963 * has been requested.
964 */
965 void
PrintSVHeader(void)966 PrintSVHeader(
967 void)
968 {
969 /* NOTE: If you have added new fields of output to be printed in
970 * PrintTrace(), make sure you update the header-list here too, so that the
971 * new field you have added also has a header in the --csv/--tsv/--sv
972 * output.
973 */
974
975 /* Headers for long output requested with comma/tab/<SP>-separated- values
976 * Split up into two headers, since the OWIN stats printed when the
977 * -lW option is given, generates fields of output that get printed in the
978 * middle of the output generated with just the -l option.
979 */
980
981 char *svHeader1[] = {
982 "conn_#" ,
983 "host_a" , "host_b",
984 "port_a" , "port_b",
985 "first_packet" , "last_packet",
986 "total_packets_a2b" , "total_packets_b2a",
987 "resets_sent_a2b" , "resets_sent_b2a",
988 "ack_pkts_sent_a2b" , "ack_pkts_sent_b2a",
989 "pure_acks_sent_a2b" , "pure_acks_sent_b2a",
990 "sack_pkts_sent_a2b" , "sack_pkts_sent_b2a",
991 "dsack_pkts_sent_a2b" , "dsack_pkts_sent_b2a",
992 "max_sack_blks/ack_a2b" , "max_sack_blks/ack_b2a",
993 "unique_bytes_sent_a2b" , "unique_bytes_sent_b2a",
994 "actual_data_pkts_a2b" , "actual_data_pkts_b2a",
995 "actual_data_bytes_a2b" , "actual_data_bytes_b2a",
996 "rexmt_data_pkts_a2b" , "rexmt_data_pkts_b2a",
997 "rexmt_data_bytes_a2b" , "rexmt_data_bytes_b2a",
998 "zwnd_probe_pkts_a2b" , "zwnd_probe_pkts_b2a",
999 "zwnd_probe_bytes_a2b" , "zwnd_probe_bytes_b2a",
1000 "outoforder_pkts_a2b" , "outoforder_pkts_b2a",
1001 "pushed_data_pkts_a2b" , "pushed_data_pkts_b2a",
1002 "SYN/FIN_pkts_sent_a2b" , "SYN/FIN_pkts_sent_b2a",
1003 "req_1323_ws/ts_a2b" , "req_1323_ws/ts_b2a",
1004 "adv_wind_scale_a2b" , "adv_wind_scale_b2a",
1005 "req_sack_a2b" , "req_sack_b2a",
1006 "sacks_sent_a2b" , "sacks_sent_b2a",
1007 "urgent_data_pkts_a2b" , "urgent_data_pkts_b2a",
1008 "urgent_data_bytes_a2b" , "urgent_data_bytes_b2a",
1009 "mss_requested_a2b" , "mss_requested_b2a",
1010 "max_segm_size_a2b" , "max_segm_size_b2a",
1011 "min_segm_size_a2b" , "min_segm_size_b2a",
1012 "avg_segm_size_a2b" , "avg_segm_size_b2a",
1013 "max_win_adv_a2b" , "max_win_adv_b2a",
1014 "min_win_adv_a2b" , "min_win_adv_b2a",
1015 "zero_win_adv_a2b" , "zero_win_adv_b2a",
1016 "avg_win_adv_a2b" , "avg_win_adv_b2a"
1017 };
1018 #define SV_HEADER1_COLUMN_COUNT (sizeof(svHeader1)/sizeof(char*))
1019
1020 char *svHeader2[] = {
1021 "initial_window_bytes_a2b" , "initial_window_bytes_b2a",
1022 "initial_window_pkts_a2b" , "initial_window_pkts_b2a",
1023 "ttl_stream_length_a2b" , "ttl_stream_length_b2a",
1024 "missed_data_a2b" , "missed_data_b2a",
1025 "truncated_data_a2b" , "truncated_data_b2a",
1026 "truncated_packets_a2b" , "truncated_packets_b2a",
1027 "data_xmit_time_a2b" , "data_xmit_time_b2a",
1028 "idletime_max_a2b" , "idletime_max_b2a",
1029 "hardware_dups_a2b" , "hardware_dups_b2a",
1030 "throughput_a2b" , "throughput_b2a"
1031 };
1032
1033 #define SV_HEADER2_COLUMN_COUNT (sizeof(svHeader2)/sizeof(char*))
1034
1035 /* Headers to be printed if the OWIN stats are requested.
1036 */
1037
1038 char *svOWINHeader[] = {
1039 "max_owin_a2b" , "max_owin_b2a",
1040 "min_non-zero_owin_a2b" , "min_non-zero_owin_b2a",
1041 "avg_owin_a2b" , "avg_owin_b2a",
1042 "wavg_owin_a2b" , "wavg_owin_b2a"
1043 };
1044
1045 #define SV_OWIN_HEADER_COLUMN_COUNT (sizeof(svOWINHeader)/sizeof(char*))
1046
1047
1048 /* Headers for RTT, to be printed for long output requested with
1049 * comma/tab/<SP>-separated- values.
1050 */
1051 char *svRTTHeader[] = {
1052 "RTT_samples_a2b" , "RTT_samples_b2a",
1053 "RTT_min_a2b" , "RTT_min_b2a",
1054 "RTT_max_a2b" , "RTT_max_b2a",
1055 "RTT_avg_a2b" , "RTT_avg_b2a",
1056 "RTT_stdev_a2b" , "RTT_stdev_b2a",
1057 "RTT_from_3WHS_a2b" , "RTT_from_3WHS_b2a",
1058 "RTT_full_sz_smpls_a2b" , "RTT_full_sz_smpls_b2a",
1059 "RTT_full_sz_min_a2b" , "RTT_full_sz_min_b2a",
1060 "RTT_full_sz_max_a2b" , "RTT_full_sz_max_b2a",
1061 "RTT_full_sz_avg_a2b" , "RTT_full_sz_avg_b2a",
1062 "RTT full_sz_stdev_a2b" , "RTT_full_sz_stdev_b2a",
1063 "post-loss_acks_a2b" , "post-loss_acks_b2a",
1064 "ambiguous_acks_a2b" , "ambiguous_acks_b2a",
1065 "RTT_min_(last)_a2b" , "RTT_min_(last)_b2a",
1066 "RTT_max_(last)_a2b" , "RTT_max_(last)_b2a",
1067 "RTT_avg_(last)_a2b" , "RTT_avg_(last)_b2a",
1068 "RTT_sdv_(last)_a2b" , "RTT_sdv_(last)_b2a",
1069 "segs_cum_acked_a2b" , "segs_cum_acked_b2a",
1070 "duplicate_acks_a2b" , "duplicate_acks_b2a",
1071 "triple_dupacks_a2b" , "triple_dupacks_b2a",
1072 "max_#_retrans_a2b" , "max_#_retrans_b2a",
1073 "min_retr_time_a2b" , "min_retr_time_b2a",
1074 "max_retr_time_a2b" , "max_retr_time_b2a",
1075 "avg_retr_time_a2b" , "avg_retr_time_b2a",
1076 "sdv_retr_time_a2b" , "sdv_retr_time_b2a"
1077 };
1078 #define SV_RTT_HEADER_COLUMN_COUNT (sizeof(svRTTHeader)/sizeof(char*))
1079
1080 /* Local Variables */
1081 u_int i = 0; /* Counter */
1082
1083 /* Set the separator */
1084 if(csv || tsv) {
1085 /* Initialize the separator buffer */
1086 sp = (char *)malloc(sizeof(char *) * 2);
1087 memset(sp, 0, sizeof(sp));
1088 /* Set it */
1089 if(csv)
1090 snprintf(sp, sizeof(sp), ",");
1091 else if(tsv)
1092 snprintf(sp, sizeof(sp), "\t");
1093 }
1094 else if (sv != NULL)
1095 {
1096 /* Look for escape sequence and remove the extra '\',
1097 * the shell puts it in there.
1098 * We will do this only for the escape sequence '\t' since that is
1099 * the only useful one, else the user probably meant something
1100 * else and things get messy.
1101 */
1102 if(strncmp(sv, "\\t", 2) == 0) {
1103 /* Initialize the separator buffer and set it */
1104 sp = (char *)malloc(sizeof(char *) * 2);
1105 memset(sp, 0, sizeof(sp));
1106 snprintf(sp, sizeof(sp), "\t");
1107 }
1108 else /* Just use the string the user has provided */
1109 sp = strdup(sv);
1110 }
1111
1112 /* Print the column headings (the field names) */
1113 for (i=0; i<SV_HEADER1_COLUMN_COUNT; i++)
1114 fprintf(stdout, "%s%s", svHeader1[i], sp);
1115
1116 if (print_owin) {
1117 for(i=0; i<SV_OWIN_HEADER_COLUMN_COUNT; i++)
1118 fprintf(stdout, "%s%s", svOWINHeader[i], sp);
1119 }
1120
1121 for (i=0; i<SV_HEADER2_COLUMN_COUNT; i++)
1122 fprintf(stdout, "%s%s", svHeader2[i], sp);
1123
1124
1125 /* Print the RTT column headings (the field names) */
1126 if(print_rtt)
1127 for(i = 0; i < SV_RTT_HEADER_COLUMN_COUNT; i++)
1128 fprintf(stdout, "%s%s", svRTTHeader[i], sp);
1129
1130 /* Improve readability */
1131 fprintf(stdout, "\n\n");
1132
1133 /* Set the number of columns expected to be printed. */
1134 sv_expected_count=SV_HEADER1_COLUMN_COUNT + SV_HEADER2_COLUMN_COUNT;
1135
1136 if (print_rtt)
1137 sv_expected_count += SV_RTT_HEADER_COLUMN_COUNT;
1138
1139 if (print_owin)
1140 sv_expected_count += SV_OWIN_HEADER_COLUMN_COUNT;
1141
1142 if (debug>3) {
1143 fprintf(stderr,"SV_HEADER_COUNT : -l alone = %d\n", \
1144 (int)(SV_HEADER1_COLUMN_COUNT + SV_HEADER2_COLUMN_COUNT));
1145 fprintf(stderr," : -W alone = %d\n", \
1146 (int)SV_OWIN_HEADER_COLUMN_COUNT);
1147 fprintf(stderr," : -r alone = %d\n", \
1148 (int)SV_RTT_HEADER_COLUMN_COUNT);
1149
1150 fprintf(stderr,"sv_expected_count=%u\n",sv_expected_count);
1151 }
1152 }
1153