1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
(* Print out packets from a tcpdump / libpcap / wireshark capture file.
* $Id: libpcap.ml 142 2008-07-17 15:45:56Z richard.wm.jones $
*
* To test this, capture some data using:
* /usr/sbin/tcpdump -s 1500 -w /tmp/dump
* then analyze it using:
* ./libpcap /tmp/dump
*
* The file format is documented here:
* http://wiki.wireshark.org/Development/LibpcapFileFormat
*
* libpcap endianness is determined at runtime.
*)
open Printf
let rec main () =
if Array.length Sys.argv <= 1 then failwith "libpcap dumpfile";
let bits = Bitstring.bitstring_of_file Sys.argv.(1) in
let endian, file_header, bits = libpcap_header bits in
(* Read the packets and print them out. *)
let rec loop bits =
let pkt_header, pkt_data, bits = libpcap_packet endian file_header bits in
decode_and_print_packet file_header pkt_header pkt_data;
loop bits
in
try loop bits
with
End_of_file -> ()
(* Determine the endianness (at runtime) from the magic number. *)
and endian_of = function
| 0xa1b2c3d4_l -> Bitstring.BigEndian
| 0xd4c3b2a1_l -> Bitstring.LittleEndian
| _ -> assert false
and libpcap_header bits =
bitmatch bits with
| { ((0xa1b2c3d4_l|0xd4c3b2a1_l) as magic) : 32; (* magic number *)
major : 16 : endian (endian_of magic); (* version *)
minor : 16 : endian (endian_of magic);
timezone : 32 : endian (endian_of magic); (* timezone correction (secs)*)
_ : 32 : endian (endian_of magic); (* always 0 apparently *)
snaplen : 32 : endian (endian_of magic); (* max length of capt pckts *)
network : 32 : endian (endian_of magic); (* data link layer type *)
rest : -1 : bitstring
} ->
endian_of magic, (major, minor, timezone, snaplen, network), rest
| { _ } ->
failwith "not a libpcap/tcpdump packet capture file"
and libpcap_packet e file_header bits =
bitmatch bits with
| { ts_sec : 32 : endian (e); (* packet timestamp seconds *)
ts_usec : 32 : endian (e); (* packet timestamp microseconds *)
incl_len : 32 : endian (e); (* packet length saved in this file *)
orig_len : 32 : endian (e); (* packet length originally on wire *)
pkt_data : Int32.to_int incl_len*8 : bitstring;
rest : -1 : bitstring
} ->
(ts_sec, ts_usec, incl_len, orig_len), pkt_data, rest
| { _ } -> raise End_of_file
and decode_and_print_packet file_header pkt_header pkt_data =
let (ts_sec, ts_usec, _, orig_len) = pkt_header in
printf "%ld.%ld %ldB " ts_sec ts_usec orig_len;
(* Assume an ethernet frame containing an IPv4/6 packet. We ignore
* the ethertype field and determine the IP version from the packet
* itself. If it doesn't match our assumptions, hexdump it.
*)
(bitmatch pkt_data with
| { d0 : 8; d1 : 8; d2 : 8; d3 : 8; d4 : 8; d5 : 8; (* ether dest *)
s0 : 8; s1 : 8; s2 : 8; s3 : 8; s4 : 8; s5 : 8; (* ether src *)
_ : 16; (* ethertype *)
packet : -1 : bitstring (* payload *)
} ->
printf "%x:%x:%x:%x:%x:%x < %x:%x:%x:%x:%x:%x "
d0 d1 d2 d3 d4 d5 s0 s1 s2 s3 s4 s5;
(bitmatch packet with
| { 4 : 4; (* IPv4 *)
hdrlen : 4; tos : 8; length : 16;
identification : 16; flags : 3; fragoffset : 13;
ttl : 8; protocol : 8; checksum : 16;
s0 : 8; s1 : 8; s2 : 8; s3 : 8;
d0 : 8; d1 : 8; d2 : 8; d3 : 8;
_(*options*) : (hdrlen-5)*32 : bitstring;
_(*payload*) : -1 : bitstring } ->
printf "IPv4 %d.%d.%d.%d < %d.%d.%d.%d "
s0 s1 s2 s3 d0 d1 d2 d3
| { 6 : 4; (* IPv6 *)
tclass : 8; flow : 20;
length : 16; nexthdr : 8; ttl : 8;
_(*source*) : 128 : bitstring;
_(*dest*) : 128 : bitstring;
_(*payload*) : -1 : bitstring } ->
printf "IPv6 ";
| { _ } ->
printf "\n"; Bitstring.hexdump_bitstring stdout packet
)
| { _ } ->
printf "\n"; Bitstring.hexdump_bitstring stdout pkt_data
);
printf "\n"
let () = main ()
|