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 <port><LF>
- [guest/success] OK <assigned_host_port><LF>

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 <dhr@amazon.com>
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-12-18 21:03:20 +01:00
parent 5c4e1726f8
commit 9701fde209
3 changed files with 23 additions and 2 deletions

View File

@ -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 {

View File

@ -38,7 +38,7 @@ type Result<T> = std::result::Result<T, Error>;
/// 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,

View File

@ -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)
}
}