mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-09-19 13:30:56 +00:00
342 lines
12 KiB
Rust
342 lines
12 KiB
Rust
|
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
//
|
||
|
|
||
|
/// `VsockPacket` provides a thin wrapper over the buffers exchanged via virtio queues.
|
||
|
/// There are two components to a vsock packet, each using its own descriptor in a
|
||
|
/// virtio queue:
|
||
|
/// - the packet header; and
|
||
|
/// - the packet data/buffer.
|
||
|
/// There is a 1:1 relation between descriptor chains and packets: the first (chain head) holds
|
||
|
/// the header, and an optional second descriptor holds the data. The second descriptor is only
|
||
|
/// present for data packets (VSOCK_OP_RW).
|
||
|
///
|
||
|
/// `VsockPacket` wraps these two buffers and provides direct access to the data stored
|
||
|
/// in guest memory. This is done to avoid unnecessarily copying data from guest memory
|
||
|
/// to temporary buffers, before passing it on to the vsock backend.
|
||
|
///
|
||
|
use byteorder::{ByteOrder, LittleEndian};
|
||
|
|
||
|
use super::super::DescriptorChain;
|
||
|
use super::defs;
|
||
|
use super::{Result, VsockError};
|
||
|
|
||
|
// The vsock packet header is defined by the C struct:
|
||
|
//
|
||
|
// ```C
|
||
|
// struct virtio_vsock_hdr {
|
||
|
// le64 src_cid;
|
||
|
// le64 dst_cid;
|
||
|
// le32 src_port;
|
||
|
// le32 dst_port;
|
||
|
// le32 len;
|
||
|
// le16 type;
|
||
|
// le16 op;
|
||
|
// le32 flags;
|
||
|
// le32 buf_alloc;
|
||
|
// le32 fwd_cnt;
|
||
|
// };
|
||
|
// ```
|
||
|
//
|
||
|
// This structed will occupy the buffer pointed to by the head descriptor. We'll be accessing it
|
||
|
// as a byte slice. To that end, we define below the offsets for each field struct, as well as the
|
||
|
// packed struct size, as a bunch of `usize` consts.
|
||
|
// Note that these offsets are only used privately by the `VsockPacket` struct, the public interface
|
||
|
// consisting of getter and setter methods, for each struct field, that will also handle the correct
|
||
|
// endianess.
|
||
|
|
||
|
/// The vsock packet header struct size (when packed).
|
||
|
pub const VSOCK_PKT_HDR_SIZE: usize = 44;
|
||
|
|
||
|
// Source CID.
|
||
|
const HDROFF_SRC_CID: usize = 0;
|
||
|
|
||
|
// Destination CID.
|
||
|
const HDROFF_DST_CID: usize = 8;
|
||
|
|
||
|
// Source port.
|
||
|
const HDROFF_SRC_PORT: usize = 16;
|
||
|
|
||
|
// Destination port.
|
||
|
const HDROFF_DST_PORT: usize = 20;
|
||
|
|
||
|
// Data length (in bytes) - may be 0, if there is no data buffer.
|
||
|
const HDROFF_LEN: usize = 24;
|
||
|
|
||
|
// Socket type. Currently, only connection-oriented streams are defined by the vsock protocol.
|
||
|
const HDROFF_TYPE: usize = 28;
|
||
|
|
||
|
// Operation ID - one of the VSOCK_OP_* values; e.g.
|
||
|
// - VSOCK_OP_RW: a data packet;
|
||
|
// - VSOCK_OP_REQUEST: connection request;
|
||
|
// - VSOCK_OP_RST: forcefull connection termination;
|
||
|
// etc (see `super::defs::uapi` for the full list).
|
||
|
const HDROFF_OP: usize = 30;
|
||
|
|
||
|
// Additional options (flags) associated with the current operation (`op`).
|
||
|
// Currently, only used with shutdown requests (VSOCK_OP_SHUTDOWN).
|
||
|
const HDROFF_FLAGS: usize = 32;
|
||
|
|
||
|
// Size (in bytes) of the packet sender receive buffer (for the connection to which this packet
|
||
|
// belongs).
|
||
|
const HDROFF_BUF_ALLOC: usize = 36;
|
||
|
|
||
|
// Number of bytes the sender has received and consumed (for the connection to which this packet
|
||
|
// belongs). For instance, for our Unix backend, this counter would be the total number of bytes
|
||
|
// we have successfully written to a backing Unix socket.
|
||
|
const HDROFF_FWD_CNT: usize = 40;
|
||
|
|
||
|
/// The vsock packet, implemented as a wrapper over a virtq descriptor chain:
|
||
|
/// - the chain head, holding the packet header; and
|
||
|
/// - (an optional) data/buffer descriptor, only present for data packets (VSOCK_OP_RW).
|
||
|
///
|
||
|
pub struct VsockPacket {
|
||
|
hdr: *mut u8,
|
||
|
buf: Option<*mut u8>,
|
||
|
buf_size: usize,
|
||
|
}
|
||
|
|
||
|
impl VsockPacket {
|
||
|
/// Create the packet wrapper from a TX virtq chain head.
|
||
|
///
|
||
|
/// The chain head is expected to hold valid packet header data. A following packet buffer
|
||
|
/// descriptor can optionally end the chain. Bounds and pointer checks are performed when
|
||
|
/// creating the wrapper.
|
||
|
///
|
||
|
pub fn from_tx_virtq_head(head: &DescriptorChain) -> Result<Self> {
|
||
|
// All buffers in the TX queue must be readable.
|
||
|
//
|
||
|
if head.is_write_only() {
|
||
|
return Err(VsockError::UnreadableDescriptor);
|
||
|
}
|
||
|
|
||
|
// The packet header should fit inside the head descriptor.
|
||
|
if head.len < VSOCK_PKT_HDR_SIZE as u32 {
|
||
|
return Err(VsockError::HdrDescTooSmall(head.len));
|
||
|
}
|
||
|
|
||
|
let mut pkt = Self {
|
||
|
hdr: head
|
||
|
.mem
|
||
|
.get_host_address(head.addr)
|
||
|
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||
|
buf: None,
|
||
|
buf_size: 0,
|
||
|
};
|
||
|
|
||
|
// No point looking for a data/buffer descriptor, if the packet is zero-lengthed.
|
||
|
if pkt.len() == 0 {
|
||
|
return Ok(pkt);
|
||
|
}
|
||
|
|
||
|
// Reject weirdly-sized packets.
|
||
|
//
|
||
|
if pkt.len() > defs::MAX_PKT_BUF_SIZE as u32 {
|
||
|
return Err(VsockError::InvalidPktLen(pkt.len()));
|
||
|
}
|
||
|
|
||
|
// If the packet header showed a non-zero length, there should be a data descriptor here.
|
||
|
let buf_desc = head.next_descriptor().ok_or(VsockError::BufDescMissing)?;
|
||
|
|
||
|
// TX data should be read-only.
|
||
|
if buf_desc.is_write_only() {
|
||
|
return Err(VsockError::UnreadableDescriptor);
|
||
|
}
|
||
|
|
||
|
// The data buffer should be large enough to fit the size of the data, as described by
|
||
|
// the header descriptor.
|
||
|
if buf_desc.len < pkt.len() {
|
||
|
return Err(VsockError::BufDescTooSmall);
|
||
|
}
|
||
|
|
||
|
pkt.buf_size = buf_desc.len as usize;
|
||
|
pkt.buf = Some(
|
||
|
buf_desc
|
||
|
.mem
|
||
|
.get_host_address(buf_desc.addr)
|
||
|
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||
|
);
|
||
|
|
||
|
Ok(pkt)
|
||
|
}
|
||
|
|
||
|
/// Create the packet wrapper from an RX virtq chain head.
|
||
|
///
|
||
|
/// There must be two descriptors in the chain, both writable: a header descriptor and a data
|
||
|
/// descriptor. Bounds and pointer checks are performed when creating the wrapper.
|
||
|
///
|
||
|
pub fn from_rx_virtq_head(head: &DescriptorChain) -> Result<Self> {
|
||
|
// All RX buffers must be writable.
|
||
|
//
|
||
|
if !head.is_write_only() {
|
||
|
return Err(VsockError::UnwritableDescriptor);
|
||
|
}
|
||
|
|
||
|
// The packet header should fit inside the head descriptor.
|
||
|
if head.len < VSOCK_PKT_HDR_SIZE as u32 {
|
||
|
return Err(VsockError::HdrDescTooSmall(head.len));
|
||
|
}
|
||
|
|
||
|
// All RX descriptor chains should have a header and a data descriptor.
|
||
|
if !head.has_next() {
|
||
|
return Err(VsockError::BufDescMissing);
|
||
|
}
|
||
|
let buf_desc = head.next_descriptor().ok_or(VsockError::BufDescMissing)?;
|
||
|
|
||
|
Ok(Self {
|
||
|
hdr: head
|
||
|
.mem
|
||
|
.get_host_address(head.addr)
|
||
|
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||
|
buf: Some(
|
||
|
buf_desc
|
||
|
.mem
|
||
|
.get_host_address(buf_desc.addr)
|
||
|
.ok_or_else(|| VsockError::GuestMemory)? as *mut u8,
|
||
|
),
|
||
|
buf_size: buf_desc.len as usize,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/// Provides in-place, byte-slice, access to the vsock packet header.
|
||
|
///
|
||
|
pub fn hdr(&self) -> &[u8] {
|
||
|
// This is safe since bound checks have already been performed when creating the packet
|
||
|
// from the virtq descriptor.
|
||
|
unsafe { std::slice::from_raw_parts(self.hdr as *const u8, VSOCK_PKT_HDR_SIZE) }
|
||
|
}
|
||
|
|
||
|
/// Provides in-place, byte-slice, mutable access to the vsock packet header.
|
||
|
///
|
||
|
pub fn hdr_mut(&mut self) -> &mut [u8] {
|
||
|
// This is safe since bound checks have already been performed when creating the packet
|
||
|
// from the virtq descriptor.
|
||
|
unsafe { std::slice::from_raw_parts_mut(self.hdr, VSOCK_PKT_HDR_SIZE) }
|
||
|
}
|
||
|
|
||
|
/// Provides in-place, byte-slice access to the vsock packet data buffer.
|
||
|
///
|
||
|
/// Note: control packets (e.g. connection request or reset) have no data buffer associated.
|
||
|
/// For those packets, this method will return `None`.
|
||
|
/// Also note: calling `len()` on the returned slice will yield the buffer size, which may be
|
||
|
/// (and often is) larger than the length of the packet data. The packet data length
|
||
|
/// is stored in the packet header, and accessible via `VsockPacket::len()`.
|
||
|
pub fn buf(&self) -> Option<&[u8]> {
|
||
|
self.buf.map(|ptr| {
|
||
|
// This is safe since bound checks have already been performed when creating the packet
|
||
|
// from the virtq descriptor.
|
||
|
unsafe { std::slice::from_raw_parts(ptr as *const u8, self.buf_size) }
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/// Provides in-place, byte-slice, mutable access to the vsock packet data buffer.
|
||
|
///
|
||
|
/// Note: control packets (e.g. connection request or reset) have no data buffer associated.
|
||
|
/// For those packets, this method will return `None`.
|
||
|
/// Also note: calling `len()` on the returned slice will yield the buffer size, which may be
|
||
|
/// (and often is) larger than the length of the packet data. The packet data length
|
||
|
/// is stored in the packet header, and accessible via `VsockPacket::len()`.
|
||
|
pub fn buf_mut(&mut self) -> Option<&mut [u8]> {
|
||
|
self.buf.map(|ptr| {
|
||
|
// This is safe since bound checks have already been performed when creating the packet
|
||
|
// from the virtq descriptor.
|
||
|
unsafe { std::slice::from_raw_parts_mut(ptr, self.buf_size) }
|
||
|
})
|
||
|
}
|
||
|
|
||
|
pub fn src_cid(&self) -> u64 {
|
||
|
LittleEndian::read_u64(&self.hdr()[HDROFF_SRC_CID..])
|
||
|
}
|
||
|
|
||
|
pub fn set_src_cid(&mut self, cid: u64) -> &mut Self {
|
||
|
LittleEndian::write_u64(&mut self.hdr_mut()[HDROFF_SRC_CID..], cid);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn dst_cid(&self) -> u64 {
|
||
|
LittleEndian::read_u64(&self.hdr()[HDROFF_DST_CID..])
|
||
|
}
|
||
|
|
||
|
pub fn set_dst_cid(&mut self, cid: u64) -> &mut Self {
|
||
|
LittleEndian::write_u64(&mut self.hdr_mut()[HDROFF_DST_CID..], cid);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn src_port(&self) -> u32 {
|
||
|
LittleEndian::read_u32(&self.hdr()[HDROFF_SRC_PORT..])
|
||
|
}
|
||
|
|
||
|
pub fn set_src_port(&mut self, port: u32) -> &mut Self {
|
||
|
LittleEndian::write_u32(&mut self.hdr_mut()[HDROFF_SRC_PORT..], port);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn dst_port(&self) -> u32 {
|
||
|
LittleEndian::read_u32(&self.hdr()[HDROFF_DST_PORT..])
|
||
|
}
|
||
|
|
||
|
pub fn set_dst_port(&mut self, port: u32) -> &mut Self {
|
||
|
LittleEndian::write_u32(&mut self.hdr_mut()[HDROFF_DST_PORT..], port);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn len(&self) -> u32 {
|
||
|
LittleEndian::read_u32(&self.hdr()[HDROFF_LEN..])
|
||
|
}
|
||
|
|
||
|
pub fn set_len(&mut self, len: u32) -> &mut Self {
|
||
|
LittleEndian::write_u32(&mut self.hdr_mut()[HDROFF_LEN..], len);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn type_(&self) -> u16 {
|
||
|
LittleEndian::read_u16(&self.hdr()[HDROFF_TYPE..])
|
||
|
}
|
||
|
|
||
|
pub fn set_type(&mut self, type_: u16) -> &mut Self {
|
||
|
LittleEndian::write_u16(&mut self.hdr_mut()[HDROFF_TYPE..], type_);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn op(&self) -> u16 {
|
||
|
LittleEndian::read_u16(&self.hdr()[HDROFF_OP..])
|
||
|
}
|
||
|
|
||
|
pub fn set_op(&mut self, op: u16) -> &mut Self {
|
||
|
LittleEndian::write_u16(&mut self.hdr_mut()[HDROFF_OP..], op);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn flags(&self) -> u32 {
|
||
|
LittleEndian::read_u32(&self.hdr()[HDROFF_FLAGS..])
|
||
|
}
|
||
|
|
||
|
pub fn set_flags(&mut self, flags: u32) -> &mut Self {
|
||
|
LittleEndian::write_u32(&mut self.hdr_mut()[HDROFF_FLAGS..], flags);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn set_flag(&mut self, flag: u32) -> &mut Self {
|
||
|
self.set_flags(self.flags() | flag);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn buf_alloc(&self) -> u32 {
|
||
|
LittleEndian::read_u32(&self.hdr()[HDROFF_BUF_ALLOC..])
|
||
|
}
|
||
|
|
||
|
pub fn set_buf_alloc(&mut self, buf_alloc: u32) -> &mut Self {
|
||
|
LittleEndian::write_u32(&mut self.hdr_mut()[HDROFF_BUF_ALLOC..], buf_alloc);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn fwd_cnt(&self) -> u32 {
|
||
|
LittleEndian::read_u32(&self.hdr()[HDROFF_FWD_CNT..])
|
||
|
}
|
||
|
|
||
|
pub fn set_fwd_cnt(&mut self, fwd_cnt: u32) -> &mut Self {
|
||
|
LittleEndian::write_u32(&mut self.hdr_mut()[HDROFF_FWD_CNT..], fwd_cnt);
|
||
|
self
|
||
|
}
|
||
|
}
|