This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
221
unikernel/duniverse/mirage-tcpip/test/test_ipv6.ml
Normal file
221
unikernel/duniverse/mirage-tcpip/test/test_ipv6.ml
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
open Common
|
||||
module B = Vnetif_backends.Basic
|
||||
module V = Vnetif.Make(B)
|
||||
module E = Ethernet.Make(V)
|
||||
|
||||
module Ipv6 = Ipv6.Make(V)(E)
|
||||
module Udp = Udp.Make(Ipv6)
|
||||
open Lwt.Infix
|
||||
|
||||
let ip =
|
||||
let module M = struct
|
||||
type t = Ipaddr.V6.t
|
||||
let pp = Ipaddr.V6.pp
|
||||
let equal p q = (Ipaddr.V6.compare p q) = 0
|
||||
end in
|
||||
(module M : Alcotest.TESTABLE with type t = M.t)
|
||||
|
||||
type stack = {
|
||||
backend : B.t;
|
||||
netif : V.t;
|
||||
ethif : E.t;
|
||||
ip : Ipv6.t;
|
||||
udp : Udp.t
|
||||
}
|
||||
|
||||
let get_stack backend address =
|
||||
let cidr = Ipaddr.V6.Prefix.make 64 address in
|
||||
V.connect backend >>= fun netif ->
|
||||
E.connect netif >>= fun ethif ->
|
||||
Ipv6.connect ~cidr netif ethif >>= fun ip ->
|
||||
Udp.connect ip >>= fun udp ->
|
||||
Lwt.return { backend; netif; ethif; ip; udp }
|
||||
|
||||
let noop = fun ~src:_ ~dst:_ _ -> Lwt.return_unit
|
||||
|
||||
let listen ?(tcp = noop) ?(udp = noop) ?(default = noop) stack =
|
||||
V.listen stack.netif ~header_size:Ethernet.Packet.sizeof_ethernet
|
||||
( E.input stack.ethif
|
||||
~arpv4:(fun _ -> Lwt.return_unit)
|
||||
~ipv4:(fun _ -> Lwt.return_unit)
|
||||
~ipv6:(
|
||||
Ipv6.input stack.ip
|
||||
~tcp:tcp
|
||||
~udp:udp
|
||||
~default:(fun ~proto:_ -> default))) >>= fun _ -> Lwt.return_unit
|
||||
|
||||
let udp_message = Cstruct.of_string "hello on UDP over IPv6"
|
||||
|
||||
let check_for_one_udp_packet on_received_one ~src ~dst buf =
|
||||
(match Udp_packet.Unmarshal.of_cstruct buf with
|
||||
| Ok (_, payload) ->
|
||||
Alcotest.(check ip) "sender address" (Ipaddr.V6.of_string_exn "fc00::23") src;
|
||||
Alcotest.(check ip) "receiver address" (Ipaddr.V6.of_string_exn "fc00::45") dst;
|
||||
Alcotest.(check cstruct) "payload is correct" udp_message payload
|
||||
| Error m -> Alcotest.fail m);
|
||||
(try Lwt.wakeup_later on_received_one () with _ -> () (* the first succeeds, the rest raise *));
|
||||
Lwt.return_unit
|
||||
|
||||
let send_forever sender receiver_address udp_message =
|
||||
let rec loop () =
|
||||
Udp.write sender.udp ~dst:receiver_address ~dst_port:1234 udp_message
|
||||
>|= Result.get_ok >>= fun () ->
|
||||
Mirage_sleep.ns (Duration.of_ms 50) >>= fun () ->
|
||||
loop () in
|
||||
loop ()
|
||||
|
||||
let pass_udp_traffic () =
|
||||
let sender_address = Ipaddr.V6.of_string_exn "fc00::23" in
|
||||
let receiver_address = Ipaddr.V6.of_string_exn "fc00::45" in
|
||||
let backend = B.create () in
|
||||
get_stack backend sender_address >>= fun sender ->
|
||||
get_stack backend receiver_address >>= fun receiver ->
|
||||
let received_one, on_received_one = Lwt.task () in
|
||||
Lwt.pick [
|
||||
listen receiver ~udp:(check_for_one_udp_packet on_received_one);
|
||||
listen sender;
|
||||
send_forever sender receiver_address udp_message;
|
||||
received_one; (* stop on the first packet *)
|
||||
Mirage_sleep.ns (Duration.of_ms 3000) >>= fun () ->
|
||||
Alcotest.fail "UDP packet should have been received";
|
||||
]
|
||||
|
||||
let create_ethernet backend =
|
||||
V.connect backend >>= fun netif ->
|
||||
E.connect netif >|= fun ethif ->
|
||||
(fun ipv6 ->
|
||||
V.listen netif ~header_size:Ethernet.Packet.sizeof_ethernet
|
||||
(E.input ethif
|
||||
~arpv4:(fun _ -> Lwt.return_unit)
|
||||
~ipv4:(fun _ -> Lwt.return_unit)
|
||||
~ipv6) >|= fun _ -> ()),
|
||||
(fun dst ?size f -> E.write ethif dst `IPv6 ?size f),
|
||||
E.mac ethif
|
||||
|
||||
let solicited_node_prefix =
|
||||
Ipaddr.V6.(Prefix.make 104 (of_int16 (0xff02, 0, 0, 0, 0, 1, 0xff00, 0)))
|
||||
|
||||
let dad_na_is_sent () =
|
||||
let address = Ipaddr.V6.of_string_exn "fc00::23" in
|
||||
let backend = B.create () in
|
||||
get_stack backend address >>= fun stack ->
|
||||
create_ethernet backend >>= fun (listen_raw, write_raw, _) ->
|
||||
let received_one, on_received_one = Lwt.task () in
|
||||
let nd_size = Ipv6_wire.sizeof_ipv6 + Ipv6_wire.Ns.sizeof_ns in
|
||||
let nd buf =
|
||||
Ipv6_wire.set_version_flow buf 0x60000000l; (* IPv6 *)
|
||||
Ipv6_wire.set_len buf Ipv6_wire.Ns.sizeof_ns;
|
||||
Ipaddr_cstruct.V6.write_cstruct_exn Ipaddr.V6.unspecified (Cstruct.shift buf 8);
|
||||
Ipaddr_cstruct.V6.write_cstruct_exn (Ipaddr.V6.Prefix.network_address solicited_node_prefix address) (Cstruct.shift buf 24);
|
||||
Ipv6_wire.set_hlim buf 255;
|
||||
Ipv6_wire.set_nhdr buf (Ipv6_wire.protocol_to_int `ICMP);
|
||||
let hdr, icmpbuf = Cstruct.split buf Ipv6_wire.sizeof_ipv6 in
|
||||
Ipv6_wire.set_ty icmpbuf 135; (* NS *)
|
||||
Ipv6_wire.set_code icmpbuf 0;
|
||||
Ipv6_wire.Ns.set_reserved icmpbuf 0l;
|
||||
Ipaddr_cstruct.V6.write_cstruct_exn address (Cstruct.shift icmpbuf 8);
|
||||
Ipv6_wire.Icmpv6.set_checksum icmpbuf 0;
|
||||
Ipv6_wire.Icmpv6.set_checksum icmpbuf @@ Ndpv6.checksum hdr [icmpbuf];
|
||||
nd_size
|
||||
and is_na buf =
|
||||
let icmpbuf = Cstruct.shift buf Ipv6_wire.sizeof_ipv6 in
|
||||
Ipv6_wire.get_version_flow buf = 0x60000000l && (* IPv6 *)
|
||||
Ipaddr.V6.compare
|
||||
(Ipaddr_cstruct.V6.of_cstruct_exn (Cstruct.shift buf 8))
|
||||
address = 0 &&
|
||||
Ipaddr.V6.compare
|
||||
(Ipaddr_cstruct.V6.of_cstruct_exn (Cstruct.shift buf 24))
|
||||
Ipaddr.V6.link_nodes = 0 &&
|
||||
Ipv6_wire.get_hlim buf = 255 &&
|
||||
Ipv6_wire.get_nhdr buf = Ipv6_wire.protocol_to_int `ICMP &&
|
||||
Ipv6_wire.get_ty icmpbuf = 136 &&
|
||||
Ipv6_wire.get_code icmpbuf = 0 &&
|
||||
Ipaddr.V6.compare
|
||||
(Ipaddr_cstruct.V6.of_cstruct_exn (Cstruct.shift icmpbuf 8))
|
||||
address = 0
|
||||
in
|
||||
Lwt.pick [
|
||||
listen stack;
|
||||
listen_raw (fun buf ->
|
||||
if is_na buf then
|
||||
Lwt.wakeup_later on_received_one ();
|
||||
Lwt.return_unit);
|
||||
(write_raw (E.mac stack.ethif) ~size:nd_size nd >|= fun _ -> ());
|
||||
received_one;
|
||||
(Mirage_sleep.ns (Duration.of_ms 1000) >>= fun () ->
|
||||
Alcotest.fail "NA packet should have been received")
|
||||
]
|
||||
|
||||
let multicast_mac =
|
||||
let pbuf = Cstruct.create 6 in
|
||||
Cstruct.BE.set_uint16 pbuf 0 0x3333;
|
||||
fun ip ->
|
||||
let _, _, _, n = Ipaddr.V6.to_int32 ip in
|
||||
Cstruct.BE.set_uint32 pbuf 2 n;
|
||||
Macaddr_cstruct.of_cstruct_exn pbuf
|
||||
|
||||
let dad_na_is_received () =
|
||||
let address = Ipaddr.V6.of_string_exn "fc00::23" in
|
||||
let backend = B.create () in
|
||||
create_ethernet backend >>= fun (listen_raw, write_raw, mac) ->
|
||||
let na_size = Ipv6_wire.sizeof_ipv6 + Ipv6_wire.Na.sizeof_na + Ipv6_wire.Llopt.sizeof_llopt in
|
||||
let is_ns buf =
|
||||
let icmpbuf = Cstruct.shift buf Ipv6_wire.sizeof_ipv6 in
|
||||
if
|
||||
Ipv6_wire.get_version_flow buf = 0x60000000l && (* IPv6 *)
|
||||
Ipaddr.V6.compare
|
||||
(Ipaddr_cstruct.V6.of_cstruct_exn (Cstruct.shift buf 8))
|
||||
Ipaddr.V6.unspecified = 0 &&
|
||||
Ipaddr.V6.Prefix.mem
|
||||
(Ipaddr_cstruct.V6.of_cstruct_exn (Cstruct.shift buf 24))
|
||||
solicited_node_prefix &&
|
||||
Ipv6_wire.get_hlim buf = 255 &&
|
||||
Ipv6_wire.get_nhdr buf = Ipv6_wire.protocol_to_int `ICMP &&
|
||||
Ipv6_wire.get_ty icmpbuf = 135 &&
|
||||
Ipv6_wire.get_code icmpbuf = 0
|
||||
then
|
||||
Some (Ipaddr_cstruct.V6.of_cstruct_exn (Cstruct.shift icmpbuf 8))
|
||||
else
|
||||
None
|
||||
in
|
||||
let na addr buf =
|
||||
Ipv6_wire.set_version_flow buf 0x60000000l; (* IPv6 *)
|
||||
Ipv6_wire.set_len buf (Ipv6_wire.Na.sizeof_na + Ipv6_wire.Llopt.sizeof_llopt);
|
||||
Ipaddr_cstruct.V6.write_cstruct_exn addr (Cstruct.shift buf 8);
|
||||
Ipaddr_cstruct.V6.write_cstruct_exn Ipaddr.V6.link_nodes (Cstruct.shift buf 24);
|
||||
Ipv6_wire.set_hlim buf 255;
|
||||
Ipv6_wire.set_nhdr buf (Ipv6_wire.protocol_to_int `ICMP);
|
||||
let hdr, icmpbuf = Cstruct.split buf Ipv6_wire.sizeof_ipv6 in
|
||||
Ipv6_wire.set_ty icmpbuf 136; (* NA *)
|
||||
Ipv6_wire.set_code icmpbuf 0;
|
||||
Ipv6_wire.Na.set_reserved icmpbuf 0x20000000l;
|
||||
Ipaddr_cstruct.V6.write_cstruct_exn addr (Cstruct.shift icmpbuf 8);
|
||||
let optbuf = Cstruct.shift icmpbuf Ipv6_wire.Na.sizeof_na in
|
||||
Ipv6_wire.set_ty optbuf 2;
|
||||
Ipv6_wire.Llopt.set_len optbuf 1;
|
||||
Macaddr_cstruct.write_cstruct_exn mac (Cstruct.shift optbuf 2);
|
||||
Ipv6_wire.Icmpv6.set_checksum icmpbuf 0;
|
||||
Ipv6_wire.Icmpv6.set_checksum icmpbuf @@ Ndpv6.checksum hdr [icmpbuf];
|
||||
na_size
|
||||
in
|
||||
Lwt.pick [
|
||||
(listen_raw (fun buf ->
|
||||
match is_ns buf with
|
||||
| None -> Lwt.return_unit
|
||||
| Some addr ->
|
||||
let dst = multicast_mac Ipaddr.V6.link_nodes in
|
||||
write_raw dst ~size:na_size (na addr) >|= fun _ -> ()));
|
||||
(Lwt.catch
|
||||
(fun () -> get_stack backend address >|= fun _ -> Error ())
|
||||
(fun _ -> Lwt.return (Ok ())) >|= function
|
||||
| Ok () -> ()
|
||||
| Error () -> Alcotest.fail "Expected stack initialization failure");
|
||||
(Mirage_sleep.ns (Duration.of_ms 5000) >>= fun () ->
|
||||
Alcotest.fail "stack initialization should have failed")
|
||||
]
|
||||
|
||||
let suite = [
|
||||
"Send a UDP packet from one IPV6 stack and check it is received by another", `Quick, pass_udp_traffic;
|
||||
"NA is sent when a ND is received", `Quick, dad_na_is_sent;
|
||||
"NA is received, stack fails to initialise", `Quick, dad_na_is_received;
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue