From 9701fde209b735596069c9710c5812b4bd498eca Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Wed, 18 Dec 2019 21:03:20 +0100 Subject: [PATCH] vm-virtio: Add connection handshake to vsock This patch has been cherry-picked from the Firecracker tree. The reference commit is 1db04ccc69862f30b7814f30024d112d1b86b80e. Changed the host-initiated vsock connection protocol to include a trivial handshake. The new protocol looks like this: - [host] CONNECT - [guest/success] OK On connection failure, the host host connection is reset without any accompanying message, as before. This allows host software to more easily detect connection failures, for instance when attempting to connect to a guest server that may have not yet started listening for client connections. Signed-off-by: Dan Horobeanu Signed-off-by: Sebastien Boeuf --- vm-virtio/src/vsock/csm/connection.rs | 7 ++++++- vm-virtio/src/vsock/csm/mod.rs | 2 +- vm-virtio/src/vsock/unix/muxer.rs | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/vm-virtio/src/vsock/csm/connection.rs b/vm-virtio/src/vsock/csm/connection.rs index a42794c6a..6770870b2 100644 --- a/vm-virtio/src/vsock/csm/connection.rs +++ b/vm-virtio/src/vsock/csm/connection.rs @@ -557,7 +557,7 @@ where /// Raw data can either be sent straight to the host stream, or to our TX buffer, if the /// former fails. /// - fn send_bytes(&mut self, buf: &[u8]) -> Result<()> { + pub fn send_bytes(&mut self, buf: &[u8]) -> Result<()> { // If there is data in the TX buffer, that means we're already registered for EPOLLOUT // events on the underlying stream. Therefore, there's no point in attempting a write // at this point. `self.notify()` will get called when EPOLLOUT arrives, and it will @@ -592,6 +592,11 @@ where Ok(()) } + /// Return the connections state. + pub fn state(&self) -> ConnState { + self.state + } + /// Check if the credit information the peer has last received from us is outdated. /// fn peer_needs_credit_update(&self) -> bool { diff --git a/vm-virtio/src/vsock/csm/mod.rs b/vm-virtio/src/vsock/csm/mod.rs index a6bd7071a..635b7931b 100644 --- a/vm-virtio/src/vsock/csm/mod.rs +++ b/vm-virtio/src/vsock/csm/mod.rs @@ -38,7 +38,7 @@ type Result = std::result::Result; /// A vsock connection state. /// -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum ConnState { /// The connection has been initiated by the host end, but is yet to be confirmed by the guest. LocalInit, diff --git a/vm-virtio/src/vsock/unix/muxer.rs b/vm-virtio/src/vsock/unix/muxer.rs index 2e6f8943b..88af69d45 100644 --- a/vm-virtio/src/vsock/unix/muxer.rs +++ b/vm-virtio/src/vsock/unix/muxer.rs @@ -36,6 +36,7 @@ use std::io::Read; use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; +use super::super::csm::ConnState; use super::super::defs::uapi; use super::super::packet::VsockPacket; use super::super::{ @@ -652,9 +653,20 @@ impl VsockMuxer { if let Some(conn) = self.conn_map.get_mut(&key) { let had_rx = conn.has_pending_rx(); let was_expiring = conn.will_expire(); + let prev_state = conn.state(); mut_fn(conn); + // If this is a host-initiated connection that has just become established, we'll have + // to send an ack message to the host end. + if prev_state == ConnState::LocalInit && conn.state() == ConnState::Established { + conn.send_bytes(format!("OK {}\n", key.local_port).as_bytes()) + .unwrap_or_else(|err| { + conn.kill(); + warn!("vsock: unable to ack host connection: {:?}", err); + }); + } + // If the connection wasn't previously scheduled for RX, add it to our RX queue. if !had_rx && conn.has_pending_rx() { self.rxq.push(MuxerRx::ConnRx(key)); @@ -917,6 +929,10 @@ mod tests { self.init_pkt(local_port, peer_port, uapi::VSOCK_OP_RESPONSE); self.send(); + let mut buf = vec![0u8; 32]; + let len = stream.read(&mut buf[..]).unwrap(); + assert_eq!(&buf[..len], format!("OK {}\n", local_port).as_bytes()); + (stream, local_port) } }