libchromeos-rs: Implement VsockCid (from Sirenia).

This provides a idomatic rust implementation of CIDs for Vsock and
refactors the usage of CIDs in related code.

BUG=b:162502718,b:144915425
TEST=cargo test for libchromeos, 9s, chunnel, andsirenia

Change-Id: I2e2eab2fd5027bded8233e03131391d0069dd576
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2338984
Tested-by: Allen Webb <allenwebb@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
diff --git a/libchromeos-rs/src/vsock.rs b/libchromeos-rs/src/vsock.rs
index b4fa589..34b9943 100644
--- a/libchromeos-rs/src/vsock.rs
+++ b/libchromeos-rs/src/vsock.rs
@@ -6,20 +6,24 @@
 use std::fmt;
 use std::io;
 use std::mem::{self, size_of};
+use std::num::ParseIntError;
 use std::os::raw::{c_int, c_uchar, c_uint, c_ushort};
 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
 use std::result;
 use std::str::FromStr;
 
-use libc::{self, c_void, sa_family_t, size_t, sockaddr, socklen_t, F_GETFL, F_SETFL, O_NONBLOCK};
+use libc::{
+    self, c_void, sa_family_t, size_t, sockaddr, socklen_t, F_GETFL, F_SETFL, O_NONBLOCK,
+    VMADDR_CID_ANY, VMADDR_CID_HOST, VMADDR_CID_HYPERVISOR,
+};
 
 // The domain for vsock sockets.
 const AF_VSOCK: sa_family_t = 40;
 
-// Vsock equivalent of INADDR_ANY.  Indicates the context id of the current endpoint.
-pub const VMADDR_CID_ANY: c_uint = c_uint::max_value();
+// Vsock loopback address.
+const VMADDR_CID_LOCAL: c_uint = 1;
 
-// Vsock equivalent of binding on port 0. Binds to a random port.
+/// Vsock equivalent of binding on port 0. Binds to a random port.
 pub const VMADDR_PORT_ANY: c_uint = c_uint::max_value();
 
 // The number of bytes of padding to be added to the sockaddr_vm struct.  Taken directly
@@ -47,10 +51,70 @@
     }
 }
 
+/// The vsock equivalent of an IP address.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum VsockCid {
+    /// Vsock equivalent of INADDR_ANY. Indicates the context id of the current endpoint.
+    Any,
+    /// An address that refers to the bare-metal machine that serves as the hypervisor.
+    Hypervisor,
+    /// The loopback address.
+    Local,
+    /// The parent machine. It may not be the hypervisor for nested VMs.
+    Host,
+    /// An assigned CID that serves as the address for VSOCK.
+    Cid(c_uint),
+}
+
+impl fmt::Display for VsockCid {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match &self {
+            VsockCid::Any => write!(fmt, "Any"),
+            VsockCid::Hypervisor => write!(fmt, "Hypervisor"),
+            VsockCid::Local => write!(fmt, "Local"),
+            VsockCid::Host => write!(fmt, "Host"),
+            VsockCid::Cid(c) => write!(fmt, "'{}'", c),
+        }
+    }
+}
+
+impl From<c_uint> for VsockCid {
+    fn from(c: c_uint) -> Self {
+        match c {
+            VMADDR_CID_ANY => VsockCid::Any,
+            VMADDR_CID_HYPERVISOR => VsockCid::Hypervisor,
+            VMADDR_CID_LOCAL => VsockCid::Local,
+            VMADDR_CID_HOST => VsockCid::Host,
+            _ => VsockCid::Cid(c),
+        }
+    }
+}
+
+impl FromStr for VsockCid {
+    type Err = ParseIntError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let c: c_uint = s.parse()?;
+        Ok(c.into())
+    }
+}
+
+impl Into<c_uint> for VsockCid {
+    fn into(self) -> c_uint {
+        match self {
+            VsockCid::Any => VMADDR_CID_ANY,
+            VsockCid::Hypervisor => VMADDR_CID_HYPERVISOR,
+            VsockCid::Local => VMADDR_CID_LOCAL,
+            VsockCid::Host => VMADDR_CID_HOST,
+            VsockCid::Cid(c) => c,
+        }
+    }
+}
+
 /// An address associated with a virtual socket.
 #[derive(Debug, Copy, Clone)]
 pub struct SocketAddr {
-    pub cid: c_uint,
+    pub cid: VsockCid,
     pub port: c_uint,
 }
 
@@ -143,7 +207,7 @@
 
         let mut svm: sockaddr_vm = Default::default();
         svm.svm_family = AF_VSOCK;
-        svm.svm_cid = sockaddr.cid;
+        svm.svm_cid = sockaddr.cid.into();
         svm.svm_port = sockaddr.port;
 
         // Safe because this just connects a vsock socket, and the return value is checked.
@@ -253,7 +317,7 @@
 impl VsockListener {
     /// Creates a new `VsockListener` bound to the specified port on the current virtual socket
     /// endpoint.
-    pub fn bind(port: c_uint) -> io::Result<VsockListener> {
+    pub fn bind(cid: VsockCid, port: c_uint) -> io::Result<VsockListener> {
         // The compiler should optimize this out since these are both compile-time constants.
         assert_eq!(size_of::<sockaddr_vm>(), size_of::<sockaddr>());
 
@@ -271,7 +335,7 @@
 
         let mut svm: sockaddr_vm = Default::default();
         svm.svm_family = AF_VSOCK;
-        svm.svm_cid = VMADDR_CID_ANY;
+        svm.svm_cid = cid.into();
         svm.svm_port = port;
 
         // Safe because this doesn't modify any memory and we check the return value.
@@ -357,7 +421,7 @@
         Ok((
             VsockStream { fd },
             SocketAddr {
-                cid: svm.svm_cid,
+                cid: svm.svm_cid.into(),
                 port: svm.svm_port,
             },
         ))
diff --git a/sirenia/src/dugong.rs b/sirenia/src/dugong.rs
index b7bdfd7..5a15c35 100644
--- a/sirenia/src/dugong.rs
+++ b/sirenia/src/dugong.rs
@@ -8,8 +8,8 @@
 use std::io::{copy, stdin, stdout};
 use std::thread::spawn;
 
+use libchromeos::vsock::VsockCid;
 use sirenia::cli::{initialize_common_arguments, TransportType};
-use sirenia::to_sys_util::VsockCid;
 use sirenia::transport::{ClientTransport, IPClientTransport, Transport, VsockClientTransport};
 
 fn main() {
diff --git a/sirenia/src/to_sys_util/mod.rs b/sirenia/src/to_sys_util/mod.rs
index d52d3d2..b28c443 100644
--- a/sirenia/src/to_sys_util/mod.rs
+++ b/sirenia/src/to_sys_util/mod.rs
@@ -12,12 +12,7 @@
 use std::mem::MaybeUninit;
 use std::ptr::null_mut;
 
-use libc::{
-    self, c_int, sigfillset, sigprocmask, sigset_t, wait, ECHILD, SIG_BLOCK, SIG_UNBLOCK,
-    VMADDR_CID_ANY, VMADDR_CID_HOST, VMADDR_CID_HYPERVISOR,
-};
-
-const VMADDR_CID_LOCAL: u32 = 1;
+use libc::{self, c_int, sigfillset, sigprocmask, sigset_t, wait, ECHILD, SIG_BLOCK, SIG_UNBLOCK};
 
 pub fn errno() -> c_int {
     io::Error::last_os_error().raw_os_error().unwrap()
@@ -72,36 +67,3 @@
         Ok(ret)
     }
 }
-
-#[derive(Debug, Copy, Clone)]
-pub enum VsockCid {
-    Any,
-    Hypervisor,
-    Local,
-    Host,
-    Cid(u32),
-}
-
-impl From<u32> for VsockCid {
-    fn from(c: u32) -> Self {
-        match c {
-            VMADDR_CID_ANY => VsockCid::Any,
-            VMADDR_CID_HYPERVISOR => VsockCid::Hypervisor,
-            VMADDR_CID_LOCAL => VsockCid::Local,
-            VMADDR_CID_HOST => VsockCid::Host,
-            _ => VsockCid::Cid(c),
-        }
-    }
-}
-
-impl Into<u32> for VsockCid {
-    fn into(self) -> u32 {
-        match self {
-            VsockCid::Any => VMADDR_CID_ANY,
-            VsockCid::Hypervisor => VMADDR_CID_HYPERVISOR,
-            VsockCid::Local => VMADDR_CID_LOCAL,
-            VsockCid::Host => VMADDR_CID_HOST,
-            VsockCid::Cid(c) => c,
-        }
-    }
-}
diff --git a/sirenia/src/transport/mod.rs b/sirenia/src/transport/mod.rs
index a9321b2..130c277 100644
--- a/sirenia/src/transport/mod.rs
+++ b/sirenia/src/transport/mod.rs
@@ -16,11 +16,9 @@
 use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
 
 use core::mem::replace;
-use libchromeos::vsock::{self, VsockListener, VsockStream};
+use libchromeos::vsock::{self, VsockCid, VsockListener, VsockStream};
 use sys_util::{handle_eintr, pipe};
 
-use super::to_sys_util::VsockCid;
-
 const DEFAULT_PORT: u32 = 5552;
 
 #[derive(Debug)]
@@ -165,7 +163,7 @@
 
 impl VsockServerTransport {
     pub fn new() -> Result<Self> {
-        let listener = VsockListener::bind(DEFAULT_PORT).map_err(Error::Bind)?;
+        let listener = VsockListener::bind(VsockCid::Any, DEFAULT_PORT).map_err(Error::Bind)?;
         Ok(VsockServerTransport(listener))
     }
 }
@@ -189,7 +187,7 @@
 impl ClientTransport for VsockClientTransport {
     fn connect(&mut self) -> Result<Transport> {
         let addr = vsock::SocketAddr {
-            cid: self.0.into(),
+            cid: self.0,
             port: DEFAULT_PORT,
         };
         let stream = handle_eintr!(VsockStream::connect(&addr)).map_err(Error::Connect)?;
diff --git a/vm_tools/9s/src/main.rs b/vm_tools/9s/src/main.rs
index 458fc2a..1210eb2 100644
--- a/vm_tools/9s/src/main.rs
+++ b/vm_tools/9s/src/main.rs
@@ -219,9 +219,9 @@
 fn run_vsock_server(
     server_params: Arc<ServerParams>,
     port: c_uint,
-    accept_cid: c_uint,
+    accept_cid: VsockCid,
 ) -> io::Result<()> {
-    let listener = VsockListener::bind(port)?;
+    let listener = VsockListener::bind(VsockCid::Any, port)?;
 
     loop {
         let (stream, peer) = listener.accept()?;
@@ -409,7 +409,7 @@
     {
         ListenAddress::Vsock(port) => {
             let accept_cid = if let Some(cid) = matches.opt_str("accept_cid") {
-                cid.parse::<c_uint>().map_err(Error::Cid)
+                cid.parse::<VsockCid>().map_err(Error::Cid)
             } else {
                 Err(Error::MissingAcceptCid)
             }?;
diff --git a/vm_tools/chunnel/src/bin/chunneld.rs b/vm_tools/chunnel/src/bin/chunneld.rs
index 0084a01..a6b2e4f 100644
--- a/vm_tools/chunnel/src/bin/chunneld.rs
+++ b/vm_tools/chunnel/src/bin/chunneld.rs
@@ -21,7 +21,7 @@
     self, tree, Connection as DBusConnection, Error as DBusError, Message as DBusMessage, OwnedFd,
 };
 use libchromeos::syslog;
-use libchromeos::vsock::{VsockListener, VMADDR_PORT_ANY};
+use libchromeos::vsock::{VsockCid, VsockListener, VMADDR_PORT_ANY};
 use log::{error, warn};
 use protobuf::{self, Message as ProtoMessage, ProtobufError};
 use sys_util::{self, block_signal, pipe, EventFd, PollContext, PollToken};
@@ -71,7 +71,7 @@
     DBusRegisterTree(DBusError),
     EventFdClone(sys_util::Error),
     EventFdNew(sys_util::Error),
-    IncorrectCid(u32),
+    IncorrectCid(VsockCid),
     LifelinePipe(sys_util::Error),
     NoListenerForPort(u16),
     NoSessionForTag(SessionTag),
@@ -141,7 +141,7 @@
     pub vm_name: String,
     pub container_name: String,
     pub owner_id: String,
-    pub vsock_cid: u32,
+    pub vsock_cid: VsockCid,
 }
 
 /// A tag that uniquely identifies a particular forwarding session. This has arbitrarily been
@@ -464,7 +464,8 @@
 ) -> Result<ForwarderSession> {
     let (tcp_stream, _) = listener.accept().map_err(Error::TcpAccept)?;
     // Bind a vsock port, tell the guest to connect, and accept the connection.
-    let mut vsock_listener = VsockListener::bind(VMADDR_PORT_ANY).map_err(Error::BindVsock)?;
+    let mut vsock_listener =
+        VsockListener::bind(VsockCid::Any, VMADDR_PORT_ANY).map_err(Error::BindVsock)?;
     vsock_listener
         .set_nonblocking(true)
         .map_err(Error::SetVsockNonblocking)?;
@@ -531,7 +532,7 @@
             vm_name: forward_target.take_vm_name(),
             owner_id: forward_target.take_owner_id(),
             container_name: forward_target.take_container_name(),
-            vsock_cid: forward_target.vsock_cid,
+            vsock_cid: forward_target.vsock_cid.into(),
         });
     }