diff --git a/.github/workflows/_rust.yml b/.github/workflows/_rust.yml index 6ea6291b4..6be2054a7 100644 --- a/.github/workflows/_rust.yml +++ b/.github/workflows/_rust.yml @@ -17,25 +17,6 @@ env: RUSTDOCFLAGS: "-D warnings" jobs: - bench: - name: bench-${{ matrix.runs-on }} - strategy: - fail-fast: false - matrix: - runs-on: [ - windows-2019, # Only platform with a benchmark right now - ] - runs-on: ${{ matrix.runs-on }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: ./.github/actions/setup-rust - id: setup-rust - - run: cargo bench ${{ steps.setup-rust.outputs.bench-packages }} - env: - RUST_LOG: "debug" - name: "cargo bench" - shell: bash - static-analysis: name: static-analysis-${{ matrix.runs-on }} strategy: diff --git a/rust/bin-shared/Cargo.toml b/rust/bin-shared/Cargo.toml index 1a4880828..c22d61042 100644 --- a/rust/bin-shared/Cargo.toml +++ b/rust/bin-shared/Cargo.toml @@ -70,7 +70,3 @@ tokio = { workspace = true, features = ["net", "time"] } [lints] workspace = true - -[[bench]] -name = "tunnel" -harness = false diff --git a/rust/bin-shared/benches/tunnel.rs b/rust/bin-shared/benches/tunnel.rs deleted file mode 100644 index 1b033b212..000000000 --- a/rust/bin-shared/benches/tunnel.rs +++ /dev/null @@ -1,169 +0,0 @@ -#![allow(clippy::unwrap_used)] - -use anyhow::Result; - -fn main() -> Result<()> { - let _guard = firezone_logging::test("debug"); - - let rt = tokio::runtime::Runtime::new()?; - rt.block_on(platform::perf())?; - Ok(()) -} - -#[cfg(not(target_os = "windows"))] -mod platform { - #[expect( - clippy::unused_async, - reason = "Must match signature of Windows platform" - )] - pub(crate) async fn perf() -> anyhow::Result<()> { - Ok(()) - } -} - -/// Synthetic performance test -/// -/// Echoes UDP packets between a local socket and the Wintun interface -#[cfg(target_os = "windows")] -mod platform { - use anyhow::Result; - use firezone_bin_shared::TunDeviceManager; - use std::{ - future::poll_fn, - net::{Ipv4Addr, Ipv6Addr}, - time::Duration, - }; - use tokio::{ - net::UdpSocket, - time::{Instant, timeout}, - }; - - pub(crate) async fn perf() -> Result<()> { - const MTU: usize = 1_280; - const NUM_REQUESTS: u64 = 1_000; - const REQ_CODE: u8 = 42; - const REQ_LEN: usize = 1_000; - const RESP_CODE: u8 = 43; - const SERVER_PORT: u16 = 3000; - const NUM_THREADS: usize = 1; // Note: Unused on Windows. - - let ipv4 = Ipv4Addr::from([100, 90, 215, 97]); - let ipv6 = Ipv6Addr::from([0xfd00, 0x2021, 0x1111, 0x0, 0x0, 0x0, 0x0016, 0x588f]); - let mut device_manager = TunDeviceManager::new(MTU, NUM_THREADS)?; - let mut tun = device_manager.make_tun()?; - - device_manager.set_ips(ipv4, ipv6).await?; - device_manager.set_routes(vec![ipv4.into()], vec![]).await?; - - let server_addr = (ipv4, SERVER_PORT).into(); - - // Listen for incoming packets on Wintun, and echo them. - let server_task = tokio::spawn(async move { - tracing::debug!("Server task entered"); - let mut requests_served = 0; - - let mut time_spent = Duration::from_millis(0); - loop { - let mut buf = Vec::with_capacity(1); - poll_fn(|cx| tun.poll_recv_many(cx, &mut buf, 1)).await; - let original_pkt = buf.remove(0); - - let start = Instant::now(); - let Some(original_udp) = original_pkt.as_udp() else { - continue; - }; - if original_udp.destination_port() != SERVER_PORT { - continue; - } - if original_udp.payload()[0] != REQ_CODE { - panic!("Wrong request code"); - } - - poll_fn(|cx| tun.poll_send_ready(cx)).await.unwrap(); - - tun.send( - ip_packet::make::udp_packet( - original_pkt.destination(), - original_pkt.source(), - original_udp.destination_port(), - original_udp.source_port(), - vec![RESP_CODE], - ) - .unwrap(), - )?; - requests_served += 1; - time_spent += start.elapsed(); - if requests_served >= NUM_REQUESTS { - break; - } - } - - tracing::info!(?time_spent, "Server all good"); - Ok::<_, anyhow::Error>(()) - }); - - // Wait for Wintun to be ready, then send it UDP packets and listen for - // the echo. - let client_task = tokio::spawn(async move { - // We'd like to hit 90 Mbps up which is nothing special but it's a good - // start. - // TODO: Investigate why we can't hit 100 anymore with the additional thread. - const EXPECTED_BITS_PER_SECOND: u64 = 80_000_000; - // This has to be an `Option` because Windows takes about 4 seconds - // to get the interface ready. - let mut start_instant = None; - - tracing::debug!("Client task entered"); - let sock = UdpSocket::bind("0.0.0.0:0").await?; - let mut responses_received = 0; - let mut req_buf = vec![0u8; REQ_LEN]; - req_buf[0] = REQ_CODE; - loop { - let Ok(_) = sock.send_to(&req_buf, server_addr).await else { - // It seems to take a few seconds for Windows to set everything up. - tracing::warn!("Failed to send"); - tokio::time::sleep(Duration::from_secs(1)).await; - continue; - }; - start_instant.get_or_insert_with(Instant::now); - let mut recv_buf = [0u8; MTU]; - let Ok((bytes_received, packet_src)) = sock.recv_from(&mut recv_buf).await else { - tracing::warn!("Timeout or couldn't recv packet"); - continue; - }; - if packet_src != server_addr { - tracing::warn!("Packet not from server"); - continue; - } - assert_eq!(bytes_received, 1); - assert_eq!(recv_buf[0], RESP_CODE); - responses_received += 1; - if responses_received >= NUM_REQUESTS { - break; - } - } - - let actual_dur = start_instant.unwrap().elapsed(); - // The 1_000_000 is needed to get decent precision without floats - let actual_bps = - NUM_REQUESTS * REQ_LEN as u64 * 8 * 1_000_000 / actual_dur.as_micros() as u64; - assert!( - actual_bps >= EXPECTED_BITS_PER_SECOND, - "{:?} < {:?}", - actual_bps, - EXPECTED_BITS_PER_SECOND - ); - tracing::info!(?actual_bps, "Client all good"); - Ok::<_, anyhow::Error>(()) - }); - - timeout(Duration::from_secs(30), async move { - client_task.await??; - server_task.await??; - Ok::<_, anyhow::Error>(()) - }) - .await??; - - Ok(()) - } -}