1 /*
2 * yconalyzer: A program to analyze TCP traffic on a server or client port.
3 *
4 Software Copyright License Agreement (BSD License)
5
6 Copyright (c) 2010, Yahoo! Inc.
7 All rights reserved.
8
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the following
11 conditions are met:
12
13 * Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22 * Neither the name of Yahoo! Inc. nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of Yahoo! Inc.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
28 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
30 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include "yconalyzer.h"
41
42 #define RTT_EST_ENABLED "RTT Estimation enabled.\n"
43 #define MAX_SYNACKS 4
44 #define DEFAULT_MAX_RTT 1100 // in msecs. see max_rtt
45
46 // tcphdr and tcp_seq taken from netinet/tcp.h in BSD
47 typedef u_int32_t tcp_seq;
48 struct tcphdr {
49 u_short th_sport; /* source port */
50 u_short th_dport; /* destination port */
51 tcp_seq th_seq; /* sequence number */
52 tcp_seq th_ack; /* acknowledgement number */
53 #if BYTE_ORDER == LITTLE_ENDIAN
54 u_int th_x2:4, /* (unused) */
55 th_off:4; /* data offset */
56 #endif
57 #if BYTE_ORDER == BIG_ENDIAN
58 u_int th_off:4, /* data offset */
59 th_x2:4; /* (unused) */
60 #endif
61 u_char th_flags;
62 #define TH_FIN 0x01
63 #define TH_SYN 0x02
64 #define TH_RST 0x04
65 #define TH_PUSH 0x08
66 #define TH_ACK 0x10
67 #define TH_URG 0x20
68 #define TH_ECE 0x40
69 #define TH_CWR 0x80
70 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
71
72 u_short th_win; /* window */
73 u_short th_sum; /* checksum */
74 u_short th_urp; /* urgent pointer */
75 };
76
77 #include <string>
78
79 #include <map>
80 using namespace std;
81 // Linux gcc-3 is not too happy with the format strings we use in BSD.
82 #define KEY_FMT_STRING "%#8x%#4x"
83
84 static int debug = 0;
85 static u_short port = 0;
86 static int nbuckets;
87 static int bucket_size;
88 static int lo_bucket = 0;
89 static unsigned long min_srvr_rspsize = 0;
90 static unsigned long min_clnt_reqsize = 0;
91 static unsigned long max_srvr_rspsize = 0;
92 static unsigned long max_clnt_reqsize = 0;
93 static int find_rtt = 0;
94 static bool fromfile = false;
95 static bool raw_output = false;
96 static int gottime = 0;
97 static pcap_dumper_t *dumper = NULL;
98 static int output_version = 0;
99 static int min_conntime = 0;
100 static unsigned long min_conn_attempts = 0;
101 static int done = 0;
102 static enum {
103 REP_DURATION = 1, // sort by duration
104 REP_THRUPUT, // sort by throughput
105 REP_CLBYTES, // sort by bytes sent by client.
106 REP_SRBYTES, // sort by bytes sent by the server.
107 } report_type;
108
109 // Maximum RTT (in msecs). When we try to estimate RTT using the FIN bits in
110 // the packets, we can have errors in RTT estimation. In order to minimize
111 // those errors, if we end up with an RTT number of more than this value, we
112 // should abandon estimating RTT for that connection. It could be that the
113 // client was busy, or somehow failed to send a FIN in time, etc.
114 static int max_rtt = DEFAULT_MAX_RTT;
115
116 #define NBUCKETS 20
117 #define BUCKET_SIZE 20 // msecs
118
119 struct portdet {
120 struct in_addr ipsrc;
121 struct in_addr ipdst;
122 int port;
123 };
124
125 typedef enum {
126 CLOSED = 0,
127 CONNECTING = 1,
128 CONNECTED = 2,
129 CLOSING = 3,
130 } state_t ;
131
132 struct conndet {
133 struct timeval st_time; // Conn start time. we see first SYN
134 struct timeval cl_time; // Time it moved into CLOSING state
135 int st_rtt; // Sample round trip time.
136 unsigned long st_seq_client; // Init seqno of client
137 unsigned long st_seq_server; // Init seq number of server
138 unsigned long st_seq_client2; // some abusive clients send a second SYN
139 struct timeval st_time2; // time on second SYN
140 // Sometimes, we see the SYN|ACK before SYN because of packet
141 // reordering in pcap. Or, the SYN|ACK could be dropped. In either
142 // case, we would create a conndet object, but not be able to
143 // initialize the server seq number. As a result, the server data
144 // will end up being very large.
145 state_t state; // current state of the connection
146 unsigned long nsyns; // Number of syns to get established.
147 struct in_addr synsender; // sender of the SYN
148 struct in_addr finsender; // sender of the first FIN.
149 int port; // src port.
150 int synack_txmts; // number of transmissions of synack
151 unsigned int thruput; // throughput
152 int bucket; // when we determine its bucket.
153 };
154
155
156 struct bucket {
157 long long nconns; // Number of connections in bucket.
158 long long clsize; // Total number of bytes sent by all clients.
159 long long srsize; // Total number of bytes sent by all servers
160 int rtt; // Total estimated RTT of all connections
161 long long msecs; // Total duration of all connections.
162 long long thruput; // Throughput of this connection.
163 };
164
165 static unsigned long nsyns; // total number of syns seen.
166 static unsigned long ndupsyns; // Number of duplicate syns seen.
167 static unsigned long nsyns2estab; // number of syns it took to estab the conns
168 static unsigned long nsyns2drop; // number of syns taken up by dropped conns
169 static unsigned long ndroppedconns; // number conns never completed.
170 static unsigned long ninprogress; // number of conns in progress at the end
171 static unsigned long ndiscarded; // a guess on number of conns discarded
172 // due to max retransmission of syn|ack
173 // pkt. Useful in the server side.
174 static unsigned long nresets; // number of conns terminated by reset.
175 static unsigned long nclosing; // Number of conns in CLOSING state.
176 //static unsigned long maxnsyns; // max number of syns to estab a conn.
177 static long long nconns;
178 static long long clsize;
179 static long long srsize;
180 static long long conntime;
181 static long long total_rtt;
182 static struct bucket *buckets;
183 static int lo_val = -1;
184 static int hi_val = -1;
185
186 #if __GNUC__ > 2
187 typedef map<int, int> addrhash;
188 typedef map<std::string, struct conndet *> connhash;
189 typedef map<unsigned long, unsigned long> nsynshash;
190 #else // gnu <= 2
191 struct hashtest
192 {
operator ()hashtest193 size_t operator()( const std::string& x ) const
194 {
195 return hash< const char* >()( x.c_str());
196 }
197 };
198
199 typedef hash_map<int, int> addrhash;
200 typedef hash_map<std::string, struct conndet *, hashtest> connhash;
201 typedef hash_map<unsigned long, unsigned long> nsynshash;
202 #endif
203
204 //long long st_time;
205 static struct timeval trace_st_time; // Dump start time
206 static struct timeval trace_end_time; // Dump end time
207 static struct timeval filter_st_time;
208 static const char * filter_st_time_str = NULL; // Ignore packets before this time (string).
209
210 // A hash table of connections that we know about.
211 static connhash tab;
212 // A hash of local addresses, all in network byte order.
213 static addrhash addrs;
214 // Port numbers with connection duration higher than lo_val and lower
215 // than hi_val.
216 static vector <struct portdet> portsvec;
217 static nsynshash synhash;
218
219 #define LOG_INFO(x_fmt, x_args...) fprintf(stderr, "%s:%d:" x_fmt "\n", __FILE__, __LINE__ , ##x_args)
220 #define LOG_WARNING(x_fmt, x_args...) fprintf(stderr, "%s:%d:" x_fmt "\n", __FILE__, __LINE__ , ##x_args)
221 #define LOG_ERROR(x_fmt, x_args...) fprintf(stderr, "%s:%d:" x_fmt "\n", __FILE__, __LINE__ , ##x_args)
222 #define LOG_DEBUG(x_fmt, x_args...) if (debug) fprintf(stdout, "%s:%d:" x_fmt "\n", __FILE__, __LINE__ , ##x_args)
223 #define LOG_TRACE(x_fmt, x_args...) if (debug) fprintf(stdout, x_fmt, ##x_args)
224 #define LOG_DDEBUG(x_fmt, x_args...) if (debug > 1) fprintf(stdout, "%s:%d:" x_fmt "\n", __FILE__, __LINE__ , ##x_args)
225
226 // Output versions, so that we don't break scripts that depend on a certain output format.
227 #define OUTPUT_VERSION_1 1
228 // Experimental stuff. When finalized, bump output version to next number and retain
229 // backward compatibility with cmd line options or compile flags, may be.
230 #define OUTPUT_EXPERIMENTAL 999
231
232 static pcap_t *pc = NULL;
233 static int datalink = -1;
234
235 // Forward declaration
236 static void end_process();
237 static void print_duration();
238 static void print_thruput();
239 static void print_bytes();
240
241 static void
usage(char * cmd)242 usage(char *cmd)
243 {
244 fprintf(stderr, "Usage: %s -p port [-d] [-R] [-r from_file] [-w to_file] [-i ifname] [-n nbuckets] [-s bucket size] [-X max-srvr-rsp-size] [ -x max-clnt-req-size] [-T] [-D msecs] [-P|-C|-S] [-c num_attempts] [-I timespec] [filter]\n", cmd);
245 fprintf(stderr, "-d: Debug on (default off)\n");
246 fprintf(stderr, "-c: Print connections that took >= num_attempts to establish\n");
247 fprintf(stderr, "-R: Raw format output. Useful for scripts\n");
248 fprintf(stderr, "-r: Data is read from file instead of live interface\n");
249 fprintf(stderr, "-w: Data is written to a file.\n");
250 fprintf(stderr, "-i: Interface name (default is ethernet interface)\n");
251 fprintf(stderr, "-n: Number of buckets for getting the distribution. Default 20\n");
252 fprintf(stderr, "-s: Bucket size (connection life time). Default 20ms\n");
253 fprintf(stderr, "-t: Run time in seconds (default 60)\n");
254 fprintf(stderr, "-X: Consider only those connections where the server sends data less than this number. 0 considers all conns (default)\n");
255 fprintf(stderr, "-x: Consider only those connections where the client sends data less than this number. 0 considers all conns (default)\n");
256 fprintf(stderr, "-Y: Consider only those connections where the server sends data more than this number. 0 considers all conns (default)\n");
257 fprintf(stderr, "-y: Consider only those connections where the client sends data more than this number. 0 considers all conns (default)\n");
258 fprintf(stderr, " Only one of -x or -X can be specified\n");
259 fprintf(stderr, "-T: Attempt to estimate round trip time\n");
260 fprintf(stderr, " Estimates may be wrong if the command is run on the server side. See man page.\n");
261 fprintf(stderr, "-D: Print connection filters for the bucket starting with this value\n");
262 fprintf(stderr, " Useful when yconalyzer reads from file. You can then filter by port to look at individual connection trace\n");
263 fprintf(stderr, "-p: port number you want to monitor. Must be specified\n");
264 fprintf(stderr, "-I: Consider pkts newer than this time. Specify in HH:MM:SS or YYYY-MM-DD.HH:MM:SS format\n");
265 fprintf(stderr, "-P for sorting the output by throughput\n");
266 fprintf(stderr, "-C for sorting the output by number of bytes sent by client\n");
267 fprintf(stderr, "-S for sorting the output by number of bytes sent by server\n");
268 fprintf(stderr, "filter: Optional, specified as in tcpdump(1)\n");
269 fprintf(stderr, "Connections are placed in one of the buckets depending on duration\n");
270 fprintf(stderr,"You must be super-user to run this program\n");
271 fprintf(stderr, "Type 'man yconalyzer' for more information\n");
272 exit(1);
273 }
274
275 static bool
str_2_tm(struct tm * tm)276 str_2_tm(struct tm *tm)
277 {
278 char *rv;
279 const char *dash = strchr(filter_st_time_str, '-');
280 const char *fmt = "%H:%M:%S";
281 if (dash != NULL) {
282 fmt = "%Y-%m-%d.%H:%M:%S";
283 }
284 rv = strptime(filter_st_time_str, fmt, tm);
285 if (rv == NULL) {
286 fprintf(stderr, "Illegal time string\n");
287 return (false);
288 }
289 if (*rv != 0) {
290 fprintf(stderr, "Illegal time string: %s\n", rv);
291 return (false);
292 }
293 return true;
294 }
295
296 static void
set_start_time(const struct timeval * tv)297 set_start_time(const struct timeval *tv)
298 {
299 struct tm tm1, tm2, *tmp;
300 time_t timet = tv->tv_sec;
301 tmp = localtime(&timet);
302 tm1 = *tmp;
303 memset(&tm2, 0, sizeof (tm2));
304 (void) str_2_tm(&tm2);
305 if (tm2.tm_mday) {
306 tm1.tm_mday = tm2.tm_mday;
307 tm1.tm_mon = tm2.tm_mon;
308 tm1.tm_year = tm2.tm_year;
309 }
310 tm1.tm_sec = tm2.tm_sec;
311 tm1.tm_min = tm2.tm_min;
312 tm1.tm_hour = tm2.tm_hour;
313 filter_st_time.tv_sec = mktime(&tm1);
314 filter_st_time.tv_usec = 0;
315 }
316
317 static pcap_t *
open_pcap(char * fname,char * device,int snaplen,int promisc,int to_ms)318 open_pcap(char *fname, char *device, int snaplen, int promisc, int to_ms)
319 {
320 char errbuf[PCAP_ERRBUF_SIZE];
321 pcap_t *rc;
322
323 errbuf[0] = 0;
324
325 if (fname[0] == 0) {
326 rc = pcap_open_live(device, snaplen, promisc, to_ms, errbuf);
327 } else {
328 rc = pcap_open_offline(fname, errbuf);
329 }
330 if (rc == NULL) {
331 LOG_ERROR("%s", errbuf);
332 } else if (errbuf[0] != 0) {
333 LOG_WARNING("%s", errbuf);
334 }
335 return (rc);
336 }
337
338 static char *
find_dev()339 find_dev()
340 {
341 pcap_if_t *alldevs;
342 int rv;
343 char errbuf[PCAP_ERRBUF_SIZE];
344 char *rc = NULL;
345
346 // Look up all devices and get a hash of the addresses.
347 errbuf[0] = 0;
348 rv = pcap_findalldevs(&alldevs, errbuf);
349 // Usually returns 0 all the time
350 if (rv != 0) {
351 printf("pcap_findalldevs:%d:%s\n", rv, errbuf);
352 return (rc);
353 }
354 for (;alldevs != NULL; alldevs = alldevs->next) {
355 //if (alldevs->flags & PCAP_IF_LOOPBACK) {
356 //continue;
357 //}
358 LOG_DDEBUG("Examining interface: %s\n", alldevs->name);
359 pcap_addr_t *paddr;
360 for (paddr = alldevs->addresses; paddr != NULL && paddr->addr != NULL;
361 paddr = paddr->next) {
362 if (paddr->addr->sa_family == AF_INET) {
363 int addr = ((struct sockaddr_in *)(paddr->addr))->sin_addr.s_addr;
364 LOG_DDEBUG("Found Inet address 0x%x\n", addr);
365 // 'addr' is in network byte order.
366 addrs[addr] = 1;
367
368 }
369 }
370 //rc = alldevs->name;
371 }
372 pcap_freealldevs(alldevs);
373
374 // The real lookup to bind to a default device.
375 rc = pcap_lookupdev(errbuf);
376 if (rc == NULL) {
377 LOG_ERROR("pcap_lookupdev:%s", errbuf);
378 return (NULL);
379 }
380 return (rc);
381 }
382
383 static int
get_ip_hdr(const u_char * pkt,u_char ** ip)384 get_ip_hdr(const u_char *pkt, u_char **ip)
385 {
386 struct ether_header *eh;
387 uint16_t etype;
388 unsigned long pf;
389
390 if (datalink == DLT_EN10MB) {
391 eh = (struct ether_header *)pkt;
392 etype = ntohs(eh->ether_type);
393 if (etype == ETHERTYPE_IP) {
394 *ip = (u_char *)(pkt + sizeof (struct ether_header));
395 return (0);
396 }
397 } else if (datalink == DLT_NULL) {
398 pf = *(unsigned long *)(pkt);
399 if (pf == PF_INET) {
400 *ip = (u_char *)(pkt + sizeof (unsigned long));
401 return (0);
402 }
403 }
404 return (-1);
405 }
406
407 static u_char
get_proto(const u_char * pkt,u_char ** tcp)408 get_proto(const u_char *pkt, u_char **tcp)
409 {
410 struct ip *ip = (struct ip *)pkt;
411 int hlen = ip->ip_hl * 4;
412 u_char proto = ip->ip_p;
413
414 *tcp = (u_char *)(pkt + hlen);
415 return (proto);
416 }
417
418
419 static void
process_syn(const struct timeval * tv,struct tcphdr * tcph,struct in_addr * ipsrc,struct in_addr * ipdst)420 process_syn(const struct timeval *tv, struct tcphdr *tcph,
421 struct in_addr *ipsrc, struct in_addr *ipdst)
422 {
423 u_short sport = ntohs(tcph->th_sport);
424 u_short dport = ntohs(tcph->th_dport);
425 //u_short hash_port = (sport != port) ? sport : dport;
426 u_short hash_port = ntohs(tcph->th_sport);
427 char key[64];
428
429 snprintf(key, sizeof (key), KEY_FMT_STRING, ipsrc->s_addr, hash_port);
430
431 connhash::iterator it = tab.find(key);
432 struct conndet * cd;
433
434 if (it != tab.end()) {
435 cd = it->second;
436 // If this is a SYN coming in, then it could well be that we
437 // sent the SYN|ACK but that got dropped. We should not be
438 // accounting that against the number of SYNs taken to
439 // establish a connection.
440 // Also, if this is an outgoing SYN, the fact that
441 // the state is still CONNECTING means we are
442 // retransmitting the SYN, and therefore we count it
443 // correctly against the number of SYNs taken up to get a
444 // connection through.
445 if ((cd->state == CONNECTING) ||
446 ((cd->state == CONNECTED) && (cd->st_seq_client == ntohl(tcph->th_seq)))) {
447 // This is a retransmitted SYN for the connection,
448 // whether we are running on the client or on the
449 // server.
450 ndupsyns++;
451 cd->nsyns++;
452 nsyns++;
453 cd->st_time = *tv;
454 cd->st_seq_client = ntohl(tcph->th_seq);
455 cd->state = CONNECTING;
456 cd->st_rtt = 0;
457 cd->synack_txmts = 0;
458 cd->port = hash_port;
459 } else {
460 // A second SYN came in while we are connected. It could be
461 // that the client dropped the SYN|ACK, and is sending a
462 // separate SYN -- a retry.
463 // Or, it could be that this is a buggy/abusive client that is
464 // trying to sneak in a SYN in the middle of the connection.
465 // Only our SYN|ACK can tell whether or not we changed the
466 // face of this connection. Retain the old state, and remember
467 // that this came in.
468 cd->st_seq_client2 = ntohl(tcph->th_seq);
469 cd->st_time2 = *tv;
470 }
471 } else {
472 cd = new conndet;
473 tab[key] = cd;
474 cd->nsyns = 1;
475 cd->synsender.s_addr = ipsrc->s_addr;
476 nsyns++;
477 cd->bucket = -1;
478 cd->state = CONNECTING;
479 cd->st_time = *tv;
480 cd->st_seq_client = ntohl(tcph->th_seq);
481 cd->st_rtt = 0;
482 cd->synack_txmts = 0;
483 cd->port = hash_port;
484 cd->st_time2.tv_sec = 0;
485 cd->st_time2.tv_usec = 0;
486 }
487
488 if (debug) {
489 // Calling inet_ntoa() twice in the same print statements will
490 // muddle the ip addresses.
491 LOG_TRACE("%s:%hu > ", inet_ntoa(*ipsrc), sport);
492 LOG_TRACE("%s:%hu S SEQ:%lu\n", inet_ntoa(*ipdst), dport, cd->st_seq_client);
493 }
494 }
495
496
497 static int
add_stats(int msecs,unsigned int clbytes,unsigned int srbytes,struct conndet * cd)498 add_stats(int msecs, unsigned int clbytes, unsigned int srbytes,
499 struct conndet *cd)
500 {
501 int bucket;
502
503 nconns++;
504 clsize += clbytes;
505 srsize += srbytes;
506 conntime += msecs;
507 total_rtt += cd->st_rtt;
508
509 if (report_type == REP_THRUPUT) {
510 unsigned int nb = clbytes > srbytes ? clbytes : srbytes;
511 if (msecs == 0) {
512 msecs = 1;
513 }
514 cd->thruput = nb / msecs; // KB/sec
515
516 bucket = (cd->thruput / bucket_size) - lo_bucket;
517 } else if (report_type == REP_CLBYTES) {
518 bucket = (clbytes / bucket_size / 1024) - lo_bucket; // bucket size is in KB
519 } else if (report_type == REP_SRBYTES) {
520 bucket = (srbytes / bucket_size / 1024) - lo_bucket;
521 } else { // default is duration
522 bucket = (msecs / bucket_size) - lo_bucket;
523 }
524
525 if (bucket > nbuckets) {
526 bucket = nbuckets;
527 } else if (bucket < 0) {
528 LOG_ERROR("Bad bucket value:%d\n", bucket);
529 abort();
530 }
531
532 buckets[bucket].nconns++;
533 buckets[bucket].clsize += clbytes;
534 buckets[bucket].srsize += srbytes;
535 buckets[bucket].rtt += cd->st_rtt;
536 buckets[bucket].msecs += msecs;
537 buckets[bucket].thruput += ((srbytes+clbytes)/(msecs+1));
538 return (bucket);
539 }
540
541 static int
tvdiff_msecs(const struct timeval * tv1,const struct timeval * tv2)542 tvdiff_msecs(const struct timeval *tv1, const struct timeval *tv2)
543 {
544 long u = tv2->tv_usec - tv1->tv_usec;
545 long s = tv2->tv_sec - tv1->tv_sec;
546 if (u < 0) {
547 u += 1000000;
548 s -= 1;
549 }
550 if (s < 0 || u < 0) {
551 // OSX-SnowLeopard has problems with a %ld for timeval
552 LOG_DEBUG("Bad value of time:%ld/%ld, %ld/%ld\n",
553 (long)(tv1->tv_sec), (long)(tv1->tv_usec),
554 (long)(tv2->tv_sec), (long)(tv2->tv_usec));
555 return (0);
556 }
557 return (s * 1000 + u / 1000);
558 }
559
560 // If max_srvr_rspsize has been set, then we need to
561 // take in only those conns where the server
562 // sends less data than the size indicated.
563 // Similarly, if the max_clnt_reqsize is set, then
564 // we need to account for only those conns where the
565 // client sends data less than value set in
566 // max_clnt_reqsize.
567 static bool
include_connection(unsigned long clbytes,unsigned long srbytes)568 include_connection(unsigned long clbytes, unsigned long srbytes)
569 {
570 if (max_srvr_rspsize != 0 && srbytes > max_srvr_rspsize) {
571 return (false);
572 }
573 if (min_srvr_rspsize != 0 && srbytes < min_srvr_rspsize) {
574 return (false);
575 }
576 if (max_clnt_reqsize != 0 && clbytes > max_clnt_reqsize) {
577 return (false);
578 }
579 if (min_clnt_reqsize != 0 && clbytes < min_clnt_reqsize) {
580 return (false);
581 }
582 return (true);
583 }
584
585 static void
process_end_conn(const struct timeval * tv,struct tcphdr * tcph,struct in_addr * ipsrc,struct in_addr * ipdst,bool reset,int datalen)586 process_end_conn(const struct timeval *tv, struct tcphdr *tcph,
587 struct in_addr *ipsrc, struct in_addr *ipdst, bool reset,
588 int datalen)
589 {
590 u_short sport = ntohs(tcph->th_sport);
591 u_short dport = ntohs(tcph->th_dport);
592 u_short hash_port = (sport != port) ? sport : dport;
593 char key[64];
594 //long long trace_end_time;
595 //unsigned long end_seq;
596 unsigned long cl_end_seq, sr_end_seq;
597
598 if (hash_port == sport) {
599 // client is intiating the FIN
600 snprintf(key, sizeof (key), KEY_FMT_STRING,
601 ipsrc->s_addr, hash_port);
602 cl_end_seq = ntohl(tcph->th_seq) + datalen;
603 sr_end_seq = ntohl(tcph->th_ack);
604 } else {
605 //Server is initiating the FIN
606 snprintf(key, sizeof (key), KEY_FMT_STRING,
607 ipdst->s_addr, hash_port);
608 cl_end_seq = ntohl(tcph->th_ack);
609 sr_end_seq = ntohl(tcph->th_seq) + datalen;
610 }
611
612 connhash::iterator it = tab.find(key);
613
614 if (it != tab.end()) {
615 struct conndet * cd = it->second;
616 if (cd->state == CLOSING) {
617 // This is the second FIN or reset of the connection
618 // If it is a retransmt of the FIN, just set the
619 // time again. Never trust a RESET, however.
620 if (!reset) {
621 if (cd->finsender.s_addr == ipsrc->s_addr) {
622 cd->cl_time = *tv;
623 return;
624 }
625 // It is the other side sending the FIN.
626 // Taking the time difference between now
627 // and when we sent the FIN can give us a
628 // rough idea of the rtt, in most cases.
629 // Discard the rtt if it is more than
630 // max_rtt, because it may be bogus -- may
631 // be the client was too busy to respond to
632 // our FIN..
633 // Also, if our current estimate of RTT (from
634 // the SYN exchange) is more than zero, then it
635 // is likely that this trace was captured in
636 // the client.
637 int rtt = tvdiff_msecs(&(cd->cl_time), tv);
638 if ((rtt < max_rtt) && (cd->st_rtt == 0) && (rtt > cd->st_rtt) && (cd->bucket >= 0)) {
639 int bucket = cd->bucket;
640 buckets[bucket].rtt += rtt;
641 total_rtt += rtt;
642 }
643 }
644 delete cd;
645 tab.erase(key);
646 return;
647 }
648 if ((cd->state == CONNECTED) && !reset) {
649 unsigned long clbytes, srbytes;
650 clbytes = cl_end_seq - cd->st_seq_client;
651 srbytes = sr_end_seq - cd->st_seq_server;
652 int msecs = tvdiff_msecs(&(cd->st_time), tv);
653 cd->cl_time = *tv;
654 cd->state = CLOSING;
655 cd->finsender.s_addr = ipsrc->s_addr;
656 if (include_connection(clbytes, srbytes)) {
657 // This is the type of req we want to count
658 if (debug) {
659 LOG_TRACE("%s:%hu > ",
660 inet_ntoa(*ipsrc),
661 sport);
662 LOG_TRACE("%s:%hu F SEQ:%lu(%lu),ACK:%lu(%lu),MSECS:%d\n",
663 inet_ntoa(*ipdst),
664 dport,
665 sport == hash_port ? cl_end_seq : sr_end_seq,
666 sport == hash_port ? clbytes : srbytes,
667 sport == hash_port ? sr_end_seq : cl_end_seq,
668 sport == hash_port ? srbytes : clbytes,
669 msecs);
670 }
671 cd->bucket = add_stats(msecs, clbytes, srbytes, cd);
672 int cmpval;
673 if (report_type == REP_THRUPUT) {
674 cmpval = cd->thruput;
675 } else if (report_type == REP_CLBYTES) {
676 cmpval = clbytes/1024;
677 } else if (report_type == REP_SRBYTES) {
678 cmpval = srbytes/1024;
679 } else { // default is duration
680 cmpval = msecs;
681 }
682 if ((lo_val != -1) && (cmpval >= lo_val) && ((hi_val == -1) || (cmpval < hi_val))) {
683 // If we knew whether the machine
684 // that captured these packets is
685 // the initiator or the server, then
686 // we can the right IP address to
687 // display. Failing that, the best
688 // way is to display both IP
689 // address. It would have been great
690 // if libpcap provided a 3-way flag
691 // indicating in/out/neither -- oh
692 // well.
693 struct portdet pd;
694 pd.ipsrc = *ipsrc;
695 pd.ipdst = *ipdst;
696 pd.port = hash_port;
697 portsvec.push_back(pd);
698 }
699 if (min_conn_attempts &&
700 cd->nsyns >= min_conn_attempts) {
701
702 printf("NSYNS=%lu:host %s and port %d\n",
703 cd->nsyns,
704 inet_ntoa(cd->synsender), cd->port);
705 }
706 nsyns2estab += cd->nsyns;
707 nsynshash::iterator it;
708 it = synhash.find(cd->nsyns);
709 if (it == synhash.end()) {
710 synhash[cd->nsyns] = 1;
711 } else {
712 it->second++;
713 }
714
715 #if 0
716 if (cd->nsyns > maxnsyns) {
717 maxnsyns = cd->nsyns;
718 }
719 #endif
720 }
721 } else if (reset) {
722 LOG_DDEBUG("Conn Reset:%s\n", key);
723 nresets++;
724 delete cd;
725 tab.erase(key);
726 return;
727 }
728 }
729 }
730 static void
process_synack(const struct timeval * tv,struct tcphdr * tcph,struct in_addr * ipsrc,struct in_addr * ipdst)731 process_synack(const struct timeval *tv, struct tcphdr *tcph,
732 struct in_addr *ipsrc, struct in_addr *ipdst)
733 {
734 u_short sport = ntohs(tcph->th_sport);
735 u_short dport = ntohs(tcph->th_dport);
736 u_short hash_port = (sport != port) ? sport : dport;
737 char key[64];
738
739 if (hash_port == sport) {
740 snprintf(key, sizeof (key), KEY_FMT_STRING,
741 ipsrc->s_addr, hash_port);
742 } else {
743 snprintf(key, sizeof (key), KEY_FMT_STRING,
744 ipdst->s_addr, hash_port);
745 }
746
747 connhash::iterator it = tab.find(key);
748 unsigned long seq = ntohl(tcph->th_seq);
749 unsigned long ack = ntohl(tcph->th_ack);
750
751 if (it != tab.end()) {
752 struct conndet * cd = it->second;
753 if (cd->state == CONNECTED) {
754 // We are already connected. This could be a
755 // retransmission of the SYN-ACK or a SYN-ACK for the
756 // first SYN.
757 if (cd->st_time2.tv_sec != 0 &&
758 cd->st_time2.tv_usec != 0) {
759 if (ack == (cd->st_seq_client2+1)) {
760 // We got another SYN in connectde state, and
761 // the server is acking the new one.
762 // Start statistics afresh for the connection.
763 cd->st_time = cd->st_time2;
764 cd->st_seq_client = cd->st_seq_client2;
765 cd->synack_txmts = 0;
766 } else {
767 // either the server is acking the old
768 // SYN, or it is a broken server.
769 // Either case, we can drop the
770 // packet.
771 if (debug) {
772 LOG_TRACE("%s:%hu > ", inet_ntoa(*ipsrc), sport);
773 LOG_TRACE("%s:%hu SA SEQ:%lu,DROPPED\n", inet_ntoa(*ipdst), dport, seq);
774 }
775 return;
776 }
777 }
778 }
779 cd->st_seq_server = seq;
780 int msecs = tvdiff_msecs(&(cd->st_time), tv);
781 cd->synack_txmts++;
782 if (debug) {
783 // Calling inet_ntoa() twice in the same print statements will
784 // muddle the ip addresses.
785 LOG_TRACE("%s:%hu > ", inet_ntoa(*ipsrc), sport);
786 LOG_TRACE("%s:%hu SA SEQ:%lu,MSECS:%d\n", inet_ntoa(*ipdst), dport, cd->st_seq_server, msecs);
787 }
788 // If this is a retransmission of the SYN|ACK, then we don't
789 // want to consider this for rtt computation -- whether we
790 // are on the client side or server side.
791 if (cd->state == CONNECTING) {
792 if (find_rtt || (addrs.find(ipdst->s_addr) != addrs.end())) {
793 // The syn|ack was destined for one of our
794 // addresses.
795 if (find_rtt == 0) {
796 fprintf(stderr, RTT_EST_ENABLED);
797 }
798 find_rtt++;
799 cd->st_rtt = msecs;
800 }
801 }
802 cd->state = CONNECTED;
803 }
804 }
805
806 static void
process_tcp_pkt(const struct timeval * tv,u_char * hdr,struct in_addr * ipsrc,struct in_addr * ipdst,int tcplen)807 process_tcp_pkt(const struct timeval *tv, u_char *hdr,
808 struct in_addr *ipsrc, struct in_addr *ipdst,
809 int tcplen)
810 {
811 struct tcphdr *tcph = (struct tcphdr *)hdr;
812 u_char flags = tcph->th_flags;
813 bool reset = ((tcph->th_flags & TH_RST) != 0);
814
815 if (filter_st_time.tv_sec >= tv->tv_sec) {
816 LOG_DDEBUG("%s", "Dropping old pkt");
817 return;
818 }
819
820 LOG_DDEBUG("TCP data offset = %d, TCP len = %d\n", tcph->th_off *4,
821 tcplen);
822 // If RST is set, then procses that and nothing else.
823 if (reset) {
824 process_end_conn(tv, tcph, ipsrc, ipdst, true, 0);
825 return;
826 }
827 if (flags & TH_SYN) {
828 if (flags & TH_ACK) {
829 process_synack(tv, tcph, ipsrc, ipdst);
830 } else {
831 process_syn(tv, tcph, ipsrc, ipdst);
832 }
833 } else if (flags & TH_FIN) {
834 process_end_conn(tv, tcph, ipsrc, ipdst, false,
835 tcplen - tcph->th_off * 4);
836 } else {
837 return;
838 }
839 }
840
841 // We output the X and Y values for a graph that shows the
842 // time in x axis and the number of long-lived connectins in
843 // Y axis. A long-lived connection is one that has been
844 // in ESTABLISHED state (or, our close guess of that state,
845 // for we count SYNRCVD state as ESTABLISHED as well).
846 // for longer than min_conntime msecs. We output this at
847 // some reasonable time intervals (for now, defined as
848 // every min_conntime/10 packets that we see).
849 static void
print_longconns(const struct timeval * tv)850 print_longconns(const struct timeval *tv)
851 {
852 long long xval = tvdiff_msecs(&filter_st_time, tv);
853 if (xval < 0) xval = 0;
854 int yval = 0;
855 connhash::const_iterator it;
856 for (it = tab.begin(); it != tab.end(); it++) {
857 struct conndet *cd = it->second;
858 if (cd->state < CONNECTED) {
859 continue;
860 }
861 if (tvdiff_msecs(&(cd->st_time), tv) > min_conntime) {
862 yval++;
863 }
864 }
865 printf("%lu, %f %d\n", (unsigned long)tab.size(), (float) xval/1000.0, yval);
866 // #ifdef __x86_64__
867 }
868
869 static void
process_pkt(u_char * useless,const struct pcap_pkthdr * pkthdr,const u_char * eth)870 process_pkt(u_char *useless, const struct pcap_pkthdr *pkthdr,
871 const u_char *eth)
872 {
873 if (done) {
874 end_process();
875 }
876 static int count = 0;
877 u_char *iph, *tcph;
878 //u_char *lh;
879 u_char proto;
880 //char prefix[128];
881 //char srcname[48], dstname[48];
882 struct in_addr *ipsrc, *ipdst;
883 const struct timeval *tv = &(pkthdr->ts);
884 static int pktnum = 0;
885
886 if (fromfile) {
887 if (gottime == 0) {
888 gottime++;
889 trace_st_time = *tv;
890 }
891 trace_end_time = *tv;
892 }
893 if (min_conntime && ((pktnum % (min_conntime/10)) == 0)) {
894 print_longconns(tv);
895 }
896 if (pktnum == 0) {
897 // This is the first packet we are seeing. Get the time from
898 // that.
899 if (filter_st_time_str != NULL) {
900 set_start_time(tv);
901 } else {
902 // Set filter_st_time to be same as trace_st_time.
903 filter_st_time = trace_st_time;
904 filter_st_time.tv_sec -= 1; // make sure we take in the first pkt.
905 }
906 }
907 pktnum++;
908 LOG_DDEBUG("Got packet %d:len = %d, caplen = %d\n", ++count,
909 pkthdr->len, pkthdr->caplen);
910 if (get_ip_hdr(eth, &iph) != 0) {
911 LOG_DDEBUG("Not IP packet\n");
912 return;
913 }
914 proto = get_proto(iph, &tcph);
915 LOG_DDEBUG("Proto = %u\n", proto);
916 if (proto != IPPROTO_TCP) {
917 LOG_DDEBUG("Not TCP packet");
918 return;
919 }
920
921 LOG_DDEBUG("FOUND TCP/IP PACKET\n");
922 LOG_DDEBUG("IP hdr len = %d\n", ((struct ip *)(iph))->ip_hl * 4);
923 LOG_DDEBUG("IP total len = %d\n", htons(((struct ip *)(iph))->ip_len));
924
925 int tcplen = htons(((struct ip *)(iph))->ip_len) -
926 ((struct ip *)(iph))->ip_hl * 4;
927 //lh = (u_char *)(tcph + sizeof (struct tcphdr));
928 ipsrc = &(((struct ip *)iph)->ip_src);
929 ipdst = &(((struct ip *)iph)->ip_dst);
930 process_tcp_pkt(tv, tcph, ipsrc, ipdst, tcplen);
931 }
932
933 static int
set_filter(pcap_t * pc,char * dev,int argc,char * argv[])934 set_filter(pcap_t *pc, char *dev, int argc, char *argv[])
935 {
936 bpf_u_int32 mask, net;
937 char errbuf[PCAP_ERRBUF_SIZE];
938 int rv;
939 char port_filter[256];
940 std::string filter;
941 struct bpf_program bpf;
942
943 errbuf[0] = 0;
944 if ((rv = pcap_lookupnet(dev, &net, &mask, errbuf)) != 0) {
945 LOG_ERROR("pcap_lookupnet:%d:%s\n", rv, errbuf);
946 return (-1);
947 }
948
949 snprintf(port_filter, sizeof (port_filter), "(tcp port %d)&&((tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst)) != 0)", port);
950 filter = port_filter;
951 if (argc) {
952 /*
953 * User has specified additional filters. We need to
954 * append them to the port filter we specify by default.
955 */
956 filter += "&&(";
957 for (int i = 0; i < argc; i++) {
958 filter += argv[i];
959 filter += " ";
960 }
961 filter += ")";
962 }
963 LOG_DEBUG("Setting filter to %s", filter.c_str());
964 char filtchars[filter.length()+1];
965 strcpy(filtchars, filter.c_str());
966 if ((rv = pcap_compile(pc, &bpf, filtchars, 0, net)) != 0) {
967 LOG_ERROR("pcap_compile:%d\n", rv);
968 return (-1);
969 }
970
971 if ((rv = pcap_setfilter(pc, &bpf)) != 0) {
972 LOG_ERROR("pcap_setfilter:%d\n", rv);
973 return (-1);
974 }
975 return (0);
976 }
977
978 static void
print_duration_bucket_raw(const int i)979 print_duration_bucket_raw(const int i)
980 {
981 if (i < nbuckets) {
982 printf("%0lld,%0lld,",
983 (long long)(i+lo_bucket) * bucket_size,
984 (long long)(i+lo_bucket+1) * bucket_size);
985 } else {
986 printf("%0lld+,", (long long)(i+lo_bucket) * bucket_size);
987 }
988 printf("%0lld,%0.2f%%,", buckets[i].nconns, (float)buckets[i].nconns*100/nconns);
989 if (buckets[i].nconns) {
990 printf("%0lld,%0lld,",
991 buckets[i].clsize/buckets[i].nconns,
992 buckets[i].srsize/buckets[i].nconns);
993 if (find_rtt) {
994 printf("%0lld,", buckets[i].rtt/buckets[i].nconns);
995 } else {
996 printf(",");
997 }
998 printf("%.2f\n", (float)(buckets[i].thruput)/buckets[i].nconns);
999 } else {
1000 printf(",,,\n");
1001 }
1002 }
1003
1004 static void
print_duration_bucket(const int i)1005 print_duration_bucket(const int i)
1006 {
1007 if (i < nbuckets) {
1008 printf("%5lld - %-5lld ",
1009 (long long)(i+lo_bucket) * bucket_size,
1010 (long long)(i+lo_bucket+1) * bucket_size);
1011 } else {
1012 printf("%5lld+ ", (long long)(i+lo_bucket) * bucket_size);
1013 }
1014 printf("%8lld(%5.2f%%) ", buckets[i].nconns, (float)buckets[i].nconns*100/nconns);
1015 if (buckets[i].nconns) {
1016 printf("%8lld %8lld ",
1017 buckets[i].clsize/buckets[i].nconns,
1018 buckets[i].srsize/buckets[i].nconns);
1019 if (find_rtt) {
1020 printf("%4lld ", buckets[i].rtt/buckets[i].nconns);
1021 } else {
1022 printf(" - ");
1023 }
1024 printf(" %.2f\n", (float)(buckets[i].thruput)/buckets[i].nconns);
1025 } else {
1026 printf(" - - -\n");
1027 }
1028 }
1029
1030 static void
print_bytes_bucket(const int i)1031 print_bytes_bucket(const int i)
1032 {
1033 if (raw_output) {
1034 if (i < nbuckets) {
1035 printf("%lld,%lld,",
1036 (long long)(i+lo_bucket) * bucket_size,
1037 (long long)(i+lo_bucket+1) * bucket_size);
1038 } else {
1039 printf("%lld+,", (long long)(i+lo_bucket) * bucket_size);
1040 }
1041 printf("%lld,%.2f%%,", buckets[i].nconns, (float)buckets[i].nconns*100/nconns);
1042 if (buckets[i].nconns) {
1043 printf("%lld,%lld,",
1044 buckets[i].clsize/buckets[i].nconns,
1045 buckets[i].srsize/buckets[i].nconns);
1046 printf("%lld,", buckets[i].msecs/buckets[i].nconns);
1047 if (find_rtt) {
1048 printf("%lld", buckets[i].rtt/buckets[i].nconns);
1049 } else {
1050 printf(",");
1051 }
1052 printf("%.2f\n", (float)(buckets[i].thruput/buckets[i].nconns));
1053 } else {
1054 printf(",,,,,,\n");
1055 }
1056 } else {
1057 if (i < nbuckets) {
1058 printf("%5lld - %-5lld ",
1059 (long long)(i+lo_bucket) * bucket_size,
1060 (long long)(i+lo_bucket+1) * bucket_size);
1061 } else {
1062 printf("%5lld+ ", (long long)(i+lo_bucket) * bucket_size);
1063 }
1064 printf("%6lld(%5.2f%%) ", buckets[i].nconns, (float)buckets[i].nconns*100/nconns);
1065 if (buckets[i].nconns) {
1066 printf("%8lld %8lld ",
1067 buckets[i].clsize/buckets[i].nconns,
1068 buckets[i].srsize/buckets[i].nconns);
1069 printf("%6lld ", buckets[i].msecs/buckets[i].nconns);
1070 if (find_rtt) {
1071 printf("%4lld ", buckets[i].rtt/buckets[i].nconns);
1072 } else {
1073 printf(" - ");
1074 }
1075 printf("%4lld\n", (buckets[i].clsize+buckets[i].srsize)/(buckets[i].msecs+1));
1076 } else {
1077 printf(" - - - - -\n");
1078 }
1079 }
1080 }
1081
1082 static void
print_bytes()1083 print_bytes()
1084 {
1085 if (report_type == REP_CLBYTES) {
1086 printf("ClientBytes NumConns AvClient AvServer AvDuration EstRtt AvThruput\n");
1087 } else {
1088 printf("ServerBytes NumConns AvClient AvServer AvDuration EstRtt AvThruput\n");
1089 }
1090 printf(" (KB) ");
1091 printf(" (bytes) (bytes) (msecs) (msecs) (KB/s)\n");
1092 if (nconns == 0) {
1093 printf("No connections\n");
1094 return;
1095 }
1096
1097 if (raw_output) {
1098 printf("===========\n");
1099 }
1100 for (int i = 0; i <= nbuckets; i++) {
1101 print_bytes_bucket(i);
1102 }
1103 if (raw_output) {
1104 printf("===========\n");
1105 }
1106 if (lo_val != -1) {
1107 printf("Connections for which client sent data ");
1108 if (hi_val == -1) {
1109 printf("%d+ KB:\n", lo_val);
1110 } else {
1111 printf("between %d and %d KB:\n", lo_val, hi_val);
1112 }
1113 vector<struct portdet>::const_iterator it;
1114 for (it = portsvec.begin(); it != portsvec.end(); it++) {
1115 struct portdet pd = *it;
1116 char ipsrc[32], ipdst[32];
1117 strcpy(ipsrc, inet_ntoa(pd.ipsrc));
1118 strcpy(ipdst, inet_ntoa(pd.ipdst));
1119 printf("host %s and host %s and port %d\n", ipsrc, ipdst, pd.port);
1120 }
1121 }
1122 }
1123
1124 // Print the bucket range, Avg Client bytes, Avg server bytes, Avg duration
1125 // and estimated avg RTT for the bucket.
1126 static void
print_thruput_bucket(const int i)1127 print_thruput_bucket(const int i)
1128 {
1129 if (raw_output) {
1130 if (i < nbuckets) {
1131 printf("%lld,%lld,",
1132 (long long)(i+lo_bucket) * bucket_size,
1133 (long long)(i+lo_bucket+1) * bucket_size);
1134 } else {
1135 printf("%lld+,", (long long)(i+lo_bucket) * bucket_size);
1136 }
1137 printf("%lld,%.2f%%,", buckets[i].nconns, (float)buckets[i].nconns*100/nconns);
1138 if (buckets[i].nconns) {
1139 printf("%lld,%lld,",
1140 buckets[i].clsize/buckets[i].nconns,
1141 buckets[i].srsize/buckets[i].nconns);
1142 printf("%lld,", buckets[i].msecs/buckets[i].nconns);
1143 if (find_rtt) {
1144 printf("%lld,\n", buckets[i].rtt/buckets[i].nconns);
1145 } else {
1146 printf(",,\n");
1147 }
1148 } else {
1149 printf(",,,\n");
1150 }
1151 } else {
1152 if (i < nbuckets) {
1153 printf("%4lld - %-4lld ",
1154 (long long)(i+lo_bucket) * bucket_size,
1155 (long long)(i+lo_bucket+1) * bucket_size);
1156 } else {
1157 printf("%4lld+ ", (long long)(i+lo_bucket) * bucket_size);
1158 }
1159 printf("%6lld(%5.2f%%) ", buckets[i].nconns, (float)buckets[i].nconns*100/nconns);
1160 if (buckets[i].nconns) {
1161 printf("%8lld %8lld ",
1162 buckets[i].clsize/buckets[i].nconns,
1163 buckets[i].srsize/buckets[i].nconns);
1164 printf("%6lld ", buckets[i].msecs/buckets[i].nconns);
1165 if (find_rtt) {
1166 printf("%4lld\n", buckets[i].rtt/buckets[i].nconns);
1167 } else {
1168 printf(" -\n");
1169 }
1170 } else {
1171 printf(" - -\n");
1172 }
1173 }
1174 }
1175
1176 static void
print_stats_v1()1177 print_stats_v1()
1178 {
1179 printf("Total Connections: %lld\nAvg Client Data: %lld bytes\n"
1180 "Avg Server Data: %lld bytes\n"
1181 "Avg conn time: %lld msecs\n",
1182 nconns, nconns ? clsize/nconns : 0,
1183 nconns ? srsize/nconns : 0,
1184 nconns ? conntime/nconns : 0);
1185 printf("Total number of SYNs: %lu (duplicates: %lu)\n", nsyns, ndupsyns);
1186 printf("Number of SYNs taken to establish connections: %lu\n", nsyns2estab);
1187 if (nconns) {
1188 printf("Avg no. of SYNs to establish a connection: %5.2f\n", (float)nsyns2estab/nconns);
1189 }
1190 printf("Number of unique connections that never completed: %lu\n", ndroppedconns);
1191 printf("Number of SYNs from incomplete connections: %lu\n", nsyns2drop);
1192 printf("Number of established connections in progress: %lu\n", ninprogress);
1193 #if 0
1194 printf("Max syns for any connections: %lu\n", maxnsyns);
1195 #endif
1196 printf("Distribution of SYNs to establsh connections (nSYNs:nConns): ");
1197 int nentries = synhash.size(); unsigned long i = 1;
1198 while (nentries) {
1199 nsynshash::const_iterator it = synhash.find(i);
1200 if (it != synhash.end()) {
1201 nentries--;
1202 printf("%lu:%lu,", i, synhash[i]);
1203 }
1204 i++;
1205 }
1206 printf("\n");
1207 }
1208
1209 /**
1210 * Output that can be modified.
1211 */
1212 static void
print_stats()1213 print_stats()
1214 {
1215 printf("Start time: %s", ctime((time_t *)&(trace_st_time.tv_sec)));
1216 printf("End time: %s", ctime((time_t *)&(trace_end_time.tv_sec)));
1217 printf("Total Connections (terminated by FIN): %lld\nAvg Client Data: %lld bytes\n"
1218 "Avg Server Data: %lld bytes\n"
1219 "Avg conn duration: %lld msecs\n",
1220 nconns, nconns ? clsize/nconns : 0,
1221 nconns ? srsize/nconns : 0,
1222 nconns ? conntime/nconns : 0);
1223 printf("Avg Round Trip Time: %lld msecs\n", nconns ? total_rtt/nconns : 0);
1224 printf("Total number of SYNs: %lu (duplicates: %lu)\n", nsyns, ndupsyns);
1225 printf("Connections terminated by RESET: %lu\n", nresets);
1226 printf("Number of SYNs taken to establish connections: %lu\n", nsyns2estab);
1227 if (nconns) {
1228 printf("Avg no. of SYNs to establish a connection: %5.2f\n", (float)nsyns2estab/nconns);
1229 }
1230 printf("Number of unique connections dropped by the server: %lu\n", ndroppedconns);
1231 printf("Number of SYNs from incomplete connections: %lu\n", nsyns2drop);
1232 printf("Number of established connections in progress: %lu (%lu may have been discarded by the client)\n", ninprogress, ndiscarded);
1233 #if 0
1234 printf("Max syns for any connections: %lu\n", maxnsyns);
1235 #endif
1236 printf("Number of connections in closing state: %lu\n", nclosing);
1237 printf("Distribution of SYNs to establsh connections (nSYNs:nConns): ");
1238 int nentries = synhash.size(); unsigned long i = 1;
1239 while (nentries) {
1240 nsynshash::const_iterator it = synhash.find(i);
1241 if (it != synhash.end()) {
1242 nentries--;
1243 printf("%lu:%lu,", i, synhash[i]);
1244 }
1245 i++;
1246 }
1247 printf("\n\n");
1248 if (report_type == REP_THRUPUT) {
1249 print_thruput();
1250 } else if (report_type == REP_CLBYTES || report_type == REP_SRBYTES) {
1251 print_bytes();
1252 } else { // default is duration
1253 print_duration();
1254 }
1255 }
1256
1257 static void
print_thruput()1258 print_thruput()
1259 {
1260 printf("Throughput NumConns AvgClient AvgServer AvgDuration EstRTT\n");
1261 printf(" KB/s ");
1262 printf(" (bytes) (bytes) (msecs) (msecs)\n");
1263 if (nconns == 0) {
1264 printf("No connections\n");
1265 return;
1266 }
1267 if (raw_output) {
1268 printf("===========\n");
1269 }
1270
1271 for (int i = 0; i <= nbuckets; i++) {
1272 print_thruput_bucket(i);
1273 }
1274 if (raw_output) {
1275 printf("===========\n");
1276 }
1277 if (lo_val != -1) {
1278 printf("Connections for which throuput was ");
1279 if (hi_val == -1) {
1280 printf("%d+ KB/sec:\n", lo_val);
1281 } else {
1282 printf("between %d and %d KB/sec:\n", lo_val, hi_val);
1283 }
1284 vector<struct portdet>::const_iterator it;
1285 for (it = portsvec.begin(); it != portsvec.end(); it++) {
1286 struct portdet pd = *it;
1287 char ipsrc[32], ipdst[32];
1288 strcpy(ipsrc, inet_ntoa(pd.ipsrc));
1289 strcpy(ipdst, inet_ntoa(pd.ipdst));
1290 printf("host %s and host %s and port %d\n", ipsrc, ipdst, pd.port);
1291 }
1292 }
1293 }
1294
1295 static void
print_duration()1296 print_duration() {
1297 printf(" Duration NumConns AvClient AvServer EstRtt AvThruput\n");
1298 printf(" (msecs) (bytes) (bytes) (msecs) (KB/s)\n");
1299 if (nconns == 0) {
1300 printf("No connections\n");
1301 return;
1302 }
1303 if (raw_output) {
1304 printf("===========\n");
1305 }
1306 for (int i = 0; i <= nbuckets; i++) {
1307 if (raw_output) {
1308 print_duration_bucket_raw(i);
1309 } else {
1310 print_duration_bucket(i);
1311 }
1312 }
1313 if (raw_output) {
1314 printf("===========\n");
1315 }
1316 if (lo_val != -1) {
1317 printf("Connections for which duration was ");
1318 if (hi_val == -1) {
1319 printf("%d+ msecs:\n", lo_val);
1320 } else {
1321 printf("between %d and %d msecs:\n", lo_val, hi_val);
1322 }
1323 vector<struct portdet>::const_iterator it;
1324 for (it = portsvec.begin(); it != portsvec.end(); it++) {
1325 struct portdet pd = *it;
1326 char ipsrc[32], ipdst[32];
1327 strcpy(ipsrc, inet_ntoa(pd.ipsrc));
1328 strcpy(ipdst, inet_ntoa(pd.ipdst));
1329 printf("host %s and host %s and port %d\n", ipsrc, ipdst, pd.port);
1330 }
1331 }
1332 }
1333
1334 static void
print_stats_experimental()1335 print_stats_experimental()
1336 {
1337 connhash::iterator it;
1338 if (min_conntime) {
1339 print_longconns(&trace_end_time);
1340 printf("Connections in Progress for longer than %d msecs:\n",
1341 min_conntime);
1342
1343 for (it = tab.begin(); it != tab.end(); it++) {
1344 struct conndet *cd = it->second;
1345 if (cd->state >= CONNECTED) {
1346 int duration = tvdiff_msecs(&(cd->st_time), &trace_end_time);
1347 if (duration > min_conntime) {
1348 printf("%d(%d):host %s and port %d\n",
1349 duration, cd->synack_txmts,
1350 inet_ntoa(cd->synsender), cd->port);
1351 }
1352 }
1353 }
1354 }
1355
1356 printf("Connections without SYN|ACK:\n");
1357
1358 for (it = tab.begin(); it != tab.end(); it++) {
1359 struct conndet *cd = it->second;
1360 if (cd->state < CONNECTED) {
1361 printf("NOSYNACK:host %s and port %d\n",
1362 inet_ntoa(cd->synsender), cd->port);
1363 }
1364 }
1365 }
1366
1367 // Do the absolute minimum in signal handlers to avoid core.
1368 static void
sighandler(int sig)1369 sighandler(int sig)
1370 {
1371 if (dumper) {
1372 end_process();
1373 }
1374 done++;
1375 }
1376
1377 static void
end_process()1378 end_process()
1379 {
1380 struct pcap_stat ps;
1381
1382 if (pcap_stats(pc, &ps) == 0) {
1383 fprintf(stderr, "%u Packets received\n", ps.ps_recv);
1384 fprintf(stderr, "%u Packets dropped\n", ps.ps_drop);
1385 }
1386 pcap_close(pc);
1387 if (dumper) {
1388 pcap_dump_close(dumper);
1389 exit(0);
1390 }
1391 LOG_DDEBUG("Hash has %u conns left\n", tab.size());
1392 connhash::iterator it;
1393 for (it = tab.begin(); it != tab.end(); it++) {
1394 struct conndet *cd = it->second;
1395 if (cd->state < CONNECTED) {
1396 // this is a dropped connection
1397 ndroppedconns++;
1398 nsyns2drop += cd->nsyns;
1399 } else {
1400 ninprogress++;
1401 if (cd->state == CLOSING) {
1402 nclosing++;
1403 }
1404 if (cd->synack_txmts >= MAX_SYNACKS) {
1405 // SYN|ACK was sent more than the max times
1406 // it is done with syncache. May be TCP just
1407 // dropped the connection altogether. We
1408 // can't say for sure, though.
1409 ndiscarded++;
1410 }
1411 }
1412 }
1413
1414 //long long trace_end_time = ysys_get_usec();
1415 //double secs = (double)(trace_end_time - st_time)/1000000;
1416 if (!fromfile) {
1417 gettimeofday(&trace_end_time, NULL);
1418 }
1419 double secs = (double)tvdiff_msecs(&filter_st_time, &trace_end_time)/1000;
1420 switch (output_version) {
1421 case OUTPUT_VERSION_1:
1422 printf("Results of monitoring port %d for %f seconds\n", port, secs);
1423 print_stats_v1();
1424 break;
1425 case OUTPUT_EXPERIMENTAL:
1426 print_stats_experimental();
1427 break;
1428 default:
1429 printf("Results of monitoring port %d for %f seconds\n", port, secs);
1430 print_stats();
1431 break;
1432 }
1433 exit(0);
1434 }
1435
1436 // If we are looking to report by the number of bytes that the server or
1437 // client sent, and we have an upper limit on the size, then we can adjust so
1438 // that we don't report buckets with 0 data unnecessarily.
1439 static void
adjust_nbuckets()1440 adjust_nbuckets()
1441 {
1442 if (report_type == REP_CLBYTES) {
1443 if (max_clnt_reqsize) {
1444 int blo = min_clnt_reqsize/bucket_size/1024;
1445 int bhi = max_clnt_reqsize/bucket_size/1024;
1446 if (bhi - blo + 1 < nbuckets) {
1447 nbuckets = bhi - blo + 1;
1448 }
1449 }
1450 } else if (report_type == REP_SRBYTES) {
1451 if (max_srvr_rspsize) {
1452 int blo = min_srvr_rspsize/bucket_size/1024;
1453 int bhi = max_srvr_rspsize/bucket_size/1024;
1454 if (bhi - blo + 1 < nbuckets) {
1455 nbuckets = bhi - blo + 1;
1456 }
1457 }
1458 }
1459 }
1460
1461 int
main(int argc,char * argv[])1462 main(int argc, char *argv[])
1463 {
1464 int ch;
1465 char *cmd = argv[0];
1466 char *dev = NULL;
1467 char rfile[MAXPATHLEN] = {0};
1468 char wfile[MAXPATHLEN] = {0};
1469 unsigned int runtime = 60; // seconds
1470 report_type = REP_DURATION; // default report type
1471
1472 while ((ch = getopt(argc, argv, "I:Rc:m:V:w:x:X:y:Y:t:n:s:p:r:i:dTD:SPC")) != -1) {
1473 switch (ch) {
1474 case 'd':
1475 debug++;
1476 break;
1477 case 'i':
1478 if (dev == NULL) {
1479 dev = optarg;
1480 } else {
1481 usage(cmd);
1482 }
1483 break;
1484 case 't':
1485 runtime = atoi(optarg);
1486 break;
1487 case 'R':
1488 raw_output = true;
1489 break;
1490 case 'r':
1491 if (rfile[0] == 0) {
1492 snprintf(rfile, sizeof (rfile), "%s", optarg);
1493 } else {
1494 usage(cmd);
1495 }
1496 break;
1497 case 'n':
1498 nbuckets = atoi(optarg);
1499 break;
1500 case 's':
1501 bucket_size = atoi(optarg);
1502 break;
1503 case 'X':
1504 max_srvr_rspsize = atoi(optarg);
1505 break;
1506 case 'x':
1507 max_clnt_reqsize = atoi(optarg);
1508 break;
1509 case 'Y':
1510 min_srvr_rspsize = atoi(optarg);
1511 break;
1512 case 'y':
1513 min_clnt_reqsize = atoi(optarg);
1514 break;
1515 case 'p':
1516 port = atoi(optarg);
1517 break;
1518 case 'T':
1519 find_rtt++;
1520 break;
1521 case 'D':
1522 lo_val = atoi(optarg);
1523 break;
1524 case 'w':
1525 if (wfile[0] == 0) {
1526 snprintf(wfile, sizeof (wfile), "%s", optarg);
1527 } else {
1528 usage(cmd);
1529 }
1530 break;
1531 case 'V':
1532 output_version = atoi(optarg);
1533 break;
1534 case 'm':
1535 min_conntime = atoi(optarg);
1536 break;
1537 case 'c':
1538 min_conn_attempts = atoi(optarg);
1539 break;
1540 case 'C':
1541 if (report_type == REP_DURATION) {
1542 report_type = REP_CLBYTES;
1543 } else {
1544 fprintf(stderr, "Only one of -S,-P or -C please\n");
1545 usage(cmd);
1546 }
1547 break;
1548 case 'S':
1549 if (report_type == REP_DURATION) {
1550 report_type = REP_SRBYTES;
1551 } else {
1552 fprintf(stderr, "Only one of -S,-P or -C please\n");
1553 usage(cmd);
1554 }
1555 break;
1556 case 'P':
1557 if (report_type == REP_DURATION) {
1558 report_type = REP_THRUPUT;
1559 } else {
1560 fprintf(stderr, "Only one of -S,-P or -C please\n");
1561 usage(cmd);
1562 }
1563 break;
1564 case 'I':
1565 filter_st_time_str = optarg;
1566 break;
1567 case '?':
1568 default:
1569 usage(cmd);
1570 break;
1571 }
1572 }
1573
1574 if (filter_st_time_str) {
1575 struct tm tm;
1576 if (!str_2_tm(&tm)) {
1577 usage(cmd);
1578 }
1579 }
1580
1581 if (min_conntime && (output_version != OUTPUT_EXPERIMENTAL)) {
1582 printf("%s: illegal option -- m\n", cmd);
1583 usage(cmd);
1584 }
1585
1586 if (wfile[0] == 0) {
1587 if (nbuckets == 0) {
1588 nbuckets = NBUCKETS;
1589 }
1590 if (bucket_size == 0) {
1591 bucket_size = BUCKET_SIZE;
1592 }
1593 }
1594
1595 if (lo_val < bucket_size * nbuckets) {
1596 hi_val = lo_val + bucket_size;
1597 }
1598
1599 if (min_clnt_reqsize && max_clnt_reqsize && min_clnt_reqsize >= max_clnt_reqsize) {
1600 fprintf(stderr, "Client requests < %ld and > %ld?\n",
1601 max_clnt_reqsize, min_clnt_reqsize);
1602 usage(cmd);
1603 exit(1);
1604 }
1605 if (min_srvr_rspsize && max_srvr_rspsize && min_srvr_rspsize >= max_srvr_rspsize) {
1606 fprintf(stderr, "Server responses < %ld and > %ld?\n",
1607 max_srvr_rspsize, min_srvr_rspsize);
1608 usage(cmd);
1609 exit(1);
1610 }
1611
1612 if (min_clnt_reqsize && report_type == REP_CLBYTES) {
1613 lo_bucket = min_clnt_reqsize/1024/bucket_size;
1614 }
1615 if (min_srvr_rspsize && report_type == REP_SRBYTES) {
1616 lo_bucket = min_srvr_rspsize/1024/bucket_size;
1617 }
1618
1619 adjust_nbuckets();
1620
1621 argc -= optind;
1622 argv += optind;
1623
1624 if (port == 0) {
1625 fprintf(stderr, "Port must be specified\n");
1626 usage(cmd);
1627 }
1628 if (wfile[0] == 0) {
1629 if (nbuckets <= 0 || runtime <= 0 || bucket_size <= 0) {
1630 fprintf(stderr, "Illegal value for options\n");
1631 exit(1);
1632 }
1633 buckets = (struct bucket *)calloc(nbuckets + 1, sizeof (struct bucket));
1634 if (buckets == NULL) {
1635 perror("malloc:");
1636 exit(1);
1637 }
1638 }
1639
1640 if ((dev == NULL) && (rfile[0] == 0)) {
1641 dev = find_dev();
1642 if (dev == NULL) {
1643 LOG_ERROR("No devices found\n");
1644 LOG_ERROR("Try the command with a 'sudo' prefix\n");
1645 usage(cmd);
1646 }
1647 }
1648
1649 pc = open_pcap(rfile, dev, 256, 0, 1000);
1650 if (pc == NULL) {
1651 exit(1);
1652 }
1653
1654 if (set_filter(pc, dev, argc, argv) != 0) {
1655 exit (1);
1656 }
1657
1658 datalink = pcap_datalink(pc);
1659 switch (datalink) {
1660 case DLT_NULL:
1661 break;
1662 case DLT_EN10MB:
1663 break;
1664 default:
1665 printf("Unknown datalink type: %d\n", datalink);
1666 exit(1);
1667 break;
1668 }
1669
1670 if (rfile[0] == 0) {
1671 // We are not reading from a file
1672 signal(SIGINT, sighandler);
1673 signal(SIGALRM, sighandler);
1674 alarm(runtime);
1675 gettimeofday(&trace_st_time, NULL);
1676 } else {
1677 fromfile = true;
1678 }
1679
1680 if (wfile[0] != 0) {
1681 dumper = pcap_dump_open(pc, wfile);
1682 if (dumper == NULL) {
1683 LOG_ERROR("%s:pcap_dump_open:%s", wfile, strerror(errno));
1684 exit(1);
1685 }
1686 }
1687 if (dumper == NULL) {
1688 if (max_clnt_reqsize) {
1689 printf("Considering connections where client sends < %lu bytes to the server\n",
1690 max_clnt_reqsize);
1691 }
1692 if (max_srvr_rspsize) {
1693 printf("Considering connections where server sends < %lu bytes to the client\n",
1694 max_srvr_rspsize);
1695 }
1696 if (min_clnt_reqsize) {
1697 printf("Considering connections where clients sends > %lu bytes to the server\n", min_clnt_reqsize);
1698 }
1699 if (min_srvr_rspsize) {
1700 printf("Considering connections where server sends > %lu bytes to the client\n", min_srvr_rspsize);
1701 }
1702 if (find_rtt) {
1703 if (rfile[0] == 0) {
1704 fprintf(stderr, RTT_EST_ENABLED "Estimates will be wrong if this machine is the server for port %d\n", port);
1705 } else {
1706 fprintf(stderr, RTT_EST_ENABLED "Estimates will be wrong if this trace was captured on the server for port %d\n", port);
1707 }
1708 }
1709 }
1710 if (dumper) {
1711 if (debug) {
1712 fprintf(stderr, "-d ignored\n");
1713 }
1714 if (nbuckets) {
1715 fprintf(stderr, "-n ignored\n");
1716 }
1717 if (bucket_size) {
1718 fprintf(stderr, "-s ignored\n");
1719 }
1720 if (max_srvr_rspsize) {
1721 fprintf(stderr, "-X ignored\n");
1722 }
1723 if (max_clnt_reqsize) {
1724 fprintf(stderr, "-x ignored\n");
1725 }
1726 if (min_srvr_rspsize) {
1727 fprintf(stderr, "-Y ignored\n");
1728 }
1729 if (min_clnt_reqsize) {
1730 fprintf(stderr, "-y ignored\n");
1731 }
1732 if (lo_val != -1) {
1733 fprintf(stderr, "-D ignored\n");
1734 }
1735 if (find_rtt) {
1736 fprintf(stderr, "-T ignored\n");
1737 }
1738 if (raw_output) {
1739 fprintf(stderr, "-R ignored\n");
1740 }
1741 if (output_version) {
1742 fprintf(stderr, "-V ignored\n");
1743 }
1744 pcap_loop(pc, -1, pcap_dump, (u_char *)dumper);
1745 } else {
1746 if (fromfile) {
1747 pcap_loop(pc, -1, process_pkt, NULL);
1748 } else {
1749 while (1) {
1750 pcap_dispatch(pc, 128, process_pkt, NULL);
1751 if (done) {
1752 end_process();
1753 break;
1754 }
1755 }
1756 }
1757 }
1758
1759 if (rfile[0] != 0) {
1760 end_process();
1761 }
1762
1763 exit(0);
1764 }
1765