mte/unikernel/duniverse/mirage-tcpip/test/test_ipv4.ml
2025-11-11 02:07:51 +01:00

303 lines
14 KiB
OCaml

open Common
let test_unmarshal_with_options () =
let datagram = Cstruct.create 40 in
Cstruct.blit_from_string ("\x46\xc0\x00\x28\x00\x00\x40\x00\x01\x02" ^
"\x42\x49\xc0\xa8\x01\x08\xe0\x00\x00\x16\x94\x04\x00\x00\x22" ^
"\x00\xfa\x02\x00\x00\x00\x01\x03\x00\x00\x00\xe0\x00\x00\xfb") 0 datagram 0 40;
match Ipv4_packet.Unmarshal.of_cstruct datagram with
| Ok ({Ipv4_packet.options ; _}, payload) ->
Alcotest.(check int) "options" (Cstruct.length options) 4;
Alcotest.(check int) "payload" (Cstruct.length payload) 16;
Lwt.return_unit
| _ ->
Alcotest.fail "Fail to parse ip packet with options"
let test_unmarshal_without_options () =
let datagram = Cstruct.create 40 in
Cstruct.blit_from_string ("\x45\x00\x00\x28\x19\x29\x40\x00\x34\x06\x98\x75\x36\xb7" ^
"\x9c\xca\xc0\xa8\x01\x08\x00\x50\xca\xa6\x6f\x19\xf4\x76" ^
"\x00\x00\x00\x00\x50\x04\x00\x00\xec\x27\x00\x00") 0 datagram 0 40;
match Ipv4_packet.Unmarshal.of_cstruct datagram with
| Ok ({Ipv4_packet.options ; _}, payload) ->
Alcotest.(check int) "options" (Cstruct.length options) 0;
Alcotest.(check int) "payload" (Cstruct.length payload) 20;
Lwt.return_unit
| _ ->
Alcotest.fail "Fail to parse ip packet with options"
let test_unmarshal_regression () =
let p = Cstruct.of_string "\x49\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30" in
Alcotest.(check (result reject pass))
"correctly return error for bad packet"
(Error "any") (Ipv4_packet.Unmarshal.of_cstruct p);
Lwt.return_unit
let test_size () =
let src = Ipaddr.V4.of_string_exn "127.0.0.1" in
let dst = Ipaddr.V4.of_string_exn "127.0.0.2" in
let ttl = 64 in
let ip = { Ipv4_packet.src; dst; proto = 17; ttl; id = 0 ; off = 0 ; options = (Cstruct.of_string "aaaa") } in
let payload = Cstruct.of_string "abcdefgh" in
let tmp = Ipv4_packet.Marshal.make_cstruct ~payload_len:(Cstruct.length payload) ip in
let tmp = Cstruct.concat [tmp; payload] in
Ipv4_packet.Unmarshal.of_cstruct tmp
|> Alcotest.(check (result (pair ipv4_packet cstruct) string)) "Loading an IP packet with IP options" (Ok (ip, payload));
Lwt.return_unit
let test_packet =
let src = Ipaddr.V4.of_string_exn "127.0.0.1" in
let dst = Ipaddr.V4.of_string_exn "127.0.0.2" in
let ttl = 64 in
{ Ipv4_packet.src; dst; proto = 17; ttl; id = 0 ; off = 0 ; options = (Cstruct.of_string "aaaa") }
let mf = 0x2000
let white = Cstruct.create 16
let black =
let buf = Cstruct.create 16 in
Cstruct.memset buf 0xFF ;
buf
let gray =
let buf = Cstruct.create 16 in
Cstruct.memset buf 0x55 ;
buf
let empty_cache = Fragments.Cache.empty 1000
let basic_fragments payload () =
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
(Some (test_packet, payload))
(snd @@ Fragments.process empty_cache 0L test_packet payload)) ;
let off_packet = { test_packet with off = 1 } in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
None
(snd @@ Fragments.process empty_cache 0L off_packet payload)) ;
Lwt.return_unit
let basic_reassembly () =
let more_frags = { test_packet with off = mf } in
let cache, res = Fragments.process empty_cache 0L more_frags black in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
let off_packet = { test_packet with off = 2 } in
Alcotest.(check (option (pair ipv4_packet cstruct)) "reassembly of two segments works"
(Some (test_packet, Cstruct.append black white))
(snd @@ Fragments.process cache 0L off_packet white)) ;
Lwt.return_unit
let basic_reassembly_timeout () =
let more_frags = { test_packet with off = mf } in
let cache, res = Fragments.process empty_cache 0L more_frags black in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
let off_packet = { test_packet with off = 2 } in
let below_max = Int64.sub Fragments.max_duration 1L in
Alcotest.(check (option (pair ipv4_packet cstruct)) "even after just before max duration"
(Some (test_packet, Cstruct.append black white))
(snd @@ Fragments.process cache below_max off_packet white)) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) "none after max duration"
None
(snd @@ Fragments.process cache Fragments.max_duration off_packet white)) ;
let more_off_packet = { test_packet with off = mf lor 2 } in
let cache, res = Fragments.process cache below_max more_off_packet gray in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
let final_packet = { test_packet with off = 4 } in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
(Some (test_packet, Cstruct.concat [ black; gray; white]))
(snd @@ Fragments.process cache below_max final_packet white)) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
None
(snd @@ Fragments.process cache Fragments.max_duration off_packet white)) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Lwt.return_unit
let reassembly_out_of_order () =
let more_frags = { test_packet with off = mf } in
let off_packet = { test_packet with off = 2 } in
let cache, res = Fragments.process empty_cache 0L off_packet gray in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) "reassembly of two segments works"
(Some (test_packet, Cstruct.append black gray))
(snd @@ Fragments.process cache 0L more_frags black)) ;
Lwt.return_unit
let reassembly_multiple_out_of_order packets final_payload () =
let _, res = List.fold_left (fun (cache, res) (off, payload) ->
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
let packet = { test_packet with off } in
Fragments.process cache 0L packet payload)
(empty_cache, None) packets
in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
(Some (test_packet, final_payload))
res) ;
Lwt.return_unit
let basic_overlaps () =
let more_frags = { test_packet with off = mf } in
let off_packet = { test_packet with off = 1 } in
let cache, res = Fragments.process empty_cache 0L off_packet black in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None
(snd @@ Fragments.process cache 0L more_frags white)) ;
Lwt.return_unit
let basic_other_ip_flow () =
let more_frags = { test_packet with off = mf } in
let cache, res = Fragments.process empty_cache 0L more_frags black in
let off_packet = { test_packet with off = 2 ; src = Ipaddr.V4.of_string_exn "127.0.0.2" } in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None
(snd @@ Fragments.process cache 0L off_packet white)) ;
let off_packet' = { test_packet with off = 2 ; proto = 25 } in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None
(snd @@ Fragments.process cache 0L off_packet' white)) ;
Lwt.return_unit
let max_fragment () =
let all_16 = [ white; gray; black; white;
white; gray; black; white;
white; gray; black; white;
white; gray; black ; gray ]
in
let (cache, res), off =
List.fold_left (fun ((cache, res), off) payload ->
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
let r = Fragments.process cache 0L { test_packet with off = off lor mf } payload in
(r, Cstruct.length payload / 8 + off))
((empty_cache, None), 0)
all_16
in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
(Some (test_packet, Cstruct.concat (all_16 @ [white ])))
(snd @@ Fragments.process cache 0L { test_packet with off } white)) ;
let cache, res = Fragments.process cache 0L { test_packet with off = off lor mf } white in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__
None
(snd @@ Fragments.process cache 0L { test_packet with off = off + 2 } black)) ;
Lwt.return_unit
let none_returned packets () =
let _, res = List.fold_left (fun (cache, res) (off, payload) ->
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
let packet = { test_packet with off } in
Fragments.process cache 0L packet payload)
(empty_cache, None) packets
in
Alcotest.(check (option (pair ipv4_packet cstruct)) __LOC__ None res) ;
Lwt.return_unit
let ins_all_positions x l =
let rec aux prev acc = function
| [] -> List.rev ((prev @ [x]) :: acc)
| hd::tl as l -> aux (prev @ [hd]) ((prev @ [x] @ l) :: acc) tl
in
aux [] [] l
let rec permutations = function
| [] -> []
| [x] -> [[x]]
| x::xs -> List.fold_left (fun acc p -> acc @ ins_all_positions x p ) []
(permutations xs)
let fragment_simple () =
let hdr =
{ Ipv4_packet.src = Ipaddr.V4.localhost ; dst = Ipaddr.V4.localhost ;
id = 0x42 ; off = 0 ; ttl = 10 ; proto = 10 ; options = Cstruct.empty }
in
let payload = Cstruct.create 1030 in
let fs = Fragments.fragment ~mtu:36 hdr payload in
(* 16 byte per packet -> 64 fragments (a 16 byte) + 1 (6 byte) *)
Alcotest.(check int __LOC__ 65 (List.length fs));
let second, last = List.hd fs, List.(hd (rev fs)) in
Alcotest.(check int __LOC__ 26 (Cstruct.length last));
match
Ipv4_packet.Unmarshal.of_cstruct second,
Ipv4_packet.Unmarshal.of_cstruct last
with
| Error e, _ -> Alcotest.fail ("failed to decode second fragment " ^ e)
| _, Error e -> Alcotest.fail ("failed to decode last fragment " ^ e)
| Ok (hdr, _payload), Ok (hdr', _payload') ->
Alcotest.(check int __LOC__ (0x2000 lor 2) hdr.Ipv4_packet.off);
Alcotest.(check int __LOC__ 0x42 hdr.Ipv4_packet.id);
Alcotest.(check int __LOC__ 130 hdr'.Ipv4_packet.off);
Alcotest.(check int __LOC__ 0x42 hdr'.Ipv4_packet.id);
let fs' = Fragments.fragment ~mtu:36 hdr (Cstruct.sub payload 0 1024) in
(* 16 byte per packet -> 64 fragments (a 16 byte) *)
Alcotest.(check int __LOC__ 64 (List.length fs'));
let second', last' = List.hd fs', List.(hd (rev fs')) in
Alcotest.(check int __LOC__ 36 (Cstruct.length last'));
match
Ipv4_packet.Unmarshal.of_cstruct second',
Ipv4_packet.Unmarshal.of_cstruct last'
with
| Error e, _ -> Alcotest.fail ("failed to decode second fragment' " ^ e)
| _, Error e -> Alcotest.fail ("failed to decode last fragment' " ^ e)
| Ok (hdr'', _payload''), Ok (hdr''', _payload''') ->
Alcotest.(check int __LOC__ (0x2000 lor 2) hdr''.Ipv4_packet.off);
Alcotest.(check int __LOC__ 0x42 hdr''.Ipv4_packet.id);
Alcotest.(check int __LOC__ 128 hdr'''.Ipv4_packet.off);
Alcotest.(check int __LOC__ 0x42 hdr'''.Ipv4_packet.id)
let suite = [
"unmarshal ip datagram with options", `Quick, test_unmarshal_with_options;
"unmarshal ip datagram without options", `Quick, test_unmarshal_without_options;
"unmarshal ip datagram with no payload & hlen > 5", `Quick, test_unmarshal_regression;
"size", `Quick, test_size ] @
List.mapi (fun i size ->
Printf.sprintf "basic fragment %d: payload %d" i size, `Quick, basic_fragments (Cstruct.create size))
[ 0 ; 1 ; 2 ; 10 ; 100 ; 1000 ; 5000 ; 10000 ] @ [
"basic reassembly", `Quick, basic_reassembly;
"basic reassembly timeout", `Quick, basic_reassembly_timeout;
"reassembly out of order", `Quick, reassembly_out_of_order ;
"other ip flow", `Quick, basic_other_ip_flow ;
"maximum amount of fragments", `Quick, max_fragment ] @
List.mapi (fun i (packets, final) ->
Printf.sprintf "reassembly multiple %d" i, `Quick,
reassembly_multiple_out_of_order packets final)
([
([ (mf, white); (2, black) ], Cstruct.concat [white;black]);
([ (mf, black); (2, white) ], Cstruct.concat [black;white]);
([ (2, black); (mf, white) ], Cstruct.concat [white;black]);
([ (2, white); (mf, black) ], Cstruct.concat [black;white]);
([ (mf, Cstruct.create 984); (123, black)], Cstruct.concat [Cstruct.create 984;black]);
([ (mf, Cstruct.create 984); (123 lor mf, black); (125, gray)],
Cstruct.concat [Cstruct.create 984;black;gray]);
([ (mf, Cstruct.create 1000); (125, (Cstruct.concat [black;black;black]))],
Cstruct.concat [Cstruct.create 1000;black;black;black]);
]@
List.map (fun x -> (x, Cstruct.concat [gray;white;black]))
(permutations [ (mf, gray); (2 lor mf, white); (4, black)]) @
List.map (fun x -> (x, Cstruct.concat [gray;white;black;Cstruct.create 10]))
(permutations [ (mf, gray); (2 lor mf, white); (4 lor mf, black); (6, Cstruct.create 10)]) @
List.map (fun x -> (x, Cstruct.concat [black;gray;white;black;gray]))
(permutations [ (mf, black); (2 lor mf, gray); (4 lor mf, white); (6 lor mf, black); (8, gray)])
) @
[ "nothing returned", `Quick, basic_overlaps ] @
List.mapi (fun i packets ->
Printf.sprintf "nothing returned %d" i, `Quick,
none_returned packets)
([
[ (mf, white); (1, black) ];
[ (mf, black); (3, white) ];
[ (mf, Cstruct.create 992); (124 lor mf, black);(126, gray)];
[ (mf, Cstruct.create 1024); (128, black)];
] @
permutations [ (mf, gray); (2 lor mf, white); (3, black)] @
permutations [ (mf, gray); (2 lor mf, white); (5, black)] @
permutations [ (mf, gray); (3 lor mf, white); (4, black)] @
permutations [ (mf, gray); (3 lor mf, white); (5, black)] @
permutations [ (mf, gray); (1 lor mf, white); (3, black)] @
permutations [ (mf, gray); (1 lor mf, white); (4, black)] @
permutations [ (mf, (Cstruct.append gray gray)); (3 lor mf, white)] @
permutations [ (mf, (Cstruct.append gray gray)); (2 lor mf, white)] @
permutations [ (mf, gray); (2 lor mf, white); (4 lor mf, black); (6 lor mf, gray)] @
permutations [ (mf, gray); (2 lor mf, white); (4 lor mf, black); (5, gray)] @
permutations [ (mf, gray); (4 lor mf, white); (4 lor mf, black); (6, gray)] @
permutations [ (mf, gray); (1 lor mf, white); (3 lor mf, black); (5, gray)] @
permutations [ (mf, gray); (2 lor mf, white); (4 lor mf, black); (7, gray)]
) @ [
"simple fragment", `Quick, (fun () -> Lwt.return (fragment_simple ()))
]