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