module Packet:sig
..end
The purpose of this module is to help individual packet inquiry and manipulation by offering an unpacked view of a single packet. It is not used when simulating a network.
This is the module to look for if you want to fiddle with packets on a one to one basis, for instance to fuzz some packet source, collect some statistics on individual packets, search for some packets in a pcap file, etc...
Due to the fact that every packet is considered in isolation, it can only decode the simplest protocols for which all required data fits within a single packet. In other words, it will not venture much deeper than TCP/UDP.
This module is more useful from the toplevel robinet.top
.
Suppose you have this huge pcap file
of several tens of gigabyte, and we want to get some idea of what's
in there. We start with the obvious:
# Pcap.infos_of "big_one.pcap";;
- : Pcap.infos =
{Pcap.filename = "big_one.pcap"; Pcap.data_link_type = 1l;
Pcap.num_packets = 53993956; Pcap.data_size = 40756596469L;
Pcap.start_time = 1323766040.99259496; Pcap.stop_time = 1323767137.688411}
This will take a significant amount of time since the whole pcap file must be scanned.
Now we are interrested in all the packets attempting to connect port 80:
# open Packet;;
# let s80 = enum_of_file "big_one.pcap" //
function [_;_;_;_; Pdu.Tcp { Tcp.Pdu.dst_port = p ;
Tcp.Pdu.flags = { Tcp.Pdu.syn = true ; _ } ;
_ }] -> p = Tcp.Port.o 80
| _ -> false;;
val s80 : Packet.Pdu.layer list BatEnum.t = <abstr>
This will return surprisingly fast, due to the lazy nature of Enum.filter. Note that in this exemple, for brevety, I pattern match for Tcp in 5th position only (since my big_file.pcap have a 802.1q tunnel between Ethernet and IP).
Let us have a look at the first of them:
# Enum.peek s80;;
- : Packet.Pdu.layer list option =
Some
[Packet.Pdu.Pcap
{Pcap.Pdu.source_name = "big_one.pcap"; Pcap.Pdu.caplen = 78;
Pcap.Pdu.dlt = Ethernet (10Mb);
Pcap.Pdu.ts = 22:32:18.38;
Pcap.Pdu.payload = 78 bytes};
Packet.Pdu.Eth
{Eth.Pdu.src = Cisco:1d:6d:01;
Eth.Pdu.dst = Cisco:4d:5c:01;
Eth.Pdu.proto = Eth8021q;
Eth.Pdu.payload = 64 bytes};
Packet.Pdu.Vlan
{Vlan.Pdu.prio = 0; Vlan.Pdu.cfi = false; Vlan.Pdu.id = 250;
Vlan.Pdu.proto = IP; Vlan.Pdu.payload = 60 bytes};
Packet.Pdu.Ip
{Ip.Pdu.tos = 0; Ip.Pdu.tot_len = 60; Ip.Pdu.id = 25178;
Ip.Pdu.dont_frag = true; Ip.Pdu.more_frags = false;
Ip.Pdu.frag_offset = 0; Ip.Pdu.ttl = 62; Ip.Pdu.proto = tcp;
Ip.Pdu.src = 193.51.52.41;
Ip.Pdu.dst = 91.202.200.31; Ip.Pdu.options = ;
Ip.Pdu.payload = 40 bytes};
Packet.Pdu.Tcp
{Tcp.Pdu.src_port = 44994; Tcp.Pdu.dst_port = www;
Tcp.Pdu.seq_num = 0xC7A42126;
Tcp.Pdu.ack_num = 0x00000000; Tcp.Pdu.win_size = 5840;
Tcp.Pdu.flags = Syn; Tcp.Pdu.checksum = Some 38264;
Tcp.Pdu.urg_ptr = 0;
Tcp.Pdu.options =
02 04 05 64 01 01 08 0a - 8c 61 60 83 00 00 00 00 ...d.....a......
01 03 03 07 ....
;
Tcp.Pdu.payload = empty}]
Now imagine you want to edit a pcap to change the TCP source/dest port from 21 to 2121.
# open Packet;;
# let old = Tcp.Port.o 21 and newp = Tcp.Port.o 2121 in
enum_of_file "some.pcap" /@
(function cap::eth::ip::Pdu.Tcp ({ Tcp.Pdu.dst_port = port ; _ } as tcp)::rest when port = old ->
cap::eth::ip::Pdu.Tcp { tcp with Tcp.Pdu.dst_port = newp }::rest
| cap::eth::ip::Pdu.Tcp ({ Tcp.Pdu.src_port = port ; _ } as tcp)::rest when port = old ->
cap::eth::ip::Pdu.Tcp { tcp with Tcp.Pdu.src_port = newp }::rest
| x -> x) |>
to_file "changed.pcap" ;;
module Pdu:sig
..end
val enum_of_file : string -> Pdu.layer list BatEnum.t
val to_file : string -> Pdu.layer list Batteries.Enum.t -> unit