open Common module B = Basic_backend.Make module V = Vnetif.Make(B) module E = Ethernet.Make(V) module Static_arp = Static_arp.Make(E) module Ip = Static_ipv4.Make(E)(Static_arp) module Udp = Udp.Make(Ip) type stack = { backend : B.t; netif : V.t; ethif : E.t; arp : Static_arp.t; ip : Ip.t; udp : Udp.t; } let get_stack ?(backend = B.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause ()) ()) ip = let open Lwt.Infix in let cidr = Ipaddr.V4.Prefix.make 24 ip in V.connect backend >>= fun netif -> E.connect netif >>= fun ethif -> Static_arp.connect ethif >>= fun arp -> Ip.connect ~cidr ethif arp >>= fun ip -> Udp.connect ip >>= fun udp -> Lwt.return { backend; netif; ethif; arp; ip; udp } let fails msg f args = match f args with | Ok _ -> Alcotest.fail msg | Error _ -> () let marshal_unmarshal () = let parse = Udp_packet.Unmarshal.of_cstruct in fails "unmarshal a 0-length packet" parse (Cstruct.create 0); fails "unmarshal a too-short packet" parse (Cstruct.create 2); let with_data = Cstruct.create 8 in Cstruct.memset with_data 0; Udp_wire.set_src_port with_data 2000; Udp_wire.set_dst_port with_data 21; Udp_wire.set_length with_data 20; let payload = Cstruct.of_string "abcdefgh1234" in let with_data = Cstruct.concat [with_data; payload] in match Udp_packet.Unmarshal.of_cstruct with_data with | Error s -> Alcotest.fail s | Ok (_header, data) -> Alcotest.(check cstruct) "unmarshalling gives expected data" payload data; Lwt.return_unit let write () = let open Lwt.Infix in let dst = Ipaddr.V4.of_string_exn "192.168.4.20" in get_stack dst >>= fun stack -> Static_arp.add_entry stack.arp dst (Macaddr.of_string_exn "00:16:3e:ab:cd:ef"); Udp.write ~src_port:1212 ~dst_port:21 ~dst stack.udp (Cstruct.of_string "MGET *") >|= Result.get_ok let unmarshal_regression () = let i = Cstruct.create 1016 in Cstruct.memset i 30; Cstruct.set_char i 4 '\x04'; Cstruct.set_char i 5 '\x00'; Alcotest.(check (result reject pass)) "correctly return error for bad packet" (Error "parse failed") (Udp_packet.Unmarshal.of_cstruct i); Lwt.return_unit let marshal_marshal () = let error_str = Alcotest.result Alcotest.reject Alcotest.string in let udp = {Udp_packet.src_port = 1; dst_port = 2} in let payload = Cstruct.create 100 in let buffer = Cstruct.create Udp_wire.sizeof_udp in let src = Ipaddr.V4.of_string_exn "127.0.0.1" in let dst = Ipaddr.V4.of_string_exn "127.0.0.1" in let pseudoheader = Ipv4_packet.Marshal.pseudoheader ~src ~dst ~proto:`UDP (Cstruct.length buffer + Cstruct.length payload) in Udp_packet.Marshal.into_cstruct ~pseudoheader ~payload udp (Cstruct.shift buffer 1) |> Alcotest.check error_str "Buffer too short" (Error "Not enough space for a UDP header"); Udp_packet.Marshal.into_cstruct ~pseudoheader ~payload udp buffer |> Alcotest.(check (result unit string)) "Buffer big enough for header" (Ok ()); Udp_packet.Unmarshal.of_cstruct (Cstruct.concat [buffer; payload]) |> Alcotest.(check (result (pair udp_packet cstruct) string)) "Save and reload" (Ok (udp, payload)); Lwt.return_unit let suite = [ "unmarshal regression", `Quick, unmarshal_regression; "marshal/marshal", `Quick, marshal_marshal; "marshal/unmarshal", `Quick, marshal_unmarshal; "write packets", `Quick, write; ]