-
Notifications
You must be signed in to change notification settings - Fork 2
/
Packet.ml
1411 lines (1194 loc) · 41.4 KB
/
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
open Sexplib
open Sexplib.Std
let icmp_code = 0x01
let igmp_code = 0x02
let tcp_code = 0x06
let udp_code = 0x11
let ip_code = 0x800
let arp_code = 0x806
exception UnparsablePacket of string
let clear_bit (n:int) (x:int32) : int32 =
Int32.logand x (Int32.lognot (Int32.shift_left Int32.one n))
let set_bit (n:int) (x:int32) : int32 =
Int32.logor x (Int32.shift_left Int32.one n)
let bit (x : int32) (n : int) (v : bool) : int32 =
if v then set_bit n x else clear_bit n x
let test_bit (n:int) (x:int32) : bool =
Int32.logand (Int32.shift_right_logical x n) Int32.one = Int32.one
let get_byte32 (n : Int32.t) (i : int) : int =
let open Int32 in
if i < 0 || i > 3 then
raise (Invalid_argument "get_byte32 index out of range");
to_int (logand 0xFFl (shift_right_logical n (8 * i)))
let get_byte (n:int64) (i:int) : int =
if i < 0 || i > 7 then
raise (Invalid_argument "Int64.get_byte index out of range");
Int64.to_int (Int64.logand 0xFFL (Int64.shift_right_logical n (8 * i)))
let bytes_of_mac (x:int64) : string =
let byte n = Char.chr (get_byte x n) in
Format.sprintf "%c%c%c%c%c%c"
(byte 5) (byte 4) (byte 3)
(byte 2) (byte 1) (byte 0)
let mac_of_bytes (str:string) : int64 =
if String.length str != 6 then
raise (Invalid_argument
(Format.sprintf "mac_of_bytes expected six-byte string, got %d
bytes" (String.length str)));
let byte n = Int64.of_int (Char.code (String.get str n)) in
let open Int64 in
logor (shift_left (byte 0) (8 * 5))
(logor (shift_left (byte 1) (8 * 4))
(logor (shift_left (byte 2) (8 * 3))
(logor (shift_left (byte 3) (8 * 2))
(logor (shift_left (byte 4) (8 * 1))
(byte 5)))))
let string_of_ip (ip : Int32.t) : string =
Format.sprintf "%d.%d.%d.%d" (get_byte32 ip 3) (get_byte32 ip 2)
(get_byte32 ip 1) (get_byte32 ip 0)
let string_of_ipv6 ((ip1,ip2) : int64*int64) : string =
Format.sprintf "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x"
(get_byte ip1 7) (get_byte ip1 6) (get_byte ip1 5) (get_byte ip1 4)
(get_byte ip1 3) (get_byte ip1 2) (get_byte ip1 1) (get_byte ip1 0)
(get_byte ip2 7) (get_byte ip2 6) (get_byte ip2 5) (get_byte ip2 4)
(get_byte ip2 3) (get_byte ip2 2) (get_byte ip2 1) (get_byte ip2 0)
let string_of_mac (x:int64) : string =
Format.sprintf "%02x:%02x:%02x:%02x:%02x:%02x"
(get_byte x 5) (get_byte x 4) (get_byte x 3)
(get_byte x 2) (get_byte x 1) (get_byte x 0)
let bytes_of_sexp s =
match s with
| Sexp.Atom w ->
begin
let n = String.length w in
let buf = Cstruct.create n in
for i = 0 to n - 1 do
Cstruct.set_char buf i w.[i]
done;
buf
end
| _ ->
failwith "bytes_of_sexp: expected Atom"
let sexp_of_bytes s =
let n = Cstruct.len s in
let buf = Buffer.create n in
for i = 0 to n - 1 do
Buffer.add_char buf (Cstruct.get_char s i)
done;
Sexp.Atom (Buffer.contents buf)
type bytes = Cstruct.t
type int8 = int with sexp
type int16 = int with sexp
type int48 = int64 with sexp
type dlAddr = int48 with sexp
type dlTyp = int16 with sexp
type dlVlan = int16 option with sexp
type dlVlanPcp = int8 with sexp
type dlVlanDei = bool with sexp
type nwAddr = int32 with sexp
type nwProto = int8 with sexp
type nwTos = int8 with sexp
type ipv6Addr = int64*int64 with sexp
type tpPort = int16 with sexp
let mk_pseudo_header (src : nwAddr) (dst : nwAddr) (proto : int) (len : int) =
(* XXX(seliopou): pseudo_header's allocated on every call. Given the usage
* pattern of this library, though, would it be safe to allocate once and
* reuse? *)
let pseudo_header = Cstruct.create 12 in
Cstruct.BE.set_uint32 pseudo_header 0 src;
Cstruct.BE.set_uint32 pseudo_header 4 dst;
Cstruct.set_uint8 pseudo_header 8 0;
Cstruct.set_uint8 pseudo_header 9 proto;
Cstruct.BE.set_uint16 pseudo_header 10 len;
pseudo_header
module Tcp = struct
module Flags = struct
type t =
{ ns : bool
; cwr : bool
; ece : bool
; urg : bool
; ack : bool
; psh : bool
; rst : bool
; syn : bool
; fin : bool } with sexp
let to_string f = Printf.sprintf
"{ ns = %B; cwr = %B; ece = %B; urg = %B; ack = %B; psh = %B; rst = %B; \
syn = %B; fin = %B }"
f.ns f.cwr f.ece f.urg f.ack f.psh f.rst f.syn f.fin
let to_int f =
let ret = Int32.zero in
let ret = bit ret 0 f.ns in
let ret = bit ret 1 f.cwr in
let ret = bit ret 2 f.ece in
let ret = bit ret 3 f.urg in
let ret = bit ret 4 f.ack in
let ret = bit ret 5 f.psh in
let ret = bit ret 6 f.rst in
let ret = bit ret 7 f.syn in
let ret = bit ret 8 f.fin in
Int32.to_int ret
let of_int d =
{ ns = test_bit 0 d
; cwr = test_bit 1 d
; ece = test_bit 2 d
; urg = test_bit 3 d
; ack = test_bit 4 d
; psh = test_bit 5 d
; rst = test_bit 6 d
; syn = test_bit 7 d
; fin = test_bit 8 d }
end
type t =
{ src : tpPort
; dst : tpPort
; seq : int32
; ack : int32
; offset : int8
; flags : Flags.t
; window : int16
; chksum : int8
; urgent : int8
; payload : bytes } with sexp
let format fmt v =
let open Format in
fprintf fmt "@[tpSrc=%d;tpDst=%d@]" v.src v.dst
cstruct tcp {
uint16_t src;
uint16_t dst;
uint32_t seq;
uint32_t ack;
uint16_t offset_flags; (* offset, reserved, and flags *)
uint16_t window;
uint16_t chksum;
uint16_t urgent
} as big_endian
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_tcp then
raise (UnparsablePacket "not enough bytes for TCP header");
let src = get_tcp_src bits in
let dst = get_tcp_dst bits in
let seq = get_tcp_seq bits in
let ack = get_tcp_ack bits in
let offset = get_tcp_offset_flags bits in
let offset = offset lsr 12 in
let _ = offset land 0x0f in
let flags = Flags.of_int (Int32.of_int (get_tcp_offset_flags bits)) in
let window = get_tcp_window bits in
let chksum = get_tcp_chksum bits in
let urgent = get_tcp_urgent bits in
(* TODO(JNF): support options *)
let payload = Cstruct.shift bits sizeof_tcp in
{ src = src;
dst = dst;
seq = seq;
ack = ack;
offset = offset;
flags = flags;
window = window;
chksum = chksum;
urgent = urgent;
payload = payload }
let len (pkt : t) = sizeof_tcp + Cstruct.len pkt.payload
(* Assumes that bits has enough room *)
let marshal (bits : Cstruct.t) (pkt : t) =
set_tcp_src bits pkt.src;
set_tcp_dst bits pkt.dst;
set_tcp_seq bits pkt.seq;
set_tcp_ack bits pkt.ack;
let offset_flags = (pkt.offset lsl 12) lor (Flags.to_int pkt.flags) in
set_tcp_offset_flags bits offset_flags;
set_tcp_window bits pkt.window;
set_tcp_chksum bits pkt.chksum;
set_tcp_urgent bits pkt.urgent;
let bits = Cstruct.shift bits sizeof_tcp in
Cstruct.blit pkt.payload 0 bits 0 (Cstruct.len pkt.payload)
let checksum (bits : Cstruct.t) (src : nwAddr) (dst : nwAddr) (pkt : t) =
let length = len pkt in
let pseudo_header = mk_pseudo_header src dst 0x6 length in
set_tcp_chksum bits 0;
let chksum = Tcpip_checksum.ones_complement_list
(if (length mod 2) = 0
then [pseudo_header; Cstruct.sub bits 0 length]
else [pseudo_header; Cstruct.sub bits 0 length; Cstruct.of_string "\x00"]) in
set_tcp_chksum bits chksum
end
module Udp = struct
type t =
{ src : tpPort
; dst : tpPort
; chksum : int16
; payload : bytes }
with sexp
let format fmt v =
let open Format in
fprintf fmt "@[tpSrc=%d;tpDst=%d@]" v.src v.dst
cstruct udp {
uint16_t src;
uint16_t dst;
uint16_t len;
uint16_t chksum
} as big_endian
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_udp then
raise (UnparsablePacket "not enough bytes for UDP header");
let src = get_udp_src bits in
let dst = get_udp_dst bits in
let chksum = get_udp_chksum bits in
let payload = Cstruct.shift bits sizeof_udp in
{ src = src;
dst = dst;
chksum = chksum;
payload = payload }
let len (pkt : t) = sizeof_udp + Cstruct.len pkt.payload
(* Assumes that bits has enough room *)
let marshal (bits : Cstruct.t) (pkt : t) =
set_udp_src bits pkt.src;
set_udp_dst bits pkt.dst;
set_udp_len bits (sizeof_udp + (Cstruct.len pkt.payload));
set_udp_chksum bits 0; (* UDP checksum is optional in IPv4 *)
let bits = Cstruct.shift bits sizeof_udp in
Cstruct.blit pkt.payload 0 bits 0 (Cstruct.len pkt.payload)
end
module Icmp = struct
type t = {
typ : int8;
code : int8;
chksum : int16;
payload : bytes
} with sexp
cstruct icmp {
uint8_t typ;
uint8_t code;
uint16_t chksum
} as big_endian
let format fmt v =
let open Format in
match v.typ with
| 0 -> fprintf fmt "ICMP echo reply";
| 8 -> fprintf fmt "ICMP echo request"
| n -> fprintf fmt "ICMP type=%d,code=%d" n v.code
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_icmp then
raise (UnparsablePacket "not enough bytes for ICMP header");
let typ = get_icmp_typ bits in
let code = get_icmp_code bits in
let chksum = get_icmp_chksum bits in
let payload = Cstruct.shift bits sizeof_icmp in
{ typ = typ; code = code; chksum = chksum; payload = payload }
let len (pkt: t) = sizeof_icmp + Cstruct.len pkt.payload
(* Assumes that bits has enough room. *)
let marshal (bits : Cstruct.t) (pkt : t) =
set_icmp_typ bits pkt.typ;
set_icmp_code bits pkt.code;
set_icmp_chksum bits pkt.chksum;
let bits = Cstruct.shift bits sizeof_icmp in
Cstruct.blit pkt.payload 0 bits 0 (Cstruct.len pkt.payload)
end
let rec indicies_maker n = if n = 0 then [] else [n]@(indicies_maker (n-1));;
(* TODO - enhance type & parsing with individual flags, like TCP or IP *)
(* TODO - add & expose some helpful constants such as A, MX, AAAA, etc. *)
(* TODO - DNS oddities: UTF-8 support, Punycode, & DNS string compression *)
(* TODO - create type hierarchy for RData: A, AAAA, NS, PTR, CNAME, MX, etc. *)
module Dns = struct
(* Helper to get the RLE-encoded, NULL-terminated names in DNS records *)
let get_dns_name bits =
let get_piece = (fun bits ->
let len = Cstruct.get_uint8 bits 0 in
Cstruct.copy bits 1 len) in
let rec get_pieces = (fun bits acc ->
let piece = get_piece bits in
let acc = acc @ [piece] in
let len = String.length piece in
let bits = Cstruct.shift bits (len + 1) in
if len > 0 then get_pieces bits acc
else acc) in
String.concat "." (get_pieces bits []);;
(* DNS string encoding requires 1 byte per '.' separated piece; since the
'.' is not included, we gain 1 for the first piece, plus NULL term *)
let dns_name_len (name : string) = (String.length name) + 2
(* Helper to set a DNS name; does not use the (optional) compression scheme.
Return 'bits' located at next write location. *)
let set_dns_name (bits : Cstruct.t) (name : string) =
let pieces = Str.split (Str.regexp "\\.") name in
let helper = (fun acc piece ->
let len = String.length piece in
Cstruct.set_uint8 bits acc len;
Cstruct.blit_from_string piece 0 bits (acc + 1) len;
(acc + len + 1)) in
let end_pos = List.fold_left helper 0 pieces in
Cstruct.set_uint8 bits end_pos 0; (* NULL terminator *)
Cstruct.shift bits (end_pos + 1)
(* DNS Question Description Records *)
module Qd = struct
type t = {
name : string;
typ : int16;
class_ : int16
} with sexp
cstruct qd {
(* preceeded by name *)
uint16_t typ;
uint16_t class_
} as big_endian
let format fmt v =
let open Format in
fprintf fmt "@[;(name=%s;typ=0x%x;class=0x%x)@]"
v.name v.typ v.class_
let parse (bits : Cstruct.t) =
let name = get_dns_name bits in
let bits = Cstruct.shift bits (dns_name_len name) in
if Cstruct.len bits < sizeof_qd then
raise (UnparsablePacket "not enough bytes for QD record");
let typ = get_qd_typ bits in
let class_ = get_qd_class_ bits in
{ name = name; typ = typ; class_ = class_ }
let len (qd : t) = (dns_name_len qd.name) + sizeof_qd
let marshal (bits : Cstruct.t) (qd : t) =
let bits = set_dns_name bits qd.name in
set_qd_typ bits qd.typ;
set_qd_class_ bits qd.class_;
Cstruct.shift bits sizeof_qd
end
(* DNS Resource Records *)
module Rr = struct
type t = {
name : string;
typ : int16;
class_ : int16;
ttl : int; (* TTL is signed 32-bit int *)
rdata : bytes
} with sexp
cstruct rr {
(* preceeded by name *)
uint16_t typ;
uint16_t class_;
int32_t ttl;
uint16_t rdlen
(* followed by variable-length RData *)
} as big_endian
let format fmt v =
let open Format in
fprintf fmt "@[;(name=%s;typ=0x%x;class=0x%x;ttl=%d)@]"
v.name v.typ v.class_ v.ttl
let parse (bits : Cstruct.t) =
let name = get_dns_name bits in
let bits = Cstruct.shift bits (dns_name_len name) in
if Cstruct.len bits < sizeof_rr then
raise (UnparsablePacket "not enough bytes for RR record");
let typ = get_rr_typ bits in
let class_ = get_rr_class_ bits in
let ttl = Int32.to_int (get_rr_ttl bits) in
let rdlen = get_rr_rdlen bits in
let rdata = Cstruct.sub bits sizeof_rr rdlen in
{ name = name; typ = typ; class_ = class_;
ttl = ttl; rdata = rdata }
let len (rr : t) =
(dns_name_len rr.name) + sizeof_rr + (Cstruct.len rr.rdata)
let marshal (bits : Cstruct.t) (rr : t) =
let bits = set_dns_name bits rr.name in
set_rr_typ bits rr.typ;
set_rr_class_ bits rr.class_;
set_rr_ttl bits (Int32.of_int rr.ttl);
let rdlen = Cstruct.len rr.rdata in
set_rr_rdlen bits rdlen;
Cstruct.blit rr.rdata 0 bits sizeof_rr rdlen;
Cstruct.shift bits (sizeof_rr + rdlen)
end
(* DNS Packet *)
type t =
{ id : int16
; flags : int16
; questions : Qd.t list
; answers : Rr.t list
; authority : Rr.t list
; additional : Rr.t list }
with sexp
let format fmt v =
let open Format in
fprintf fmt "@[id=%x;flags=%x@]" v.id v.flags;
List.iter (Qd.format fmt) v.questions;
List.iter (Rr.format fmt) v.answers;
List.iter (Rr.format fmt) v.authority;
List.iter (Rr.format fmt) v.additional
cstruct dns {
uint16_t id;
uint16_t flags;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount
(* followed by questions (if any) *)
(* followed by resource records (if any) *)
} as big_endian
let parse_helper (bits : Cstruct.t) (num : int) pf lf off =
let indices = indicies_maker num in
let offset = ref (sizeof_dns + off) in
let get_x = (fun i -> let bits = Cstruct.shift bits (!offset) in
let x = pf bits in
offset := (!offset + lf x);
x) in
(List.map get_x indices, !offset)
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_dns then
raise (UnparsablePacket "not enough bytes for DNS header");
let id = get_dns_id bits in
let flags = get_dns_flags bits in
let (qd, off) = parse_helper bits (get_dns_qdcount bits) Qd.parse Qd.len 0 in
let (an, off) = parse_helper bits (get_dns_ancount bits) Rr.parse Rr.len off in
let (ns, off) = parse_helper bits (get_dns_nscount bits) Rr.parse Rr.len off in
let (ar, off) = parse_helper bits (get_dns_arcount bits) Rr.parse Rr.len off in
{ id = id; flags = flags; questions = qd;
answers = an; authority = ns; additional = ar }
let len (pkt : t) =
let tally = fun lfun lst ->
List.fold_left (fun acc x -> acc + (lfun x)) 0 lst in
let qd_len = tally Qd.len pkt.questions in
let an_len = tally Rr.len pkt.answers in
let ns_len = tally Rr.len pkt.authority in
let ar_len = tally Rr.len pkt.additional in
sizeof_dns + qd_len + an_len + ns_len + ar_len
(* Assumes that bits has enough room *)
let marshal (bits : Cstruct.t) (pkt : t) =
set_dns_id bits pkt.id;
set_dns_flags bits pkt.flags;
set_dns_qdcount bits (List.length pkt.questions);
set_dns_ancount bits (List.length pkt.answers);
set_dns_nscount bits (List.length pkt.authority);
set_dns_arcount bits (List.length pkt.additional);
let bits = Cstruct.shift bits sizeof_dns in
let bits = List.fold_left Qd.marshal bits pkt.questions in
let bits = List.fold_left Rr.marshal bits pkt.answers in
let bits = List.fold_left Rr.marshal bits pkt.authority in
ignore (List.fold_left Rr.marshal bits pkt.additional)
let serialize (dns : t) =
let bits = Cstruct.create (len dns) in
let () = marshal bits dns in
bits
end
module Igmp1and2 = struct
type t = {
mrt: int8;
chksum : int16;
addr : nwAddr;
} with sexp
cstruct igmp1and2 {
uint8_t mrt;
uint16_t chksum;
uint32_t addr
} as big_endian
let format fmt v =
let open Format in
fprintf fmt "@[mrt=%x;addr=%s@]" v.mrt (string_of_ip v.addr)
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_igmp1and2 then
raise (UnparsablePacket "not enough bytes for IGMPv1/2 header");
let mrt = get_igmp1and2_mrt bits in
let chksum = get_igmp1and2_chksum bits in
let addr = get_igmp1and2_addr bits in
{ mrt = mrt; chksum = chksum; addr = addr; }
let len (msg: t) = sizeof_igmp1and2
(* Assumes that bits has enough room. *)
let marshal (bits : Cstruct.t) (msg : t) =
set_igmp1and2_mrt bits msg.mrt;
set_igmp1and2_chksum bits 0;
set_igmp1and2_addr bits msg.addr;
(* ADF: hack since Igmp.sizeof_igmp not defined at this point *)
let igmp_hdr = Cstruct.sub bits (-1) (1 + sizeof_igmp1and2) in
let chksum = Tcpip_checksum.ones_complement igmp_hdr in
set_igmp1and2_chksum bits chksum;
end
module Igmp3 = struct
(* IGMP v3 Group Records *)
module GroupRec = struct
type t = {
typ : int8;
addr : nwAddr;
sources : nwAddr list;
} with sexp
cstruct grouprec {
uint8_t typ;
uint8_t aux_len;
uint16_t num_sources;
uint32_t addr
(* followed by sources (if any) *)
} as big_endian
let format fmt v =
let open Format in
fprintf fmt "@[;(typ=%x;addr=%s;sources=%s)@]"
v.typ
(string_of_ip v.addr)
(String.concat "," (List.map string_of_ip v.sources))
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_grouprec then
raise (UnparsablePacket "not enough bytes for IGMPv3 group record");
let typ = get_grouprec_typ bits in
let num_sources = get_grouprec_num_sources bits in
let addr = get_grouprec_addr bits in
let indices = indicies_maker num_sources in
let get_source = fun i -> Cstruct.BE.get_uint32 bits (sizeof_grouprec + ((i-1) * 4)) in
let sources = List.map get_source indices in
{ typ = typ; addr = addr; sources = sources }
let len (gr : t) = sizeof_grouprec + (4 * List.length gr.sources)
let marshal (bits : Cstruct.t) (gr : t) =
set_grouprec_typ bits gr.typ;
set_grouprec_aux_len bits 0;
set_grouprec_num_sources bits (List.length gr.sources);
set_grouprec_addr bits gr.addr;
let bits = Cstruct.shift bits sizeof_grouprec in
List.iteri (fun i v -> Cstruct.BE.set_uint32 bits (i * 4) v) gr.sources;
Cstruct.shift bits (4 * List.length gr.sources)
end
type t = {
chksum : int16;
grs : GroupRec.t list;
} with sexp
cstruct igmp3 {
uint8_t reserved1;
uint16_t chksum;
uint16_t reserved2;
uint16_t num_records
(* followed by group records (if any) *)
} as big_endian
let format fmt v =
let open Format in
fprintf fmt "@[num_records=%d@]"
(List.length v.grs);
List.iter (GroupRec.format fmt) v.grs
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_igmp3 then
raise (UnparsablePacket "not enough bytes for IGMPv3 header");
let chksum = get_igmp3_chksum bits in
let num_records = get_igmp3_num_records bits in
let indices = indicies_maker num_records in
let offset = ref (sizeof_igmp3) in
let get_gr = (fun i -> let bits = Cstruct.shift bits (!offset) in
let gr = GroupRec.parse bits in
offset := (!offset + GroupRec.len gr);
gr) in
let grs = List.map get_gr indices in
{ chksum = chksum; grs = grs}
let len (msg: t) =
let grs_len = List.fold_left (fun acc gr -> acc + (GroupRec.len gr)) 0 msg.grs in
sizeof_igmp3 + grs_len
(* Assumes that bits has enough room. *)
let marshal (bits : Cstruct.t) (msg : t) =
set_igmp3_chksum bits 0;
set_igmp3_num_records bits (List.length msg.grs);
let gr_bits = Cstruct.shift bits sizeof_igmp3 in
ignore (List.fold_left GroupRec.marshal gr_bits msg.grs);
(* ADF: hack since Igmp.sizeof_igmp not defined at this point *)
let igmp_hdr = Cstruct.sub bits (-1) (1 + len msg) in
let chksum = Tcpip_checksum.ones_complement igmp_hdr in
set_igmp3_chksum bits chksum;
end
module Igmp = struct
type msg =
| Igmp1and2 of Igmp1and2.t
| Igmp3 of Igmp3.t
| Unparsable of (int8 * bytes)
with sexp
type t = {
ver_and_typ : int8;
msg : msg
} with sexp
cenum igmp_msg_type {
IGMP_MSG_QUERY = 0x11;
IGMP_v1_REPORT = 0x12;
IGMP_v2_REPORT = 0x16;
IGMP_v2_LEAVE = 0x17;
IGMP_v3_REPORT = 0x22
} as uint8_t
cstruct igmp {
uint8_t ver_and_typ (* version implicit in type. facepalm. *)
} as big_endian
let format_msg fmt = function
| Igmp1and2 igmp1and2 -> Igmp1and2.format fmt igmp1and2
| Igmp3 igmp3 -> Igmp3.format fmt igmp3
| Unparsable (_, bytes) -> Format.fprintf fmt "msg_len=%d" (Cstruct.len bytes)
let format_ver_and_typ fmt v =
let open Format in
match v with
| 0x11 -> fprintf fmt "IGMP Membership Query";
| 0x12 -> fprintf fmt "IGMP v1 Membership Report"
| 0x16 -> fprintf fmt "IGMP v2 Membership Report"
| 0x17 -> fprintf fmt "IGMP v2 Leave Group"
| 0x22 -> fprintf fmt "IGMP v3 Membership Report"
| n -> fprintf fmt "IGMP ver_and_type=%d" n
let format fmt v =
let open Format in
fprintf fmt "@[%a@,%a@]"
format_ver_and_typ v.ver_and_typ
format_msg v.msg
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_igmp then
raise (UnparsablePacket "not enough bytes for IGMP header");
let ver_and_typ = get_igmp_ver_and_typ bits in
let bits = Cstruct.shift bits sizeof_igmp in
let msg =
try match int_to_igmp_msg_type ver_and_typ with
| Some IGMP_MSG_QUERY -> Igmp1and2 (Igmp1and2.parse bits)
| Some IGMP_v1_REPORT -> Igmp1and2 (Igmp1and2.parse bits)
| Some IGMP_v2_REPORT -> Igmp1and2 (Igmp1and2.parse bits)
| Some IGMP_v2_LEAVE -> Igmp1and2 (Igmp1and2.parse bits)
| Some IGMP_v3_REPORT -> Igmp3 (Igmp3.parse bits)
| _ -> Unparsable (ver_and_typ, bits)
with UnparsablePacket _ -> Unparsable (ver_and_typ, bits) in
{ ver_and_typ = ver_and_typ; msg = msg }
let len (pkt: t) =
let msg_len = match pkt.msg with
| Igmp1and2 igmp1and2 -> Igmp1and2.len igmp1and2
| Igmp3 igmp3 -> Igmp3.len igmp3
| Unparsable (_, data) -> Cstruct.len data in
sizeof_igmp + msg_len
(* Assumes that bits has enough room. *)
let marshal (bits : Cstruct.t) (pkt : t) =
set_igmp_ver_and_typ bits pkt.ver_and_typ;
let bits = Cstruct.shift bits sizeof_igmp in
match pkt.msg with
| Igmp1and2 igmp1and2 ->
Igmp1and2.marshal bits igmp1and2
| Igmp3 igmp3 ->
Igmp3.marshal bits igmp3
| Unparsable (_, data) ->
Cstruct.blit data 0 bits 0 (Cstruct.len data)
end
module Ip = struct
type tp =
| Tcp of Tcp.t
| Udp of Udp.t
| Icmp of Icmp.t
| Igmp of Igmp.t
| Unparsable of (nwProto * bytes)
with sexp
module Flags = struct
(** [Flags] is the type of IPv4 flags. *)
type t =
{ df : bool (** Don't fragment. *)
; mf : bool (** More fragments. *)
} with sexp
let to_string v = Printf.sprintf "{ df = %B; mf = %B }" v.df v.mf
let of_int d =
{ df = test_bit 1 d
; mf = test_bit 2 d }
let to_int v =
let ret = Int32.zero in
let ret = bit ret 1 v.df in
let ret = bit ret 2 v.mf in
Int32.to_int ret
end
type t = {
tos : nwTos;
ident : int16;
flags : Flags.t;
frag : int16;
ttl : int8;
chksum : int16;
src : nwAddr;
dst : nwAddr;
options : bytes;
tp : tp
} with sexp
let format_tp fmt = function
| Tcp tcp -> Tcp.format fmt tcp
| Udp udp -> Udp.format fmt udp
| Icmp icmp -> Icmp.format fmt icmp
| Igmp igmp -> Igmp.format fmt igmp
| Unparsable (proto, _) -> Format.fprintf fmt "protocol=%d" proto
let format fmt v =
let open Format in
fprintf fmt "@[nwSrc=%s,nwDst=%s,%a@]"
(string_of_ip v.src)
(string_of_ip v.dst)
format_tp v.tp
cenum ip_proto {
IP_ICMP = 0x01;
IP_IGMP = 0x02;
IP_TCP = 0x06;
IP_UDP = 0x11
} as uint8_t
cstruct ip {
uint8_t vhl; (* version and ihl *)
uint8_t tos;
uint16_t len;
uint16_t ident;
uint16_t frag; (* flags and frag *)
uint8_t ttl;
uint8_t proto;
uint16_t chksum;
uint32_t src;
uint32_t dst
} as big_endian
let parse (bits : Cstruct.t) =
if Cstruct.len bits < sizeof_ip then
raise (UnparsablePacket "not enough bytes for IP header");
let vhl = get_ip_vhl bits in
if vhl lsr 4 <> 4 then
raise (UnparsablePacket "expected IPv4 header");
let ihl = vhl land 0x0f in
let tos = get_ip_tos bits in
let frag = get_ip_frag bits in
let flags = Flags.of_int (Int32.of_int (frag lsr 13)) in
let frag = frag land 0x1fff in
let ttl = get_ip_ttl bits in
let ident = get_ip_ident bits in
let proto = get_ip_proto bits in
let chksum = get_ip_chksum bits in
let src = get_ip_src bits in
let dst = get_ip_dst bits in
let options_len = (ihl * 4) - sizeof_ip in
let options = Cstruct.sub bits sizeof_ip options_len in
let bits = Cstruct.shift bits (ihl * 4) in
let tp =
try match int_to_ip_proto proto with
| Some IP_ICMP -> Icmp (Icmp.parse bits)
| Some IP_IGMP -> Igmp (Igmp.parse bits)
| Some IP_TCP -> Tcp (Tcp.parse bits)
| Some IP_UDP -> Udp (Udp.parse bits)
| _ -> Unparsable (proto, bits)
with UnparsablePacket _ -> Unparsable (proto, bits) in
{ tos = tos;
ident = ident;
flags = flags;
frag = frag;
ttl = ttl;
chksum = chksum;
src = src;
dst = dst;
options = options;
tp = tp }
let len (pkt : t) =
let options_len = Cstruct.len pkt.options in
let tp_len = match pkt.tp with
| Tcp tcp -> Tcp.len tcp
| Udp udp -> Udp.len udp
| Icmp icmp -> Icmp.len icmp
| Igmp igmp -> Igmp.len igmp
| Unparsable (_, data) -> Cstruct.len data in
sizeof_ip + options_len + tp_len
(* Assumes there is enough space *)
let marshal (bits : Cstruct.t) (pkt:t) =
let header_len = sizeof_ip + (Cstruct.len pkt.options) in
let v = 4 in (* IP version 4. *)
let ihl = header_len / 4 in
let vhl = (v lsl 4) lor ihl in
set_ip_vhl bits vhl;
set_ip_tos bits pkt.tos;
set_ip_len bits (len pkt);
set_ip_ident bits pkt.ident;
set_ip_frag bits (((Flags.to_int pkt.flags) lsl 13) lor pkt.frag);
set_ip_ttl bits pkt.ttl;
let proto = match pkt.tp with
| Tcp _ -> tcp_code
| Udp _ -> udp_code
| Icmp _ -> icmp_code
| Igmp _ -> igmp_code
| Unparsable (p, _) -> p in
set_ip_proto bits proto;
set_ip_src bits pkt.src;
set_ip_dst bits pkt.dst;
Cstruct.blit pkt.options 0 bits sizeof_ip (Cstruct.len pkt.options);
set_ip_chksum bits 0;
let chksum = Tcpip_checksum.ones_complement (Cstruct.sub bits 0 header_len) in
set_ip_chksum bits chksum;
let bits = Cstruct.shift bits header_len in
match pkt.tp with
| Tcp tcp ->
Tcp.marshal bits tcp;
Tcp.checksum bits pkt.src pkt.dst tcp
| Udp udp ->
Udp.marshal bits udp
| Icmp icmp ->
Icmp.marshal bits icmp
| Igmp igmp ->
Igmp.marshal bits igmp
| Unparsable (protocol, data) ->
Cstruct.blit data 0 bits 0 (Cstruct.len data)
end
module Arp = struct
type t =
| Query of dlAddr * nwAddr * nwAddr
| Reply of dlAddr * nwAddr * dlAddr * nwAddr
with sexp
let format fmt v =
let open Format in
match v with
| Query (srcMac, srcIP,dstIP) ->
(* src mac should be the same as ethernet srcMac, in theory *)
fprintf fmt "@[ARP Query,senderIP=%s,targetIP=%s@]"
(string_of_ip srcIP) (string_of_ip dstIP)
| Reply (srcMac, srcIP, dstMac, dstIP) ->
(* src mac should be the same as ethernet srcMac, in theory *)
fprintf fmt "@[ARP Reply,senderMac=%s,senderIP=%s,targetIP=%s@]"
(string_of_mac srcMac)
(string_of_ip srcIP)
(string_of_ip dstIP)
(* Network *)
cstruct arp {
uint16_t htype;
uint16_t ptype;
uint8_t hlen;
uint8_t plen;
uint16_t oper;
uint8_t sha[6];
uint32_t spa;
uint8_t tha[6];
uint32_t tpa
} as big_endian
let nwSrc t = match t with
| Query (_, ip, _) -> ip
| Reply (_, ip, _, _) -> ip
let nwDst t = match t with
| Query (_, _, ip) -> ip
| Reply (_, _, _, ip) -> ip