trusty: libtrusty-rs: Add vsock support
Bug: 298705967
Test: Manual run android.hardware.security.keymint-service.rust.trusty -d VSOCK:2048:1
Change-Id: I2aa5660c3c86fed53420b874de3ef6db9dc22f96
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
index 4fc162b..e289005 100644
--- a/trusty/libtrusty-rs/Android.bp
+++ b/trusty/libtrusty-rs/Android.bp
@@ -21,9 +21,10 @@
crate_name: "trusty",
vendor_available: true,
srcs: [
- "src/lib.rs"
+ "src/lib.rs",
],
rustlibs: [
+ "liblog_rust",
"libnix",
"liblibc",
],
@@ -36,5 +37,5 @@
rustlibs: [
"libtrusty-rs",
"liblibc",
- ]
+ ],
}
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
index 22b894a..9237c8b 100644
--- a/trusty/libtrusty-rs/src/lib.rs
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -61,12 +61,18 @@
//! ```
use crate::sys::tipc_connect;
+use log::{trace, warn};
+use nix::sys::socket;
+use std::convert::From;
use std::ffi::CString;
use std::fs::File;
+use std::io;
use std::io::prelude::*;
use std::io::{ErrorKind, Result};
use std::os::unix::prelude::AsRawFd;
use std::path::Path;
+use std::thread;
+use std::time;
mod sys;
@@ -98,7 +104,89 @@
/// bytes. This is handled with a panic because the service names are all
/// hard-coded constants, and so such an error should always be indicative of a
/// bug in the calling code.
- pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> {
+ pub fn connect(device: &str, service: &str) -> Result<Self> {
+ if let Some(cid_port_str) = device.strip_prefix("VSOCK:") {
+ Self::connect_vsock(cid_port_str, service)
+ } else {
+ Self::connect_tipc(device, service)
+ }
+ }
+
+ fn connect_vsock(type_cid_port_str: &str, service: &str) -> Result<Self> {
+ let cid_port_str;
+ let socket_type;
+ if let Some(stream_cid_port_str) = type_cid_port_str.strip_prefix("STREAM:") {
+ socket_type = socket::SockType::Stream;
+ cid_port_str = stream_cid_port_str;
+ } else if let Some(seqpacket_cid_port_str) = type_cid_port_str.strip_prefix("SEQPACKET:") {
+ socket_type = socket::SockType::SeqPacket;
+ cid_port_str = seqpacket_cid_port_str;
+ } else {
+ /*
+ * Default to SOCK_STREAM if neither type is specified.
+ *
+ * TODO: use SOCK_SEQPACKET by default instead of SOCK_STREAM when SOCK_SEQPACKET is fully
+ * supported since it matches tipc better. At the moment SOCK_SEQPACKET is not supported by
+ * crosvm. It is also significantly slower since the Linux kernel implementation (as of
+ * v6.7-rc1) sends credit update packets every time it receives a data packet while the
+ * SOCK_STREAM version skips these unless the remaining buffer space is "low".
+ */
+ socket_type = socket::SockType::Stream;
+ cid_port_str = type_cid_port_str;
+ }
+
+ let [cid, port]: [u32; 2] = cid_port_str
+ .split(':')
+ .map(|v| v.parse::<u32>().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)))
+ .collect::<Result<Vec<u32>>>()?
+ .try_into()
+ .map_err(|e| {
+ io::Error::new(io::ErrorKind::InvalidInput, format!("Wrong number of args: {e:?}"))
+ })?;
+
+ trace!("got cid, port: {cid}, {port}");
+ let s = socket::socket(
+ socket::AddressFamily::Vsock,
+ socket_type,
+ socket::SockFlag::SOCK_CLOEXEC,
+ None,
+ )?;
+ trace!("got socket");
+ let sa = socket::VsockAddr::new(cid, port);
+ trace!("got sa");
+
+ //let connect_timeout = libc::timeval {tv_sec: 60, tv_usec: 0};
+ // TODO: Set AF_VSOCK/SO_VM_SOCKETS_CONNECT_TIMEOUT sockopt.
+
+ let mut retry = 10;
+ loop {
+ let res = socket::connect(s.as_raw_fd(), &sa);
+ if res.is_ok() || retry <= 0 {
+ res?;
+ break;
+ }
+ warn!("vsock:{cid}:{port} connect failed {res:?}, {retry} retries remaining");
+ retry -= 1;
+ thread::sleep(time::Duration::from_secs(5));
+ }
+ trace!("connected");
+ // TODO: Current vsock tipc bridge in trusty expects a port name in the
+ // first packet. We need to replace this with a protocol that also does DICE
+ // based authentication.
+ // `s` is a valid file descriptor because it came from socket::socket.
+ let mut channel = Self(File::from(s));
+ channel.send(service.as_bytes())?;
+ trace!("sent tipc port name");
+
+ // Work around lack of seq packet support. Read a status byte to prevent
+ // the caller from sending more data until srv_name has been read.
+ let mut status = [0; 1];
+ channel.recv_no_alloc(&mut status)?;
+ trace!("got status byte: {status:?}");
+ Ok(channel)
+ }
+
+ fn connect_tipc(device: impl AsRef<Path>, service: &str) -> Result<Self> {
let file = File::options().read(true).write(true).open(device)?;
let srv_name = CString::new(service).expect("Service name contained null bytes");
@@ -108,7 +196,7 @@
tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
}
- Ok(TipcChannel(file))
+ Ok(Self(file))
}
/// Sends a message to the connected service.