1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 //! This module implements our vsock connection state machine. The heavy lifting is done by 5 //! `connection::VsockConnection`, while this file only defines some constants and helper structs. 6 7 use thiserror::Error; 8 9 mod connection; 10 mod txbuf; 11 12 pub use connection::VsockConnection; 13 14 pub mod defs { 15 /// Vsock connection TX buffer capacity. 16 pub const CONN_TX_BUF_SIZE: u32 = 64 * 1024; 17 18 /// When the guest thinks we have less than this amount of free buffer space, 19 /// we will send them a credit update packet. 20 pub const CONN_CREDIT_UPDATE_THRESHOLD: u32 = 4 * 1024; 21 22 /// Connection request timeout, in millis. 23 pub const CONN_REQUEST_TIMEOUT_MS: u64 = 2000; 24 25 /// Connection graceful shutdown timeout, in millis. 26 pub const CONN_SHUTDOWN_TIMEOUT_MS: u64 = 2000; 27 } 28 29 #[derive(Debug, Error)] 30 pub enum Error { 31 /// Attempted to push data to a full TX buffer. 32 #[error("TX buffer full")] 33 TxBufFull, 34 /// An I/O error occurred, when attempting to flush the connection TX buffer. 35 #[error("Error flushing TX buffer")] 36 TxBufFlush(#[source] std::io::Error), 37 /// An I/O error occurred, when attempting to write data to the host-side stream. 38 #[error("Error writing to host side stream")] 39 StreamWrite(#[source] std::io::Error), 40 } 41 42 type Result<T> = std::result::Result<T, Error>; 43 44 /// A vsock connection state. 45 /// 46 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 47 pub enum ConnState { 48 /// The connection has been initiated by the host end, but is yet to be confirmed by the guest. 49 LocalInit, 50 /// The connection has been initiated by the guest, but we are yet to confirm it, by sending 51 /// a response packet (VSOCK_OP_RESPONSE). 52 PeerInit, 53 /// The connection handshake has been performed successfully, and data can now be exchanged. 54 Established, 55 /// The host (AF_UNIX) socket was closed. 56 LocalClosed, 57 /// A VSOCK_OP_SHUTDOWN packet was received from the guest. The tuple represents the guest R/W 58 /// indication: (will_not_recv_anymore_data, will_not_send_anymore_data). 59 PeerClosed(bool, bool), 60 /// The connection is scheduled to be forcefully terminated as soon as possible. 61 Killed, 62 } 63 64 /// An RX indication, used by `VsockConnection` to schedule future `recv_pkt()` responses. 65 /// For instance, after being notified that there is available data to be read from the host stream 66 /// (via `notify()`), the connection will store a `PendingRx::Rw` to be later inspected by 67 /// `recv_pkt()`. 68 /// 69 #[derive(Clone, Copy, PartialEq, Eq)] 70 enum PendingRx { 71 /// We need to yield a connection request packet (VSOCK_OP_REQUEST). 72 Request = 0, 73 /// We need to yield a connection response packet (VSOCK_OP_RESPONSE). 74 Response = 1, 75 /// We need to yield a forceful connection termination packet (VSOCK_OP_RST). 76 Rst = 2, 77 /// We need to yield a data packet (VSOCK_OP_RW), by reading from the AF_UNIX socket. 78 Rw = 3, 79 /// We need to yield a credit update packet (VSOCK_OP_CREDIT_UPDATE). 80 CreditUpdate = 4, 81 } 82 impl PendingRx { 83 /// Transform the enum value into a bitmask, that can be used for set operations. 84 /// 85 fn into_mask(self) -> u16 { 86 1u16 << (self as u16) 87 } 88 } 89 90 /// A set of RX indications (`PendingRx` items). 91 /// 92 struct PendingRxSet { 93 data: u16, 94 } 95 96 impl PendingRxSet { 97 /// Insert an item into the set. 98 /// 99 fn insert(&mut self, it: PendingRx) { 100 self.data |= it.into_mask(); 101 } 102 103 /// Remove an item from the set and return: 104 /// - true, if the item was in the set; or 105 /// - false, if the item wasn't in the set. 106 /// 107 fn remove(&mut self, it: PendingRx) -> bool { 108 let ret = self.contains(it); 109 self.data &= !it.into_mask(); 110 ret 111 } 112 113 /// Check if an item is present in this set. 114 /// 115 fn contains(&self, it: PendingRx) -> bool { 116 self.data & it.into_mask() != 0 117 } 118 119 /// Check if the set is empty. 120 /// 121 fn is_empty(&self) -> bool { 122 self.data == 0 123 } 124 } 125 126 /// Create a set containing only one item. 127 /// 128 impl From<PendingRx> for PendingRxSet { 129 fn from(it: PendingRx) -> Self { 130 Self { 131 data: it.into_mask(), 132 } 133 } 134 } 135