From f932aee000ca6cc9ddc0cf09564d43f355ed8e2e Mon Sep 17 00:00:00 2001 From: David Scott Date: Fri, 4 Aug 2017 17:04:03 +0100 Subject: [PATCH 1/2] Windows uses SOCK_RAW for ICMP sockets The MSDN reference[1] says: > An example of the first type of socket is a socket for the ICMP protocol > (IP protocol type = 1) or the ICMPv6 protocol (IP procotol type = 58). [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms740548(v=vs.85).aspx Signed-off-by: David Scott --- src/stack-unix/icmpv4_socket.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/stack-unix/icmpv4_socket.ml b/src/stack-unix/icmpv4_socket.ml index cdc1bb942..04b792de4 100644 --- a/src/stack-unix/icmpv4_socket.ml +++ b/src/stack-unix/icmpv4_socket.ml @@ -9,6 +9,13 @@ type t = unit type error = [ `Ip of string ] let pp_error ppf (`Ip s) = Fmt.string ppf s +let is_win32 = Sys.os_type = "Win32" + +let sock_icmp = + (* Windows uses SOCK_RAW protocol 1 for ICMP + Unix uses SOCK_DGRAM protocol 1 for ICMP *) + if is_win32 then Lwt_unix.SOCK_RAW else Lwt_unix.SOCK_DGRAM + let ipproto_icmp = 1 (* according to BSD /etc/protocols *) let port = 0 (* port isn't meaningful in this context *) @@ -29,7 +36,7 @@ let write _t ~dst buf = let flags = [] in let ipproto_icmp = 1 in (* according to BSD /etc/protocols *) let port = 0 in (* port isn't meaningful in this context *) - let fd = socket PF_INET SOCK_DGRAM ipproto_icmp in + let fd = socket PF_INET sock_icmp ipproto_icmp in let in_addr = Unix.inet_addr_of_string (Ipaddr.V4.to_string dst) in let sockaddr = ADDR_INET (in_addr, port) in Lwt.catch (fun () -> @@ -61,7 +68,7 @@ let input t ~src ~dst:_ buf = | _, _ -> Lwt.return_unit let listen _t addr fn = - let fd = Lwt_unix.(socket PF_INET SOCK_DGRAM) ipproto_icmp in + let fd = Lwt_unix.socket PF_INET sock_icmp ipproto_icmp in let sa = Lwt_unix.ADDR_INET (Unix.inet_addr_of_string (Ipaddr.V4.to_string addr), port) in Lwt_unix.bind fd sa >>= fun () -> Log.debug (fun f -> f "Bound ICMP file descriptor to %a" pp_sockaddr sa); From 6340082fd752a1989bf4c996e02002ddc5c39ba2 Mon Sep 17 00:00:00 2001 From: David Scott Date: Fri, 4 Aug 2017 17:22:49 +0100 Subject: [PATCH 2/2] Work around missing Lwt_unix win32 support Lwt_unix on Win32 doesn't support Lwt_bytes.{recvfrom,sendto}. This patch works around this by copying to and from a temporary `Bytes.t`. Signed-off-by: David Scott --- src/stack-unix/icmpv4_socket.ml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/stack-unix/icmpv4_socket.ml b/src/stack-unix/icmpv4_socket.ml index 04b792de4..62664cc7d 100644 --- a/src/stack-unix/icmpv4_socket.ml +++ b/src/stack-unix/icmpv4_socket.ml @@ -31,6 +31,24 @@ let pp_sockaddr fmt sa = let src = Logs.Src.create "icmpv4_socket" ~doc:"Mirage ICMPv4 (Sockets Edition)" module Log = (val Logs.src_log src : Logs.LOG) +let sendto' fd buf flags dst = + if is_win32 then begin + (* Lwt on Win32 doesn't support Lwt_bytes.sendto *) + let bytes = Bytes.make (Cstruct.len buf) '\000' in + Cstruct.blit_to_bytes buf 0 bytes 0 (Cstruct.len buf); + Lwt_unix.sendto fd bytes 0 (Bytes.length bytes) flags dst + end else Lwt_cstruct.sendto fd buf flags dst + +let recvfrom' fd buf flags = + if is_win32 then begin + (* Lwt on Win32 doesn't support Lwt_bytes.recvfrom *) + let bytes = Bytes.make (Cstruct.len buf) '\000' in + Lwt_unix.recvfrom fd bytes 0 (Bytes.length bytes) flags + >>= fun (n, sockaddr) -> + Cstruct.blit_from_bytes bytes 0 buf 0 n; + Lwt.return (n, sockaddr) + end else Lwt_cstruct.recvfrom fd buf flags + let write _t ~dst buf = let open Lwt_unix in let flags = [] in @@ -40,7 +58,7 @@ let write _t ~dst buf = let in_addr = Unix.inet_addr_of_string (Ipaddr.V4.to_string dst) in let sockaddr = ADDR_INET (in_addr, port) in Lwt.catch (fun () -> - Lwt_cstruct.sendto fd buf flags sockaddr >>= fun sent -> + sendto' fd buf flags sockaddr >>= fun sent -> if (sent <> (Cstruct.len buf)) then Log.debug (fun f -> f "short write: %d received vs %d expected" sent (Cstruct.len buf)); Lwt_unix.close fd |> Lwt_result.ok @@ -74,7 +92,7 @@ let listen _t addr fn = Log.debug (fun f -> f "Bound ICMP file descriptor to %a" pp_sockaddr sa); let aux fn = let receive_buffer = Cstruct.create 4096 in - Lwt_cstruct.recvfrom fd receive_buffer [] >>= fun (len, _sockaddr) -> + recvfrom' fd receive_buffer [] >>= fun (len, _sockaddr) -> (* trim the buffer to the amount of data actually received *) let receive_buffer = Cstruct.set_len receive_buffer len in fn receive_buffer