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