diff --git a/Cargo.lock b/Cargo.lock index 81dfb0c..ec47a1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2753,9 +2753,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vmnet" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96eae216a9fa27c8e458ee5ef56dfc92d43fc8c25d67ade4a7fa83f0cb8b21b" +checksum = "4d88878a5583a43715c9f6de9b4cf94bc1af1dfec2a6abd75fd821de623f693b" dependencies = [ "bitflags 1.3.2", "block", @@ -2771,9 +2771,9 @@ dependencies = [ [[package]] name = "vmnet-derive" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa20b8d2b5b0504355848b99b54fda9411f8201f44c1a9ee5a2d3c3134d31297" +checksum = "9ee4569490df90dfa2cfeda38230905b2331e72a16fa62a8f5d31069ea024db2" dependencies = [ "darling", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index fccfbb4..f34f345 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ smoltcp = "0" libc = "0" polling = "3" dhcproto = { git = "https://github.com/bluecatengineering/dhcproto.git", branch = "master" } -vmnet = "0" +vmnet = "0.5.0" clap = { version = "4", features = ["derive"] } mac_address = "1" privdrop = "0" diff --git a/lib/host.rs b/lib/host.rs index c8fcb02..7b2ee66 100644 --- a/lib/host.rs +++ b/lib/host.rs @@ -9,7 +9,7 @@ use std::sync::mpsc::{SyncSender, sync_channel}; use vmnet::mode::Mode; use vmnet::parameters::{Parameter, ParameterKind}; use vmnet::port_forwarding::{AddressFamily, Protocol}; -use vmnet::{Events, Options}; +use vmnet::{Batch, Events, Options}; #[derive(ValueEnum, Clone, Debug)] pub enum NetType { @@ -29,6 +29,7 @@ pub struct Host { callback_can_continue_tx: SyncSender<()>, pub gateway_ip: smoltcp::wire::Ipv4Address, pub max_packet_size: u64, + pub read_max_packets: u64, finalized: bool, } @@ -67,6 +68,15 @@ impl Host { )); }; + // Retrieve read max packets for this interface + let Some(Parameter::ReadMaxPackets(read_max_packets)) = + interface.parameters().get(ParameterKind::ReadMaxPackets) + else { + return Err(anyhow!( + "failed to retrieve vmnet's interface read max packets" + )); + }; + // Set up a socketpair() to emulate polling of the vmnet interface let (new_packets_tx, new_packets_rx) = UnixDatagram::pair()?; new_packets_rx.set_nonblocking(true)?; @@ -96,6 +106,7 @@ impl Host { callback_can_continue_tx, gateway_ip, max_packet_size, + read_max_packets, finalized: false, }) } @@ -133,14 +144,14 @@ impl Host { .map_err(|err| anyhow!("failed to remove port forwarding rule {details}: {err}")) } - pub fn read(&mut self, buf: &mut [u8]) -> vmnet::Result { + pub fn read(&mut self, batch: &mut Batch, bufs: &mut [Vec]) -> vmnet::Result { // Dequeue dummy datagram from the socket (if any) // to free up buffer space and reduce false-positives // when polling let mut buf_to_be_discarded: [u8; 1] = [0; 1]; let _ = self.new_packets_rx.recv(&mut buf_to_be_discarded); - let result = self.interface.read(buf); + let result = self.interface.read_batch(batch, bufs); if let Err(vmnet::Error::VmnetReadNothing) = result { // We've emptied everything, unlock the callback diff --git a/lib/proxy/mod.rs b/lib/proxy/mod.rs index 39f0347..e5f6655 100644 --- a/lib/proxy/mod.rs +++ b/lib/proxy/mod.rs @@ -18,6 +18,7 @@ use prefix_trie::{Prefix, PrefixMap, PrefixSet}; use smoltcp::wire::EthernetFrame; use std::io::ErrorKind; use std::os::unix::io::{AsRawFd, RawFd}; +use vmnet::Batch; pub struct Proxy<'proxy> { vm: VM, @@ -76,8 +77,16 @@ impl Proxy<'_> { } pub fn run(&mut self) -> Result<()> { + // Create a single buffer from reading from the VM let mut buf: Vec = vec![0; self.host.max_packet_size as usize]; + // Create multiple buffers and a batch for reading from the host + let mut bufs = vec![ + vec![0u8; self.host.max_packet_size as usize]; + self.host.read_max_packets as usize + ]; + let mut batch = Batch::preallocate(bufs.len()); + self.poller.arm()?; loop { @@ -88,7 +97,7 @@ impl Proxy<'_> { } if host_readable { - self.read_from_host(buf.as_mut_slice())?; + self.read_from_host(&mut batch, &mut bufs)?; } // Graceful termination @@ -125,12 +134,14 @@ impl Proxy<'_> { } } - fn read_from_host(&mut self, buf: &mut [u8]) -> Result<()> { + fn read_from_host(&mut self, batch: &mut Batch, bufs: &mut [Vec]) -> Result<()> { loop { - match self.host.read(buf) { - Ok(n) => { - if let Ok(pkt) = EthernetFrame::new_checked(&buf[..n]) { - self.process_frame_from_host(&pkt)?; + match self.host.read(batch, bufs) { + Ok(pktcnt) => { + for buf in batch.packet_sized_bufs(bufs).take(pktcnt) { + if let Ok(pkt) = EthernetFrame::new_checked(buf) { + self.process_frame_from_host(&pkt)?; + } } } Err(err) => {