vmsocket/
af_hyperv.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! AF_HYPERV support.
5
6// UNSAFETY: Creating and managing a raw socket.
7#![expect(unsafe_code)]
8
9use crate::VmListener;
10use crate::VmSocket;
11use crate::VmStream;
12use guid::Guid;
13use mesh::payload::os_resource;
14use socket2::SockAddr;
15use socket2::Socket;
16use socket2::Type;
17use std::io;
18use std::os::raw::c_int;
19use std::os::windows::prelude::*;
20use std::time::Duration;
21use windows_sys::Win32::Networking::WinSock::AF_HYPERV;
22use windows_sys::Win32::Networking::WinSock::SOCKET_ERROR;
23use windows_sys::Win32::Networking::WinSock::WSAGetLastError;
24use windows_sys::Win32::Networking::WinSock::setsockopt;
25use windows_sys::Win32::System::Hypervisor::HV_GUID_PARENT;
26use windows_sys::Win32::System::Hypervisor::HV_GUID_ZERO;
27use windows_sys::Win32::System::Hypervisor::HV_PROTOCOL_RAW;
28use windows_sys::Win32::System::Hypervisor::HVSOCKET_CONNECT_TIMEOUT;
29use windows_sys::Win32::System::Hypervisor::SOCKADDR_HV;
30
31/// Creates an AF_HYPERV service ID from an AF_VSOCK port.
32fn service_id_from_vsock_port(port: u32) -> Guid {
33    Guid {
34        data1: port,
35        .."00000000-facb-11e6-bd58-64006a7986d3".parse().unwrap()
36    }
37}
38
39#[derive(Debug)]
40pub struct Address {
41    pub(crate) vm_id: Guid,
42    pub(crate) service_id: Guid,
43}
44
45impl Address {
46    pub fn new(vm_id: Guid, service_id: Guid) -> Self {
47        Self { vm_id, service_id }
48    }
49
50    pub fn vsock(vm_id: Guid, port: u32) -> Self {
51        Self::new(vm_id, service_id_from_vsock_port(port))
52    }
53
54    pub fn hyperv_any(service_id: Guid) -> Self {
55        Self::new(HV_GUID_ZERO.into(), service_id)
56    }
57
58    pub fn hyperv_host(service_id: Guid) -> Self {
59        Self::new(HV_GUID_PARENT.into(), service_id)
60    }
61
62    pub fn vsock_any(port: u32) -> Self {
63        Self::hyperv_any(service_id_from_vsock_port(port))
64    }
65
66    pub fn vsock_host(port: u32) -> Self {
67        Self::hyperv_host(service_id_from_vsock_port(port))
68    }
69
70    pub fn into_sock_addr(self) -> SockAddr {
71        let address = SOCKADDR_HV {
72            Family: AF_HYPERV,
73            Reserved: 0,
74            VmId: self.vm_id.into(),
75            ServiceId: self.service_id.into(),
76        };
77
78        // SAFETY: initializing storage as documented.
79        let (_, address) = unsafe {
80            SockAddr::try_init(|storage, len| {
81                assert!(*len as usize >= size_of_val(&address));
82                let storage: &mut SOCKADDR_HV = &mut *storage.cast();
83                *storage = address;
84                *len = size_of_val(&address) as i32;
85                Ok(())
86            })
87            .unwrap()
88        };
89
90        address
91    }
92
93    pub fn try_from_sock_addr(addr: &SockAddr) -> Option<Self> {
94        if (addr.len() as usize) < size_of::<SOCKADDR_HV>() {
95            return None;
96        }
97        // SAFETY: buffer is large enough.
98        let addr = unsafe { &*addr.as_ptr().cast::<SOCKADDR_HV>() };
99        if addr.Family != AF_HYPERV {
100            return None;
101        }
102        Some(Self::new(addr.VmId.into(), addr.ServiceId.into()))
103    }
104}
105
106impl VmSocket {
107    pub(crate) fn new_inner() -> io::Result<Self> {
108        Ok(Self(Socket::new(
109            (AF_HYPERV as c_int).into(),
110            Type::STREAM,
111            Some((HV_PROTOCOL_RAW as c_int).into()),
112        )?))
113    }
114
115    /// Sets the connection timeout for this socket.
116    pub fn set_connect_timeout(&self, duration: Duration) -> io::Result<()> {
117        let timeout = duration.as_millis().min(u32::MAX as u128) as u32;
118        // SAFETY: calling as documented
119        unsafe {
120            if setsockopt(
121                self.as_socket().as_raw_socket() as _,
122                HV_PROTOCOL_RAW as _,
123                HVSOCKET_CONNECT_TIMEOUT as i32,
124                std::ptr::from_ref::<u32>(&timeout) as *mut u8,
125                size_of_val(&timeout) as _,
126            ) == SOCKET_ERROR
127            {
128                return Err(io::Error::from_raw_os_error(WSAGetLastError()));
129            }
130            Ok(())
131        }
132    }
133
134    /// Sets whether this socket targets a VM's high VTL.
135    pub fn set_high_vtl(&self, high_vtl: bool) -> io::Result<()> {
136        const HVSOCKET_HIGH_VTL: i32 = 8;
137        let high_vtl: u32 = high_vtl.into();
138        // SAFETY: calling as documented
139        unsafe {
140            if setsockopt(
141                self.as_socket().as_raw_socket() as _,
142                HV_PROTOCOL_RAW as _,
143                HVSOCKET_HIGH_VTL,
144                std::ptr::from_ref::<u32>(&high_vtl) as *mut u8,
145                size_of_val(&high_vtl) as _,
146            ) == SOCKET_ERROR
147            {
148                return Err(io::Error::from_raw_os_error(WSAGetLastError()));
149            }
150        }
151        Ok(())
152    }
153}
154
155impl From<OwnedSocket> for VmSocket {
156    fn from(socket: OwnedSocket) -> Self {
157        Self(socket.into())
158    }
159}
160
161impl From<VmSocket> for OwnedSocket {
162    fn from(socket: VmSocket) -> Self {
163        socket.0.into()
164    }
165}
166
167impl AsSocket for VmSocket {
168    fn as_socket(&self) -> BorrowedSocket<'_> {
169        self.0.as_socket()
170    }
171}
172
173impl From<OwnedSocket> for VmListener {
174    fn from(socket: OwnedSocket) -> Self {
175        Self(socket.into())
176    }
177}
178
179impl From<VmListener> for OwnedSocket {
180    fn from(socket: VmListener) -> Self {
181        socket.0.into()
182    }
183}
184
185impl AsSocket for VmListener {
186    fn as_socket(&self) -> BorrowedSocket<'_> {
187        self.0.as_socket()
188    }
189}
190
191impl From<OwnedSocket> for VmStream {
192    fn from(socket: OwnedSocket) -> Self {
193        Self(socket.into())
194    }
195}
196
197impl From<VmStream> for OwnedSocket {
198    fn from(socket: VmStream) -> Self {
199        socket.0.into()
200    }
201}
202
203impl AsSocket for VmStream {
204    fn as_socket(&self) -> BorrowedSocket<'_> {
205        self.0.as_socket()
206    }
207}
208
209os_resource!(VmSocket, OwnedSocket);
210os_resource!(VmStream, OwnedSocket);
211os_resource!(VmListener, OwnedSocket);