Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions include/libkrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ int32_t krun_add_virtiofs2(uint32_t ctx_id,
as required by gvproxy in vfkit mode. */
#define NET_FLAG_VFKIT 1 << 0

/* TSI (Transparent Socket Impersonation) feature flags for vsock */
#define KRUN_TSI_HIJACK_INET (1 << 0)
#define KRUN_TSI_HIJACK_UNIX (1 << 1)

/* Taken from uapi/linux/virtio_net.h */
#define NET_FEATURE_CSUM 1 << 0
#define NET_FEATURE_GUEST_CSUM 1 << 1
Expand Down Expand Up @@ -862,6 +866,27 @@ int32_t krun_add_vsock_port2(uint32_t ctx_id,
uint32_t port,
const char *c_filepath,
bool listen);

/**
* Add a vsock device with specified TSI features.
*
* By default, libkrun creates a vsock device implicitly with TSI hijacking
* enabled based on heuristics. Calling this function overrides the implicit
* behavior and explicitly configures the vsock device.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above seems to point that calling this function will automatically disable the implicit vsock, but that's not what happens. Calling this one without calling krun_disable_implicit_vsock() first will lead to [libcrun:krun]: could not add vsock configuration: File exists.

I think we need to either have this function also disable the implicit vsock, or clarify that krun_disable_implicit_vsock() needs to be called first.

*
* Currently only one vsock device is supported. Calling this function
* multiple times will return an error.
*
* Arguments:
* "ctx_id" - the configuration context ID.
* "tsi_features" - bitmask of TSI features (KRUN_TSI_HIJACK_INET, KRUN_TSI_HIJACK_UNIX)
* Use 0 to add vsock without any TSI hijacking.
*
* Returns:
* Zero on success or a negative error number on failure.
*/
int32_t krun_add_vsock(uint32_t ctx_id, uint32_t tsi_features);

/**
* Returns the eventfd file descriptor to signal the guest to shut down orderly. This must be
* called before starting the microVM with "krun_start_event". Only available in libkrun-efi.
Expand Down Expand Up @@ -1009,6 +1034,20 @@ int32_t krun_nitro_set_start_flags(uint32_t ctx_id, uint64_t start_flags);
*/
int32_t krun_disable_implicit_console(uint32_t ctx_id);

/**
* Disable the implicit vsock device.
*
* By default, libkrun creates a vsock device automatically. This function
* disables that behavior entirely - no vsock device will be created.
*
* Arguments:
* "ctx_id" - the configuration context ID.
*
* Returns:
* Zero on success or a negative error number on failure.
*/
int32_t krun_disable_implicit_vsock(uint32_t ctx_id);

/*
* Specify the value of `console=` in the kernel commandline.
*
Expand Down
24 changes: 5 additions & 19 deletions src/devices/src/virtio/vsock/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use super::super::{
};
use super::muxer::VsockMuxer;
use super::packet::VsockPacket;
use super::TsiFlags;
use super::{defs, defs::uapi};
use crate::virtio::InterruptTransport;

Expand Down Expand Up @@ -52,8 +53,7 @@ impl Vsock {
host_port_map: Option<HashMap<u16, u16>>,
queues: Vec<VirtQueue>,
unix_ipc_port_map: Option<HashMap<u32, (PathBuf, bool)>>,
enable_tsi: bool,
enable_tsi_unix: bool,
tsi_flags: TsiFlags,
) -> super::Result<Vsock> {
let mut queue_events = Vec::new();
for _ in 0..queues.len() {
Expand All @@ -66,13 +66,7 @@ impl Vsock {

Ok(Vsock {
cid,
muxer: VsockMuxer::new(
cid,
host_port_map,
unix_ipc_port_map,
enable_tsi,
enable_tsi_unix,
),
muxer: VsockMuxer::new(cid, host_port_map, unix_ipc_port_map, tsi_flags),
queue_rx,
queue_tx,
queues,
Expand All @@ -90,21 +84,13 @@ impl Vsock {
cid: u64,
host_port_map: Option<HashMap<u16, u16>>,
unix_ipc_port_map: Option<HashMap<u32, (PathBuf, bool)>>,
enable_tsi: bool,
enable_tsi_unix: bool,
tsi_flags: TsiFlags,
) -> super::Result<Vsock> {
let queues: Vec<VirtQueue> = defs::QUEUE_SIZES
.iter()
.map(|&max_size| VirtQueue::new(max_size))
.collect();
Self::with_queues(
cid,
host_port_map,
queues,
unix_ipc_port_map,
enable_tsi,
enable_tsi_unix,
)
Self::with_queues(cid, host_port_map, queues, unix_ipc_port_map, tsi_flags)
}

pub fn id(&self) -> &str {
Expand Down
28 changes: 28 additions & 0 deletions src/devices/src/virtio/vsock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,39 @@ mod tsi_stream;
mod unix;

pub use self::defs::uapi::VIRTIO_ID_VSOCK as TYPE_VSOCK;
pub use self::defs::TsiFlags;
pub use self::device::Vsock;

use bitflags::bitflags;
use vm_memory::GuestMemoryError;

mod defs {
use super::bitflags;

bitflags! {
/// TSI (Transparent Socket Impersonation) feature flags.
///
/// These flags control which socket families are hijacked by TSI.
pub struct TsiFlags: u32 {
/// Hijack AF_INET and AF_INET6 sockets
const HIJACK_INET = 1 << 0;
/// Hijack AF_UNIX sockets
const HIJACK_UNIX = 1 << 1;
}
}

impl TsiFlags {
/// Returns true if any TSI hijacking is enabled.
pub fn tsi_enabled(&self) -> bool {
!self.is_empty()
}
}

impl Default for TsiFlags {
fn default() -> Self {
TsiFlags::empty()
}
}
/// Device ID used in MMIO device identification.
/// Because Vsock is unique per-vm, this ID can be hardcoded.
pub const VSOCK_DEV_ID: &str = "vsock";
Expand Down
54 changes: 36 additions & 18 deletions src/devices/src/virtio/vsock/muxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use super::timesync::TimesyncThread;
use super::tsi_dgram::TsiDgramProxy;
use super::tsi_stream::TsiStreamProxy;
use super::unix::UnixProxy;
use super::TsiFlags;
use super::VsockError;
use crossbeam_channel::{unbounded, Sender};
use utils::epoll::{ControlOperation, Epoll, EpollEvent, EventSet};
Expand Down Expand Up @@ -106,17 +107,15 @@ pub struct VsockMuxer {
proxy_map: ProxyMap,
reaper_sender: Option<Sender<u64>>,
unix_ipc_port_map: Option<HashMap<u32, (PathBuf, bool)>>,
enable_tsi: bool,
enable_tsi_unix: bool,
tsi_flags: TsiFlags,
}

impl VsockMuxer {
pub(crate) fn new(
cid: u64,
host_port_map: Option<HashMap<u16, u16>>,
unix_ipc_port_map: Option<HashMap<u32, (PathBuf, bool)>>,
enable_tsi: bool,
enable_tsi_unix: bool,
tsi_flags: TsiFlags,
) -> Self {
VsockMuxer {
cid,
Expand All @@ -129,8 +128,7 @@ impl VsockMuxer {
proxy_map: Arc::new(RwLock::new(HashMap::new())),
reaper_sender: None,
unix_ipc_port_map,
enable_tsi,
enable_tsi_unix,
tsi_flags,
}
}

Expand Down Expand Up @@ -285,8 +283,16 @@ impl VsockMuxer {
defs::SOCK_STREAM => {
debug!("proxy create stream");
let id = ((req.peer_port as u64) << 32) | (defs::TSI_PROXY_PORT as u64);
if req.family as i32 == libc::AF_UNIX && !self.enable_tsi_unix {
warn!("rejecting tcp unix proxy because tsi_unix is disabled");
if req.family as i32 == libc::AF_UNIX
&& !self.tsi_flags.contains(TsiFlags::HIJACK_UNIX)
{
warn!("rejecting stream unix proxy because HIJACK_UNIX is disabled");
return;
}
if (req.family as i32 == libc::AF_INET || req.family as i32 == libc::AF_INET6)
&& !self.tsi_flags.contains(TsiFlags::HIJACK_INET)
{
warn!("rejecting stream inet proxy because HIJACK_INET is disabled");
return;
}
match TsiStreamProxy::new(
Expand All @@ -312,8 +318,16 @@ impl VsockMuxer {
defs::SOCK_DGRAM => {
debug!("proxy create dgram");
let id = ((req.peer_port as u64) << 32) | (defs::TSI_PROXY_PORT as u64);
if req.family as i32 == libc::AF_UNIX && !self.enable_tsi_unix {
warn!("rejecting udp unix proxy because tsi_unix is disabled");
if req.family as i32 == libc::AF_UNIX
&& !self.tsi_flags.contains(TsiFlags::HIJACK_UNIX)
{
warn!("rejecting dgram unix proxy because HIJACK_UNIX is disabled");
return;
}
if (req.family as i32 == libc::AF_INET || req.family as i32 == libc::AF_INET6)
&& !self.tsi_flags.contains(TsiFlags::HIJACK_INET)
{
warn!("rejecting dgram inet proxy because HIJACK_INET is disabled");
return;
}
match TsiDgramProxy::new(
Expand Down Expand Up @@ -498,14 +512,18 @@ impl VsockMuxer {
}

match pkt.dst_port() {
defs::TSI_PROXY_CREATE if self.enable_tsi => self.process_proxy_create(pkt),
defs::TSI_CONNECT if self.enable_tsi => self.process_connect(pkt),
defs::TSI_GETNAME if self.enable_tsi => self.process_getname(pkt),
defs::TSI_SENDTO_ADDR if self.enable_tsi => self.process_sendto_addr(pkt),
defs::TSI_SENDTO_DATA if self.enable_tsi => self.process_sendto_data(pkt),
defs::TSI_LISTEN if self.enable_tsi => self.process_listen_request(pkt),
defs::TSI_ACCEPT if self.enable_tsi => self.process_accept_request(pkt),
defs::TSI_PROXY_RELEASE if self.enable_tsi => self.process_proxy_release(pkt),
defs::TSI_PROXY_CREATE if self.tsi_flags.tsi_enabled() => {
self.process_proxy_create(pkt)
}
defs::TSI_CONNECT if self.tsi_flags.tsi_enabled() => self.process_connect(pkt),
defs::TSI_GETNAME if self.tsi_flags.tsi_enabled() => self.process_getname(pkt),
defs::TSI_SENDTO_ADDR if self.tsi_flags.tsi_enabled() => self.process_sendto_addr(pkt),
defs::TSI_SENDTO_DATA if self.tsi_flags.tsi_enabled() => self.process_sendto_data(pkt),
defs::TSI_LISTEN if self.tsi_flags.tsi_enabled() => self.process_listen_request(pkt),
defs::TSI_ACCEPT if self.tsi_flags.tsi_enabled() => self.process_accept_request(pkt),
defs::TSI_PROXY_RELEASE if self.tsi_flags.tsi_enabled() => {
self.process_proxy_release(pkt)
}
_ => {
if pkt.op() == uapi::VSOCK_OP_RW {
self.process_dgram_rw(pkt);
Expand Down
Loading
Loading