1 (* Print out packets from a tcpdump / libpcap / wireshark capture file.
4 * To test this, capture some data using:
5 * /usr/sbin/tcpdump -s 1500 -w /tmp/dump
6 * then analyze it using:
9 * The file format is documented here:
10 * http://wiki.wireshark.org/Development/LibpcapFileFormat
12 * libpcap endianness is determined at runtime. Currently we don't
13 * handle this well - we need to write the match code out twice.
14 * Runtime endianness setting will solve this. See TODO list, item 11.
20 if Array.length Sys.argv <= 1 then failwith "libpcap dumpfile";
21 let bits = Bitmatch.bitstring_of_file Sys.argv.(1) in
22 let endian, file_header, bits = libpcap_header bits in
24 (* Read the packets and print them out. *)
26 let pkt_header, pkt_data, bits = libpcap_packet endian file_header bits in
27 decode_and_print_packet file_header pkt_header pkt_data;
34 and libpcap_header bits =
36 | { 0xd4c3b2a1_l : 32; (* writer was little endian *)
37 major : 16 : littleendian; (* version *)
38 minor : 16 : littleendian;
39 timezone : 32 : littleendian; (* timezone correction (seconds) *)
40 _ : 32 : littleendian; (* always 0 apparently *)
41 snaplen : 32 : littleendian; (* max length of captured packets *)
42 network : 32 : littleendian; (* data link layer type *)
45 Bitmatch.LittleEndian, (major, minor, timezone, snaplen, network), rest
47 | { 0xa1b2c3d4_l : 32; (* writer was big endian *)
48 major : 16; (* version *)
50 timezone : 32; (* timezone correction (seconds) *)
51 _ : 32; (* always 0 apparently *)
52 snaplen : 32; (* max length of captured packets *)
53 network : 32; (* data link layer type *)
56 Bitmatch.BigEndian, (major, minor, timezone, snaplen, network), rest
59 failwith "not a libpcap/tcpdump packet capture file"
61 and libpcap_packet endian file_header bits =
62 if endian = Bitmatch.LittleEndian then (
64 | { ts_sec : 32 : littleendian; (* packet timestamp seconds *)
65 ts_usec : 32 : littleendian; (* packet timestamp microseconds *)
66 incl_len : 32 : littleendian; (* packet length saved in this file *)
67 orig_len : 32 : littleendian; (* packet length originally on wire *)
68 pkt_data : Int32.to_int incl_len*8 : bitstring;
71 (ts_sec, ts_usec, incl_len, orig_len), pkt_data, rest
73 | { _ } -> raise End_of_file
76 | { ts_sec : 32; (* packet timestamp seconds *)
77 ts_usec : 32; (* packet timestamp microseconds *)
78 incl_len : 32; (* packet length saved in this file *)
79 orig_len : 32; (* packet length originally on wire *)
80 pkt_data : Int32.to_int incl_len*8 : bitstring;
83 (ts_sec, ts_usec, incl_len, orig_len), pkt_data, rest
85 | { _ } -> raise End_of_file
88 and decode_and_print_packet file_header pkt_header pkt_data =
89 let (ts_sec, ts_usec, _, orig_len) = pkt_header in
90 printf "%ld.%ld %ldB " ts_sec ts_usec orig_len;
92 (* Assume an ethernet frame containing an IPv4/6 packet. We ignore
93 * the ethertype field and determine the IP version from the packet
94 * itself. If it doesn't match our assumptions, hexdump it.
96 (bitmatch pkt_data with
97 | { d0 : 8; d1 : 8; d2 : 8; d3 : 8; d4 : 8; d5 : 8; (* ether dest *)
98 s0 : 8; s1 : 8; s2 : 8; s3 : 8; s4 : 8; s5 : 8; (* ether src *)
99 _ : 16; (* ethertype *)
100 packet : -1 : bitstring (* payload *)
102 printf "%x:%x:%x:%x:%x:%x < %x:%x:%x:%x:%x:%x "
103 d0 d1 d2 d3 d4 d5 s0 s1 s2 s3 s4 s5;
105 (bitmatch packet with
106 | { 4 : 4; (* IPv4 *)
107 hdrlen : 4; tos : 8; length : 16;
108 identification : 16; flags : 3; fragoffset : 13;
109 ttl : 8; protocol : 8; checksum : 16;
110 s0 : 8; s1 : 8; s2 : 8; s3 : 8;
111 d0 : 8; d1 : 8; d2 : 8; d3 : 8;
112 _(*options*) : (hdrlen-5)*32 : bitstring;
113 _(*payload*) : -1 : bitstring } ->
114 printf "IPv4 %d.%d.%d.%d < %d.%d.%d.%d "
115 s0 s1 s2 s3 d0 d1 d2 d3
117 | { 6 : 4; (* IPv6 *)
118 tclass : 8; flow : 20;
119 length : 16; nexthdr : 8; ttl : 8;
120 _(*source*) : 128 : bitstring;
121 _(*dest*) : 128 : bitstring;
122 _(*payload*) : -1 : bitstring } ->
126 printf "\n"; Bitmatch.hexdump_bitstring stdout packet
130 printf "\n"; Bitmatch.hexdump_bitstring stdout pkt_data