Module Pcap

module Pcap: sig .. end
This module holds all functions related to libpcap, packet sniffing, packet injection and pcap file reading and writing.

All I want is sniffing packets:

To grab the first packet from "em1" interface and display it:

# let itf = Pcap.openif "em1";;
val itf : Pcap.iface = {Pcap.handler = ; name = "em1"; caplen = 1500}
# let pkt = Pcap.sniff itf;;
val pkt : Pcap.Pdu.t =
 {Pcap.Pdu.source_name = "em1"; caplen = 1500;
   dlt = Ethernet (10Mb); ts = 15:09:49.81;
   payload = 66 bytes (74 46 a0 a1 28 8e 00...)}
# Packet.Pdu.unpack pkt;;
- : Packet.Pdu.layer list =
 [Packet.Pdu.Pcap
   {Pcap.Pdu.source_name = "em1"; caplen = 1500;
    dlt = Ethernet (10Mb); ts = 15:09:49.81;
    payload = 66 bytes (74 46 a2 a1 28 8e 00...)};
  Packet.Pdu.Eth
   {Eth.Pdu.src = Cisco:25:ac:42;
    dst = 74:46:a2:a1:28:8e; proto = IP;
    payload = 52 bytes (45 20 00 34 86 aa 40...)};
  Packet.Pdu.Ip
   {Ip.Pdu.tos = 32; tot_len = 52; id = 34474; dont_frag = true;
    more_frags = false; frag_offset = 0; ttl = 58; proto = tcp;
    src = 172.16.255.194; dst = 172.28.11.20;
    options = ; payload = 32 bytes (02 02 bc c8 a2 6c d0...)};
  Packet.Pdu.Tcp
   {Tcp.Pdu.src_port = shell; dst_port = 48328;
    seq_num = 0xA26CD0EB; ack_num = 0x304E4FC3;
    win_size = 501; flags = Ack; urg_ptr = 0;
    options = 01 01 08 0a 28 f5 dd 89 - 57 c2 c6 25              .....��.W��%
    ;
    payload = empty}]

Following packets can be dumped easily with just

Pcap.sniff itf |> Packet.Pdu.unpack
("|>" is like the UNIX pipe).

All I want is editing pcap files:

To create a small pcap file with a single packet:

Tcp.Pdu.make ~dst_port:(Tcp.Port.o 5000) (bitstring_of_string "HTTP/1.2 pas glop") |>
    Tcp.Pdu.pack |>
    Ip.Pdu.make Ip.Proto.tcp (Ip.Addr.random ()) (Ip.Addr.random ()) |>
    Ip.Pdu.pack |>
    Eth.Pdu.make Arp.HwProto.ip4 (Eth.Addr.random ()) (Eth.Addr.random ()) |>
    Eth.Pdu.pack |>
    Pcap.save "/tmp/random.pcap";;

To grep a string into a pcap file and obtain another pcap file with matching packets only:

let grep needle haystack = try String.find haystack needle ; true with Not_found -> false in
Pcap.enum_of_file "input.pcap" |>
    Enum.filter (fun pdu -> grep "needle" (string_of_bitstring (pdu.Pcap.Pdu.payload :> bitstring))) |>
    Pcap.file_of_enum "output.pcap";;


val debug : bool

Libpcap low level wrappers


type iface_handler 
Libpcap network interface handler.
val inject_ : iface_handler -> string -> unit
inject_ iface_handler packet inject this packet into this interface
val sniff_ : iface_handler -> Clock.Time.t * string
sniff_ iface_handler will return the next available packet as a string, as well as its capture timestamp.
val openif_ : string -> bool -> string -> int -> iface_handler
openif_ "eth0" true "port 80" 96 returns the iface representing eth0, in promiscuous mode, filtering port 80 and capturing only the first 96 bytes of each packets. Notice that if caplen is set to 0 then a "default" value of 65535 will be chosen, which is probably not what you want. You should set caplen = your MTU size.

Pcap files


module Dlt: sig .. end
Data Link Types are constant values indicating what protocol and hardware technology some captured packets were taken from.
type global_header = {
   name : string; (*
The file name.
*)
   endianness : Bitstring.endian; (*
Endianess of the file.
*)
   version_major : int; (*
Libpcap version.
*)
   version_minor : int;
   this_zone : int32; (*
Time zone (should be zero, unused).
*)
   sigfigs : int32; (*
unused.
*)
   snaplen : int32; (*
Indicate that no caplen will be smaller. We don't use this.
*)
   dlt : Dlt.t; (*
The Data Link Type (see Pcap.Dlt).
*)
}
The global header of a pcap file.

Captured packet


module Pdu: sig .. end
Packets harvested with libpcap will come with additional informations such as caplen, timestamp etc.
val save : ?caplen:int -> ?dlt:Dlt.t -> string -> Tools.Payload.outer_t -> unit
save "file.pcap" returns a function that will save passed bitstrings as packets in "file.pcap".
caplen : can be used to cap saved packet to a given number of bytes
dlt : can be used to change the file's DLT (required if you do not write Ethernet packets)
exception Not_a_pcap_file
When trying to read packets from a file that doesn't look like a pcap file.
val bitstring_of_global_header : global_header -> Bitstring.bitstring
val global_header_of_bitstring : string -> Bitstring.bitstring -> global_header
val read_global_header : string -> global_header * BatIO.input
read_global_header filename reads the pcap global header from the given file, and returns both a Pcap.global_header and the input channel.
val read_next_pkt : global_header -> Batteries.IO.input -> Pdu.t
read_next_pkt global_header ic will return the next Pcap.Pdu.t that's to be read from the input stream ic.
val enum_of_file : string -> Pdu.t Batteries.Enum.t
From a pcap file, returns an Enum.t of Pcap.Pdu.t.
val write_global_header : string -> global_header -> unit BatIO.output
write_global_header filename write a 'generic' pcap global header and returns the output channel.
val write_next_pkt : unit BatIO.output -> Pdu.t -> unit
write_next_pkt global_header ic will return the next Pcap.Pdu.t that's to be read from the input stream ic.
val file_of_enum : string -> ?dlt:Dlt.t -> Pdu.t Batteries.Enum.t -> unit
file_of_enum filename e will save an Enum.t of Pcap.Pdu.t into the file named filename.

Tools


type infos = {
   filename : string;
   data_link_type : Dlt.t;
   num_packets : int;
   data_size : int64;
   start_time : Clock.Time.t;
   stop_time : Clock.Time.t;
}
Informations on a pcap file.
val infos_of : string -> infos
Return some informations about a pcap file (require to scan the whole file, so depending on the file size it may take some time).
val merge : Pdu.t Batteries.Enum.t list -> Pdu.t Batteries.Enum.t
merge [e1 ; e2 ; e3] will merge the three Enumt.t of packets in chronological order.
val repair_file : string -> unit
Small utility that truncate a pcap file to the last valid packet. Useful for those interrupted/damaged pcap files with an incomplete packet at the end, that some tools then refuse to read.
val play : (Bitstring.bitstring -> 'a) -> string -> unit
play tx "file.pcap" will read packets from "file.pcap" and send them to tx copying the pcap file frame rate. Notice that we use the internal Clock for this, so it's both very accurate or not accurate at all, depending on how you look at it.

User friendly functions for capturing/injecting packets


type iface = {
   handler : iface_handler;
   name : string;
   caplen : int;
}
A network device opened for sniffing or injection
val mtu : string -> int
Get the MTU of a device (on Linux). May raise all kind of exceptions.
val openif : ?promisc:bool -> ?filter:string -> ?caplen:int -> string -> iface
openif "eth0" true "port 80" 96 returns the iface representing eth0, in promiscuous mode, filtering port 80 and capturing only the first 96 bytes of each packets. Notice that if caplen is not set then MTU for the device will be chosen.
val sniff : iface -> Pdu.t
sniff iface will return the next available packet as a Pcap.Pdu.t.

Packet injection


val packets_injected_ok : Metric.Atomic.t
A counter for how many packets were injected successfully.
val packets_injected_err : Metric.Atomic.t
A counter for how many packets we failed to inject.
val bytes_out : Metric.Counter.t
A counter for how many bytes were injected successfully.
val inject : iface -> Bitstring.bitstring -> unit
inject iface bits inject the packet bits into interface iface.

Packet sniffing


val packets_sniffed_ok : Metric.Atomic.t
A counter for how many packets were sniffed.
val bytes_in : Metric.Counter.t
A counter for how many bytes were sniffed.
val sniffer : iface -> (Bitstring.bitstring -> unit) -> Thread.t
sniffer iface rx returns a thread that continuously sniff packets and pass them to the rx function (via the Clock).