Source file arp_packet.ml
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
114
type op =
| Request
| Reply
let op_to_int = function Request -> 1 | Reply -> 2
let int_to_op = function 1 -> Some Request | 2 -> Some Reply | _ -> None
type t = {
operation : op;
source_mac : Macaddr.t;
source_ip : Ipaddr.V4.t;
target_mac : Macaddr.t;
target_ip : Ipaddr.V4.t;
}
let equal a b =
op_to_int a.operation = op_to_int b.operation &&
Macaddr.compare a.source_mac b.source_mac = 0 &&
Ipaddr.V4.compare a.source_ip b.source_ip = 0 &&
Macaddr.compare a.target_mac b.target_mac = 0 &&
Ipaddr.V4.compare a.target_ip b.target_ip = 0
type error =
| Too_short
| Unusable
| Unknown_operation of Cstruct.uint16
let[@coverage off] pp fmt t =
if t.operation = Request then
Format.fprintf fmt "ARP request from %a to %a, who has %a tell %a"
Macaddr.pp t.source_mac Macaddr.pp t.target_mac
Ipaddr.V4.pp t.target_ip Ipaddr.V4.pp t.source_ip
else
Format.fprintf fmt "ARP reply from %a to %a, %a is at %a"
Macaddr.pp t.source_mac Macaddr.pp t.target_mac
Ipaddr.V4.pp t.source_ip Macaddr.pp t.source_mac
let[@coverage off] pp_error ppf = function
| Too_short -> Format.pp_print_string ppf "frame too short (below 28 bytes)"
| Unusable -> Format.pp_print_string ppf "ARP address types are not IPv4 and Ethernet"
| Unknown_operation i -> Format.fprintf ppf "ARP message has unsupported operation %d" i
let ipv4_ethertype = 0x0800
and ipv4_size = 4
and ether_htype = 1
and ether_size = 6
and size = 28
let guard p e = if p then Ok () else Error e
let (>>=) x f = match x with
| Ok y -> f y
| Error e -> Error e
let decode buf =
let check_len buf = Cstruct.length buf >= size in
let check_hdr buf =
Cstruct.BE.get_uint16 buf 0 = ether_htype &&
Cstruct.BE.get_uint16 buf 2 = ipv4_ethertype &&
Cstruct.get_uint8 buf 4 = ether_size &&
Cstruct.get_uint8 buf 5 = ipv4_size
in
guard (check_len buf) Too_short >>= fun () ->
guard (check_hdr buf) Unusable >>= fun () ->
let op = Cstruct.BE.get_uint16 buf 6 in
match int_to_op op with
| None -> Error (Unknown_operation op)
| Some operation ->
let source_mac = Macaddr.of_octets_exn (Cstruct.to_string (Cstruct.sub buf 8 6))
and target_mac = Macaddr.of_octets_exn (Cstruct.to_string (Cstruct.sub buf 18 6))
and source_ip = Ipaddr.V4.of_int32 (Cstruct.BE.get_uint32 buf 14)
and target_ip = Ipaddr.V4.of_int32 (Cstruct.BE.get_uint32 buf 24) in
Ok {
operation ;
source_mac; source_ip ;
target_mac; target_ip
}
let hdr =
let buf = Cstruct.create 6 in
Cstruct.BE.set_uint16 buf 0 ether_htype;
Cstruct.BE.set_uint16 buf 2 ipv4_ethertype;
Cstruct.set_uint8 buf 4 ether_size;
Cstruct.set_uint8 buf 5 ipv4_size;
buf
let encode_into t buf =
Cstruct.blit hdr 0 buf 0 6 ;
Cstruct.BE.set_uint16 buf 6 (op_to_int t.operation) ;
Cstruct.blit_from_string (Macaddr.to_octets t.source_mac) 0 buf 8 6 ;
Cstruct.BE.set_uint32 buf 14 (Ipaddr.V4.to_int32 t.source_ip) ;
Cstruct.blit_from_string (Macaddr.to_octets t.target_mac) 0 buf 18 6 ;
Cstruct.BE.set_uint32 buf 24 (Ipaddr.V4.to_int32 t.target_ip)
[@@inline]
let encode t =
let buf = Cstruct.create_unsafe size in
encode_into t buf;
buf