Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
2056: add vsock support for macOS r=asomers a=tzneal

Adds vsock support for macOS.  It's not possible to create a listening
vsock socket on a macOS host, but you can create one from inside a 
VM and connect back to the host.

Co-authored-by: Todd Neal <tnealt@amazon.com>
Co-authored-by: Alan Somers <asomers@gmail.com>
  • Loading branch information
3 people authored Aug 6, 2023
2 parents 779b23c + d517f07 commit 78a4d9e
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- Added `SOF_TIMESTAMPING_OPT_ID` and `SOF_TIMESTAMPING_OPT_TSONLY` to `nix::sys::socket::TimestampingFlag`.
([#2048](https://github.com/nix-rust/nix/pull/2048))
- Enabled socket timestamping options on Android. ([#2077](https://github.com/nix-rust/nix/pull/2077))
- Added vsock support for macOS ([#2056](https://github.com/nix-rust/nix/pull/2056))
- Added `SO_SETFIB` and `SO_USER_COOKIE` to `nix::sys::socket::sockopt` for FreeBSD.
([#2085](https://github.com/nix-rust/nix/pull/2085))

Expand Down
37 changes: 27 additions & 10 deletions src/sys/socket/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
))]
#[cfg(feature = "net")]
pub use self::datalink::LinkAddr;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
pub use self::vsock::VsockAddr;
use super::sa_family_t;
use crate::errno::Errno;
Expand Down Expand Up @@ -248,7 +248,7 @@ pub enum AddressFamily {
#[cfg_attr(docsrs, doc(cfg(all())))]
Nfc = libc::AF_NFC,
/// VMWare VSockets protocol for hypervisor-guest interaction.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
Vsock = libc::AF_VSOCK,
/// ARPANet IMP addresses
Expand Down Expand Up @@ -443,7 +443,7 @@ impl AddressFamily {
target_os = "openbsd"
))]
libc::AF_LINK => Some(AddressFamily::Link),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
libc::AF_VSOCK => Some(AddressFamily::Vsock),
_ => None,
}
Expand Down Expand Up @@ -1286,7 +1286,7 @@ pub union SockaddrStorage {
sin6: SockaddrIn6,
ss: libc::sockaddr_storage,
su: UnixAddr,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
#[cfg_attr(docsrs, doc(cfg(all())))]
vsock: VsockAddr,
}
Expand Down Expand Up @@ -1378,7 +1378,7 @@ impl SockaddrLike for SockaddrStorage {
libc::AF_SYSTEM => {
SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
libc::AF_VSOCK => {
VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
}
Expand Down Expand Up @@ -1554,7 +1554,7 @@ impl SockaddrStorage {
accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr,
AddressFamily::System, libc::sockaddr_ctl, sctl}

#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr,
AddressFamily::Vsock, libc::sockaddr_vm, vsock}
Expand Down Expand Up @@ -1604,7 +1604,7 @@ impl fmt::Display for SockaddrStorage {
#[cfg(feature = "ioctl")]
libc::AF_SYSTEM => self.sctl.fmt(f),
libc::AF_UNIX => self.su.fmt(f),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
libc::AF_VSOCK => self.vsock.fmt(f),
_ => "<Address family unspecified>".fmt(f),
}
Expand Down Expand Up @@ -1678,7 +1678,7 @@ impl Hash for SockaddrStorage {
#[cfg(feature = "ioctl")]
libc::AF_SYSTEM => self.sctl.hash(s),
libc::AF_UNIX => self.su.hash(s),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
libc::AF_VSOCK => self.vsock.hash(s),
_ => self.ss.hash(s),
}
Expand Down Expand Up @@ -1720,7 +1720,7 @@ impl PartialEq for SockaddrStorage {
#[cfg(feature = "ioctl")]
(libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl,
(libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
(libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock,
_ => false,
}
Expand Down Expand Up @@ -2268,7 +2268,7 @@ mod datalink {
}
}

#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub mod vsock {
use super::*;
Expand Down Expand Up @@ -2314,20 +2314,33 @@ pub mod vsock {
}

impl PartialEq for VsockAddr {
#[cfg(any(target_os = "android", target_os = "linux"))]
fn eq(&self, other: &Self) -> bool {
let (inner, other) = (self.0, other.0);
(inner.svm_family, inner.svm_cid, inner.svm_port)
== (other.svm_family, other.svm_cid, other.svm_port)
}
#[cfg(target_os = "macos")]
fn eq(&self, other: &Self) -> bool {
let (inner, other) = (self.0, other.0);
(inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len)
== (other.svm_family, other.svm_cid, other.svm_port, inner.svm_len)
}
}

impl Eq for VsockAddr {}

impl Hash for VsockAddr {
#[cfg(any(target_os = "android", target_os = "linux"))]
fn hash<H: Hasher>(&self, s: &mut H) {
let inner = self.0;
(inner.svm_family, inner.svm_cid, inner.svm_port).hash(s);
}
#[cfg(target_os = "macos")]
fn hash<H: Hasher>(&self, s: &mut H) {
let inner = self.0;
(inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len).hash(s);
}
}

/// VSOCK Address
Expand All @@ -2342,6 +2355,10 @@ pub mod vsock {
addr.svm_cid = cid;
addr.svm_port = port;

#[cfg(target_os = "macos")]
{
addr.svm_len = std::mem::size_of::<sockaddr_vm>() as u8;
}
VsockAddr(addr)
}

Expand Down
2 changes: 1 addition & 1 deletion src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub use crate::sys::socket::addr::netlink::NetlinkAddr;
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(feature = "ioctl")]
pub use crate::sys::socket::addr::sys_control::SysControlAddr;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
pub use crate::sys::socket::addr::vsock::VsockAddr;

#[cfg(all(feature = "uio", not(target_os = "redox")))]
Expand Down
43 changes: 43 additions & 0 deletions test/sys/test_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,49 @@ pub fn test_vsock() {
assert_eq!(addr3.as_ref().svm_port, addr1.port());
}

#[cfg(target_os = "macos")]
#[test]
pub fn test_vsock() {
use nix::sys::socket::SockaddrLike;
use nix::sys::socket::{AddressFamily, VsockAddr};
use std::mem;

let port: u32 = 3000;

// macOS doesn't have a VMADDR_CID_LOCAL, so test with host again
let addr_host = VsockAddr::new(libc::VMADDR_CID_HOST, port);
assert_eq!(addr_host.cid(), libc::VMADDR_CID_HOST);
assert_eq!(addr_host.port(), port);

let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY);
assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY);
assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY);

assert_ne!(addr_host, addr_any);
assert_ne!(calculate_hash(&addr_host), calculate_hash(&addr_any));

let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
assert_eq!(addr1, addr2);
assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));

let addr3 = unsafe {
VsockAddr::from_raw(
addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
)
}
.unwrap();
assert_eq!(
addr3.as_ref().svm_family,
AddressFamily::Vsock as libc::sa_family_t
);
let cid = addr3.as_ref().svm_cid;
let port = addr3.as_ref().svm_port;
assert_eq!(cid, addr1.cid());
assert_eq!(port, addr1.port());
}

// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
// of QEMU support is suspected.
#[cfg_attr(qemu, ignore)]
Expand Down

0 comments on commit 78a4d9e

Please sign in to comment.