From 51a93bc635a61105e8e92ffdf7694a821b958edc Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 28 Apr 2021 15:35:50 +0100 Subject: [PATCH] virtio-devices: net: Add support for VIRTIO_NET_F_CTRL_GUEST_OFFLOADS This allows the guest to reprogram the offload settings and mitigates issues where the Linux kernel tries to reprogram the queues even when the feature is not advertised. Fixes: #2528 Signed-off-by: Rob Bradford --- virtio-devices/src/net.rs | 3 ++- virtio-devices/src/net_util.rs | 33 +++++++++++++++++++++++++-- virtio-devices/src/seccomp_filters.rs | 18 +++++++++++---- virtio-devices/src/vhost_user/net.rs | 2 +- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/virtio-devices/src/net.rs b/virtio-devices/src/net.rs index ea86d3dbb..c8d7a89db 100644 --- a/virtio-devices/src/net.rs +++ b/virtio-devices/src/net.rs @@ -315,6 +315,7 @@ impl Net { rate_limiter_config: Option, ) -> Result { let mut avail_features = 1 << VIRTIO_NET_F_CSUM + | 1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS | 1 << VIRTIO_NET_F_GUEST_CSUM | 1 << VIRTIO_NET_F_GUEST_ECN | 1 << VIRTIO_NET_F_GUEST_TSO4 @@ -511,7 +512,7 @@ impl VirtioDevice for Net { mem: mem.clone(), kill_evt, pause_evt, - ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt), + ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt, Some(self.taps.clone())), }; let paused = self.common.paused.clone(); diff --git a/virtio-devices/src/net_util.rs b/virtio-devices/src/net_util.rs index 90f53192a..3e86dd488 100644 --- a/virtio-devices/src/net_util.rs +++ b/virtio-devices/src/net_util.rs @@ -4,6 +4,7 @@ use super::{EpollHelper, EpollHelperError, EpollHelperHandler, Queue, EPOLL_HELPER_EVENT_LAST}; use net_util::MacAddr; +use net_util::Tap; use std::os::raw::c_uint; use std::os::unix::io::AsRawFd; use std::sync::atomic::AtomicBool; @@ -48,6 +49,7 @@ pub enum Error { pub struct NetCtrl { pub queue_evt: EventFd, pub queue: Queue, + pub taps: Option>, } #[repr(C, packed)] @@ -60,8 +62,12 @@ pub struct ControlHeader { unsafe impl ByteValued for ControlHeader {} impl NetCtrl { - pub fn new(queue: Queue, queue_evt: EventFd) -> Self { - NetCtrl { queue_evt, queue } + pub fn new(queue: Queue, queue_evt: EventFd, taps: Option>) -> Self { + NetCtrl { + queue_evt, + queue, + taps, + } } pub fn process_cvq(&mut self, mem: &GuestMemoryMmap) -> Result<()> { @@ -96,6 +102,29 @@ impl NetCtrl { true } } + VIRTIO_NET_CTRL_GUEST_OFFLOADS => { + let features = mem + .read_obj::(data_desc.addr) + .map_err(Error::GuestMemory)?; + if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET { + warn!("Unsupported command: {}", ctrl_hdr.cmd); + false + } else { + let mut ok = true; + if let Some(ref mut taps) = &mut self.taps { + for tap in taps.iter_mut() { + info!("Reprogramming tap offload with features: {}", features); + tap.set_offload(virtio_features_to_tap_offload(features)) + .map_err(|e| { + error!("Error programming tap offload: {:?}", e); + ok = false + }) + .ok(); + } + } + ok + } + } _ => { warn!("Unsupported command {:?}", ctrl_hdr); false diff --git a/virtio-devices/src/seccomp_filters.rs b/virtio-devices/src/seccomp_filters.rs index 7194de75b..067b6bfb6 100644 --- a/virtio-devices/src/seccomp_filters.rs +++ b/virtio-devices/src/seccomp_filters.rs @@ -59,6 +59,9 @@ const FIONBIO: u64 = 0x5421; const VFIO_IOMMU_MAP_DMA: u64 = 0x3b71; const VFIO_IOMMU_UNMAP_DMA: u64 = 0x3b72; +// See include/uapi/linux/if_tun.h in the kernel code. +const TUNSETOFFLOAD: u64 = 0x4004_54d0; + fn create_virtio_iommu_ioctl_seccomp_rule() -> Vec { or![ and![Cond::new(1, ArgLen::DWORD, Eq, VFIO_IOMMU_MAP_DMA).unwrap()], @@ -234,8 +237,12 @@ fn virtio_net_thread_rules() -> Vec { ] } -fn virtio_net_ctl_thread_rules() -> Vec { - vec![ +fn create_virtio_net_ctl_ioctl_seccomp_rule() -> Result, Error> { + Ok(or![and![Cond::new(1, ArgLen::DWORD, Eq, TUNSETOFFLOAD)?],]) +} + +fn virtio_net_ctl_thread_rules() -> Result, Error> { + Ok(vec![ allow_syscall(libc::SYS_brk), allow_syscall(libc::SYS_close), allow_syscall(libc::SYS_dup), @@ -246,13 +253,14 @@ fn virtio_net_ctl_thread_rules() -> Vec { allow_syscall(libc::SYS_epoll_wait), allow_syscall(libc::SYS_exit), allow_syscall(libc::SYS_futex), + allow_syscall_if(libc::SYS_ioctl, create_virtio_net_ctl_ioctl_seccomp_rule()?), allow_syscall(libc::SYS_madvise), allow_syscall(libc::SYS_munmap), allow_syscall(libc::SYS_read), allow_syscall(libc::SYS_rt_sigprocmask), allow_syscall(libc::SYS_sigaltstack), allow_syscall(libc::SYS_write), - ] + ]) } fn virtio_pmem_thread_rules() -> Vec { @@ -451,7 +459,7 @@ fn get_seccomp_filter_trap(thread_type: Thread) -> Result Thread::VirtioIommu => virtio_iommu_thread_rules(), Thread::VirtioMem => virtio_mem_thread_rules(), Thread::VirtioNet => virtio_net_thread_rules(), - Thread::VirtioNetCtl => virtio_net_ctl_thread_rules(), + Thread::VirtioNetCtl => virtio_net_ctl_thread_rules()?, Thread::VirtioPmem => virtio_pmem_thread_rules(), Thread::VirtioRng => virtio_rng_thread_rules(), Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules(), @@ -473,7 +481,7 @@ fn get_seccomp_filter_log(thread_type: Thread) -> Result { Thread::VirtioIommu => virtio_iommu_thread_rules(), Thread::VirtioMem => virtio_mem_thread_rules(), Thread::VirtioNet => virtio_net_thread_rules(), - Thread::VirtioNetCtl => virtio_net_ctl_thread_rules(), + Thread::VirtioNetCtl => virtio_net_ctl_thread_rules()?, Thread::VirtioPmem => virtio_pmem_thread_rules(), Thread::VirtioRng => virtio_rng_thread_rules(), Thread::VirtioVhostBlk => virtio_vhost_blk_thread_rules(), diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index 74f2dea4a..d76d15543 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -247,7 +247,7 @@ impl VirtioDevice for Net { mem: mem.clone(), kill_evt, pause_evt, - ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt), + ctrl_q: NetCtrl::new(cvq_queue, cvq_queue_evt, None), }; let paused = self.common.paused.clone();