3 # Read a pcap dump containing a single TCP connection and analyze it to
4 # determine as much as possible about the performance of that connection.
5 # (Specifically designed for measuring performance of fetches to Amazon S3.)
7 import impacket, pcapy, re, sys
8 import impacket.ImpactDecoder, impacket.ImpactPacket
10 # Estimate of the network RTT
14 def __init__(self, connection, ts, pkt):
15 self.connection = connection
18 self.ip = self.pkt.child()
19 self.tcp = self.ip.child()
21 self.datalen = self.ip.get_ip_len() - self.ip.get_header_size() \
22 - self.tcp.get_header_size()
23 self.data = self.tcp.get_data_as_string()[0:self.datalen]
25 self.seq = (self.tcp.get_th_seq(), self.tcp.get_th_seq() + self.datalen)
26 self.ack = self.tcp.get_th_ack()
27 self.id = self.ip.get_ip_id()
29 if self.tcp.get_th_sport() == 80:
32 elif self.tcp.get_th_dport() == 80:
39 return "<Packet[%s]: id=%d seq=%d..%d ack=%d %s>" % \
40 ({-1: '<', 1: '>', 0: '?'}[self.direction], self.id,
41 self.seq[0], self.seq[1], self.ack, self.ts)
45 self.start_time = None
46 self.decoder = impacket.ImpactDecoder.EthDecoder()
49 def process_file(self, filename):
50 """Load a pcap file and process the packets contained in it."""
52 p = pcapy.open_offline(filename)
53 p.setfilter(r"ip proto \tcp")
54 assert p.datalink() == pcapy.DLT_EN10MB
55 p.loop(0, self.packet_handler)
57 def packet_handler(self, header, data):
58 """Callback function run by the pcap parser for each packet."""
60 (sec, us) = header.getts()
61 ts = sec * 1000000 + us
62 if self.start_time is None:
65 pkt = Packet(self, ts * 1e-6, self.decoder.decode(data))
66 self.packets.append(pkt)
68 def split_trace(packets, predicate, before=True):
69 """Split a sequence of packets apart where packets satisfy the predicate.
71 If before is True (default), the split happens just before the matching
72 packet; otherwise it happens just after.
91 def analyze_get(packets):
92 packets = iter(packets)
94 # First packet is the GET request itself
96 if not(p.direction > 0 and p.data.startswith('GET')):
97 print "Doesn't seem to be a GET request..."
103 # Find the first response packet containing data
104 while not(p.direction < 0 and p.datalen > 0):
111 print "Response time:", resp_ts - start_ts
113 # Scan through the incoming packets, looking for gaps in either the IP ID
114 # field or in the timing
118 if not p.direction < 0: continue
119 if p.id != (id_in + 1) & 0xffff:
121 print "Sequence number gap at", id_in
122 if p.ts - last_ts > 2 * RTT_EST:
124 print "Long gap of", p.ts - last_ts
125 elif p.ts - last_ts > RTT_EST / 2:
127 print "Short gap of", p.ts - last_ts
129 print " [occurred after", p.seq[0] - start_seq, "bytes, time", p.ts, "sec]"
131 print "Short packet of", p.datalen, "bytes, brings total to", p.seq[1] - start_seq
135 if __name__ == '__main__':
136 for f in sys.argv[1:]:
140 def request_start(p):
141 return p.direction > 0 and p.datalen > 0
142 for s in split_trace(conn.packets, request_start):
148 if p.ts - ts > 2 * RTT_EST:
149 print "LONG DELAY\n----"
152 if p.direction > 0 and p.datalen > 0:
153 print "Request:", repr(p.data)