diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 156f92697b..f007f37dc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,9 +40,6 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@${{ env.NEXTEST_VERSION }} - - name: Set up tun - run: | - sudo ./litebox_platform_linux_userland/scripts/tun-setup.sh - name: Install iperf3 run: | sudo apt install -y iperf3 @@ -136,9 +133,6 @@ jobs: uses: taiki-e/install-action@v2 with: tool: nextest@${{ env.NEXTEST_VERSION }} - - name: Set up tun - run: | - sudo ./litebox_platform_linux_userland/scripts/tun-setup.sh - uses: Swatinem/rust-cache@v2 - name: Cache custom out directories uses: actions/cache@v5 diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ddc334fb73..f8d369aca1 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -34,6 +34,3 @@ jobs: - name: Set up Nextest run: | curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin - - name: Set up tun device for Linux userland testing - run: | - sudo ./litebox_platform_linux_userland/scripts/tun-setup.sh diff --git a/dev_bench/src/main.rs b/dev_bench/src/main.rs index 33c3cf2bb1..a769af28dc 100644 --- a/dev_bench/src/main.rs +++ b/dev_bench/src/main.rs @@ -6,6 +6,7 @@ use clap::Parser; use std::sync::atomic::Ordering::Relaxed; use std::{ collections::{BTreeMap, BTreeSet}, + net::{Ipv4Addr, TcpListener}, path::{Path, PathBuf}, sync::atomic::AtomicBool, time::Duration, @@ -695,6 +696,7 @@ fn run_rewritten_iperf3(ctx: BenchCtx<'_>) -> Result<()> { "cargo build -p litebox_runner_linux_userland {release...} {features...}" ) .run()?; + cmd!(sh, "cargo build -p litebox_broker_userland {release...}").run()?; } else { let mode = if release_mode { "release" } else { "debug" }; let iperf3_host = locate_command(sh, "iperf3")?; @@ -702,48 +704,121 @@ fn run_rewritten_iperf3(ctx: BenchCtx<'_>) -> Result<()> { "{}/target/{mode}/litebox_runner_linux_userland", project_root.display() ); + let broker = format!( + "{}/target/{mode}/litebox-broker-userland", + project_root.display() + ); let iperf3_rewritten = sh.current_dir().join("iperf3_rewritten"); - - // Spawn the sandboxed iperf3 server in a background thread so we can run the client - // from the host side. The server uses `-1` to exit after handling one client. - let server_handle = std::thread::spawn(move || -> Result<()> { - let sh = xshell::Shell::new()?; - cmd!( - sh, - "{runner} --unstable --env LD_LIBRARY_PATH=/lib64:/lib32:/lib --env HOME=/ --tun-device-name tun99 --initial-files {tar_file} {iperf3_rewritten} -s -1 -B 10.0.0.2" - ).run()?; - Ok(()) - }); - - // Retry the client connection until the server is ready, using a short - // connect-timeout so we don't waste time sleeping for a fixed duration. - debug!("Connecting iperf3 client to sandboxed server"); let client_sh = xshell::Shell::new()?; - let max_attempts = 50; - for attempt in 1..=max_attempts { - let result = cmd!( - client_sh, - "{iperf3_host} -c 10.0.0.2 --bytes 1G --connect-timeout 50" - ) - .quiet() - .ignore_stdout() - .ignore_stderr() - .run(); - if result.is_ok() { - break; + let max_server_attempts = 5; + let max_client_attempts = 50; + let mut last_failure = "sandboxed server did not become ready".to_owned(); + + for server_attempt in 1..=max_server_attempts { + let port = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))? + .local_addr()? + .port() + .to_string(); + let mut server_command = std::process::Command::new(&broker); + server_command + .arg("--runner") + .arg(&runner) + .arg("--") + .args([ + "--env", + "LD_LIBRARY_PATH=/lib64:/lib32:/lib", + "--env", + "HOME=/", + "--initial-files", + ]) + .arg(&tar_file) + .arg(&iperf3_rewritten) + .args(["-s", "-1", "-B", "127.0.0.1", "-p", &port]); + if COMMAND_EXECUTION_IS_QUIET.load(Relaxed) { + server_command + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()); } - if attempt == max_attempts { - return Err(anyhow!( - "iperf3 client failed to connect after {max_attempts} attempts" - )); + let mut server = server_command.spawn()?; + + debug!( + server_attempt, + "Connecting iperf3 client to sandboxed server" + ); + let mut client_succeeded = false; + for client_attempt in 1..=max_client_attempts { + let result = cmd!( + client_sh, + "{iperf3_host} -c 127.0.0.1 -p {port} --bytes 1G --connect-timeout 50" + ) + .quiet() + .ignore_stdout() + .ignore_stderr() + .run(); + if result.is_ok() { + client_succeeded = true; + break; + } + match server.try_wait() { + Ok(Some(status)) => { + last_failure = format!("sandboxed server exited with {status}"); + break; + } + Ok(None) => {} + Err(error) => { + let _ = server.kill(); + let _ = server.wait(); + return Err(error.into()); + } + } + if client_attempt == max_client_attempts { + last_failure = format!( + "iperf3 client failed to connect after {max_client_attempts} attempts" + ); + break; + } + debug!(client_attempt, "iperf3 client connection failed, retrying"); + std::thread::sleep(Duration::from_millis(100)); + } + + if !client_succeeded { + match server.try_wait() { + Ok(Some(_)) => {} + Ok(None) => { + let _ = server.kill(); + let _ = server.wait(); + } + Err(error) => { + let _ = server.kill(); + let _ = server.wait(); + return Err(error.into()); + } + } + debug!( + server_attempt, + %last_failure, + "Relaunching sandboxed iperf3 server" + ); + continue; + } + + let status = match server.wait() { + Ok(status) => status, + Err(error) => { + let _ = server.kill(); + let _ = server.wait(); + return Err(error.into()); + } + }; + if !status.success() { + return Err(anyhow!("sandboxed iperf3 server exited with {status}")); } - debug!(attempt, "iperf3 client connection failed, retrying"); - std::thread::sleep(Duration::from_millis(100)); + return Ok(()); } - server_handle - .join() - .map_err(|e| anyhow!("iperf3 server thread panicked: {e:?}"))??; + return Err(anyhow!( + "sandboxed iperf3 server failed after {max_server_attempts} attempts: {last_failure}" + )); } Ok(()) } diff --git a/litebox/src/net/errors.rs b/litebox/src/net/errors.rs index 1f3bdf3cf4..0b74ab1f22 100644 --- a/litebox/src/net/errors.rs +++ b/litebox/src/net/errors.rs @@ -21,6 +21,8 @@ use thiserror::Error; pub enum SocketError { #[error("Unsupported protocol {0}")] UnsupportedProtocol(u8), + #[error("Brokered networking is unavailable")] + BrokerUnavailable, #[error("Socket resources are exhausted")] ResourceExhausted, #[error("Socket creation was denied")] diff --git a/litebox/src/net/mod.rs b/litebox/src/net/mod.rs index 9b195af290..ff4aa3cb95 100644 --- a/litebox/src/net/mod.rs +++ b/litebox/src/net/mod.rs @@ -15,7 +15,7 @@ use crate::sync::RawSyncPrimitivesProvider; use crate::{LiteBox, platform, sync}; use bitflags::bitflags; -use smoltcp::socket::{icmp, raw, tcp, udp}; +use smoltcp::socket::{tcp, udp}; mod broker_socket; pub mod errors; @@ -56,6 +56,7 @@ const INTERFACE_IP_ADDR: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 2); /// IP address for the gateway // TODO: Make this configurable const GATEWAY_IP_ADDR: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 1); +const ICMP_PROTOCOL_NUMBER: u8 = 1; /// Maximum size of rx/tx buffers for sockets pub const SOCKET_BUFFER_SIZE: usize = 65536 * 4; @@ -77,6 +78,7 @@ pub enum ShutdownDirection { } /// Limits maximum number of packets in a buffer +#[cfg(test)] const MAX_PACKET_COUNT: usize = 32; /// TCP connection timeout. @@ -793,24 +795,37 @@ where ) } - /// Creates a socket. + /// Creates a broker-owned TCP or UDP socket. /// /// By default, the created socket has no associated proxy; to set a proxy, use /// [`attach_socket_proxy`](Self::attach_socket_proxy). pub fn socket(&mut self, protocol: Protocol) -> Result, SocketError> { let broker_socket = match (&protocol, self.litebox.broker_control()) { - (Protocol::Tcp, Some(broker)) => Some(BrokerSocket::Tcp( + (Protocol::Tcp, Some(broker)) => BrokerSocket::Tcp( BrokerTcpSocket::new(broker, self.litebox.broker_pollable_registry()) .map_err(SocketError::from)?, - )), - (Protocol::Udp, Some(broker)) => Some(BrokerSocket::Udp( + ), + (Protocol::Udp, Some(broker)) => BrokerSocket::Udp( BrokerUdpSocket::new(broker, self.litebox.broker_pollable_registry()) .map_err(SocketError::from)?, - )), - _ => None, + ), + (Protocol::Tcp | Protocol::Udp, None) => { + return Err(SocketError::BrokerUnavailable); + } + (Protocol::Icmp, _) => { + return Err(SocketError::UnsupportedProtocol(ICMP_PROTOCOL_NUMBER)); + } + (Protocol::Raw { protocol }, _) => { + return Err(SocketError::UnsupportedProtocol(*protocol)); + } }; + + Ok(self.new_socket_fd(protocol, None, Some(broker_socket))) + } + + #[cfg(test)] + fn local_socket(&mut self, protocol: Protocol) -> Result, SocketError> { let handle = match protocol { - Protocol::Tcp | Protocol::Udp if broker_socket.is_some() => None, Protocol::Tcp => Some(self.socket_set.add(tcp::Socket::new( smoltcp::storage::RingBuffer::new(vec![0u8; SOCKET_BUFFER_SIZE]), smoltcp::storage::RingBuffer::new(vec![0u8; SOCKET_BUFFER_SIZE]), @@ -825,42 +840,24 @@ where vec![0u8; SOCKET_BUFFER_SIZE], ), ))), - Protocol::Icmp => Some(self.socket_set.add(icmp::Socket::new( - smoltcp::storage::PacketBuffer::new( - vec![smoltcp::storage::PacketMetadata::EMPTY; MAX_PACKET_COUNT], - vec![0u8; SOCKET_BUFFER_SIZE], - ), - smoltcp::storage::PacketBuffer::new( - vec![smoltcp::storage::PacketMetadata::EMPTY; MAX_PACKET_COUNT], - vec![0u8; SOCKET_BUFFER_SIZE], - ), - ))), + Protocol::Icmp => { + return Err(SocketError::UnsupportedProtocol(ICMP_PROTOCOL_NUMBER)); + } Protocol::Raw { protocol } => { - // TODO: Should we maintain a specific allow-list of protocols for raw sockets? - // Should we allow everything except TCP/UDP/ICMP? Should we allow everything? These - // questions should be resolved; for now I am disallowing everything else. return Err(SocketError::UnsupportedProtocol(protocol)); - - #[expect( - unreachable_code, - reason = "currently raw is just directly disallowed; we might bring this code back in the future" - )] - Some(self.socket_set.add(raw::Socket::new( - smoltcp::wire::IpVersion::Ipv4, - smoltcp::wire::IpProtocol::from(protocol), - smoltcp::storage::PacketBuffer::new( - vec![smoltcp::storage::PacketMetadata::EMPTY; MAX_PACKET_COUNT], - vec![0u8; SOCKET_BUFFER_SIZE], - ), - smoltcp::storage::PacketBuffer::new( - vec![smoltcp::storage::PacketMetadata::EMPTY; MAX_PACKET_COUNT], - vec![0u8; SOCKET_BUFFER_SIZE], - ), - ))) } }; - Ok(self.new_socket_fd_for(SocketHandle { + Ok(self.new_socket_fd(protocol, handle, None)) + } + + fn new_socket_fd( + &mut self, + protocol: Protocol, + handle: Option, + broker_socket: Option>, + ) -> SocketFd { + self.new_socket_fd_for(SocketHandle { consider_closed: false, handle, broker_socket, @@ -878,7 +875,7 @@ where Protocol::Raw { protocol: _ } => unimplemented!(), }, proxy: None, - })) + }) } /// Creates a new [`SocketFd`] for a newly-created [`SocketHandle`]. diff --git a/litebox/src/net/tests.rs b/litebox/src/net/tests.rs index 9d37786e3a..e82dd127aa 100644 --- a/litebox/src/net/tests.rs +++ b/litebox/src/net/tests.rs @@ -13,7 +13,7 @@ extern crate std; fn bidi_tcp_comms(mut network: Network, comms: fn(&mut Network)) { // Create a listening socket let listener_fd = network - .socket(Protocol::Tcp) + .local_socket(Protocol::Tcp) .expect("Failed to create TCP socket"); let listen_addr = SocketAddr::V4(SocketAddrV4::from_str("10.0.0.2:8080").unwrap()); @@ -26,7 +26,7 @@ fn bidi_tcp_comms(mut network: Network, comms: fn(&mut Network break, + Ok(_) => { + clear_readiness(listener, ReadinessFlags::READ)?; + break; + } + Err(Errno::INTR) => {} + Err(error) => return Err(broker_error_from_errno(error)), + } + } let remote_address = SocketAddrV4::try_from(remote_address.ok_or(BrokerError::Internal)?) .map_err(|_| BrokerError::Internal)?; let local_address = local_socket_address(&socket)?; @@ -1526,6 +1542,19 @@ fn receive_socket_once( } Ok((_buffer, received)) => { data.truncate(received); + let terminal_readable = socket.read_shutdown + || socket + .snapshot + .lock() + .expect("Linux socket snapshot mutex poisoned") + .readiness + .contains(ReadinessFlags::HANGUP); + if !flags.contains(LinuxRecvFlags::PEEK) + && !terminal_readable + && ioctl_fionread(&socket.socket).map_err(broker_error_from_errno)? == 0 + { + clear_readiness(socket, ReadinessFlags::READ)?; + } return Ok(ReactorReceiveOutcome::Received(data)); } Err(Errno::INTR) => {} @@ -2280,6 +2309,12 @@ mod tests { Ok(SocketOutcome::Completed(ReceiveSocketResponse::Received(3))) ); assert_eq!(&received, b"ong"); + assert!( + !session + .check_readiness(handle) + .unwrap() + .contains(ReadinessFlags::READ) + ); assert_eq!( litebox_broker_core::socket::receive( &session, @@ -2365,6 +2400,12 @@ mod tests { Ok(SocketOutcome::Completed(ReceiveSocketResponse::Received(1))) ); assert_eq!(queued_after_shutdown, [b'x']); + assert!( + session + .check_readiness(read_shutdown_handle) + .unwrap() + .contains(ReadinessFlags::READ) + ); assert_eq!( litebox_broker_core::socket::receive( &session, @@ -2569,6 +2610,13 @@ mod tests { second.remote_address, socket_address_v4(second_client.local_addr().unwrap()) ); + assert!( + !session + .check_readiness(listener) + .unwrap() + .contains(ReadinessFlags::READ), + "accept must clear listener readiness after draining the queue" + ); assert!(matches!( litebox_broker_core::socket::accept(&session, listener, readiness.clone()), Err(BrokerError::WouldBlock) diff --git a/litebox_common_linux/src/errno/mod.rs b/litebox_common_linux/src/errno/mod.rs index 047eda2eee..cf2127f120 100644 --- a/litebox_common_linux/src/errno/mod.rs +++ b/litebox_common_linux/src/errno/mod.rs @@ -347,6 +347,7 @@ impl From for Errno { fn from(value: litebox::net::errors::SocketError) -> Self { match value { litebox::net::errors::SocketError::UnsupportedProtocol(_) => Errno::EPROTONOSUPPORT, + litebox::net::errors::SocketError::BrokerUnavailable => Errno::EAFNOSUPPORT, litebox::net::errors::SocketError::ResourceExhausted => Errno::ENOBUFS, litebox::net::errors::SocketError::PermissionDenied => Errno::EACCES, litebox::net::errors::SocketError::BackendFailure => Errno::EIO, diff --git a/litebox_runner_linux_userland/tests/net/tcp_client.c b/litebox_runner_linux_userland/tests/net/tcp_client.c deleted file mode 100644 index 9ad0cffe4f..0000000000 --- a/litebox_runner_linux_userland/tests/net/tcp_client.c +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_PORT 12345 -#define BUFFER_SIZE 1024 - -int main(int argc, char *argv[]) { - int client_fd; - struct sockaddr_in addr; - char recv_buf[BUFFER_SIZE]; - const char* ip_addr = "127.0.0.1"; - int port = DEFAULT_PORT; - - // Parse command line arguments - if (argc > 1) { - ip_addr = argv[1]; - } - if (argc > 2) { - port = atoi(argv[2]); - if (port <= 0 || port > 65535) { - fprintf(stderr, "Invalid port number. Using default: %d\n", DEFAULT_PORT); - port = DEFAULT_PORT; - } - } - - printf("===== TCP Client Test =====\n\n"); - - client_fd = socket(AF_INET, SOCK_STREAM, 0); - if (client_fd < 0) { - perror("socket failed"); - return 1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - - // Convert IP address string to binary form - if (inet_pton(AF_INET, ip_addr, &addr.sin_addr) <= 0) { - fprintf(stderr, "Invalid IP address: %s\n", ip_addr); - close(client_fd); - return 1; - } - - addr.sin_port = htons(port); - - printf("Client: Connecting to %s:%d...\n", ip_addr, port); - if (connect(client_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { - perror("connect failed"); - close(client_fd); - return 1; - } - - printf("Client: Connected\n"); - - const char* message = "Hello from TCP client!"; - if (send(client_fd, message, strlen(message), 0) < 0) { - perror("send failed"); - } else { - printf("Client: Sent '%s'\n", message); - } - - // Receive response - memset(recv_buf, 0, sizeof(recv_buf)); - ssize_t n = recv(client_fd, recv_buf, sizeof(recv_buf), 0); - if (n < 0) { - perror("recv failed"); - } else { - printf("Client: Received %zd bytes: '%s'\n", n, recv_buf); - } - - close(client_fd); - - printf("\n===== Client Test Complete =====\n"); - - return 0; -} \ No newline at end of file diff --git a/litebox_runner_linux_userland/tests/net/tcp_server.c b/litebox_runner_linux_userland/tests/net/tcp_server.c deleted file mode 100644 index 2e8b5c57aa..0000000000 --- a/litebox_runner_linux_userland/tests/net/tcp_server.c +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_PORT 12345 -#define BUFFER_SIZE 1024 - -int main(int argc, char *argv[]) { - int server_fd, conn_fd; - struct sockaddr_in addr, client_addr; - socklen_t client_len = sizeof(client_addr); - char recv_buf[BUFFER_SIZE]; - int opt = 1; - const char* ip_addr = "127.0.0.1"; - int port = DEFAULT_PORT; - - // Parse command line arguments - if (argc > 1) { - ip_addr = argv[1]; - } - if (argc > 2) { - port = atoi(argv[2]); - if (port <= 0 || port > 65535) { - fprintf(stderr, "Invalid port number. Using default: %d\n", DEFAULT_PORT); - port = DEFAULT_PORT; - } - } - - printf("===== TCP Server Test =====\n\n"); - - server_fd = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd < 0) { - perror("socket failed"); - return 1; - } - - // Allow address reuse - // if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - // perror("setsockopt failed"); - // close(server_fd); - // return 1; - // } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - - // Convert IP address string to binary form - if (inet_pton(AF_INET, ip_addr, &addr.sin_addr) <= 0) { - fprintf(stderr, "Invalid IP address: %s\n", ip_addr); - close(server_fd); - return 1; - } - - addr.sin_port = htons(port); - - if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { - perror("bind failed"); - close(server_fd); - return 1; - } - - if (listen(server_fd, 5) < 0) { - perror("listen failed"); - close(server_fd); - return 1; - } - - printf("Server: Listening on %s:%d...\n", ip_addr, port); - - conn_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); - if (conn_fd < 0) { - perror("accept failed"); - close(server_fd); - return 1; - } - - char client_ip[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN); - printf("Server: Client connected from %s:%d\n", client_ip, ntohs(client_addr.sin_port)); - - memset(recv_buf, 0, sizeof(recv_buf)); - ssize_t n = recv(conn_fd, recv_buf, sizeof(recv_buf), 0); - if (n < 0) { - perror("recv failed"); - } else { - printf("Server: Received %zd bytes: '%s'\n", n, recv_buf); - } - - // Send response back - const char* response = "Hello from TCP server!"; - if (send(conn_fd, response, strlen(response), 0) < 0) { - perror("send failed"); - } else { - printf("Server: Sent response\n"); - } - - close(conn_fd); - close(server_fd); - - printf("\n===== Server Test Complete =====\n"); - - return 0; -} \ No newline at end of file diff --git a/litebox_runner_linux_userland/tests/run.rs b/litebox_runner_linux_userland/tests/run.rs index b94d054934..16da35fd45 100644 --- a/litebox_runner_linux_userland/tests/run.rs +++ b/litebox_runner_linux_userland/tests/run.rs @@ -113,11 +113,6 @@ impl Runner { self } - fn tun_device_name(&mut self, tun_name: &str) -> &mut Self { - self.command.arg("--tun-device-name").arg(tun_name); - self - } - #[cfg(target_arch = "x86_64")] fn guest_program_path(&mut self, guest_path: &str) -> &mut Self { self.cmd_path = PathBuf::from(guest_path); @@ -396,16 +391,11 @@ fn spawn_test_broker( let control_ring = litebox_broker_transport::control_ring::ControlRing::new(control_memory) .expect("failed to attach broker test control ring"); - control_stream - .set_read_timeout(Some(BROKER_HELPER_TIMEOUT)) - .expect("failed to configure broker test read timeout"); - control_stream - .set_write_timeout(Some(BROKER_HELPER_TIMEOUT)) - .expect("failed to configure broker test write timeout"); + let setup_deadline = std::time::Instant::now() + BROKER_HELPER_TIMEOUT; let mut channel = litebox_broker_transport_linux_userland::unix_socket::UnixStreamHostSetupChannel::from_host_guaranteed( control_stream, - std::time::Instant::now() + BROKER_HELPER_TIMEOUT, + setup_deadline, ); let readiness = std::sync::Arc::new( litebox_broker_userland::readiness::ReadinessPublisherRuntime::new(), @@ -416,8 +406,8 @@ fn spawn_test_broker( &shared_buffers, readiness.clone(), |channel| { - channel.send_memfd(shared_buffers.memory(), None)?; - channel.send_memfd(control_ring.memory(), None) + channel.send_memfd(shared_buffers.memory(), Some(setup_deadline))?; + channel.send_memfd(control_ring.memory(), Some(setup_deadline)) }, ) .expect("broker host setup failed") @@ -1068,107 +1058,15 @@ fn test_runner_with_python_repl_pty() { ); } +#[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[test] -fn test_tun_with_tcp_socket() { - let tcp_server_path = PathBuf::from("./tests/net/tcp_server.c"); - let tcp_client_path = PathBuf::from("./tests/net/tcp_client.c"); - let unique_name = "tcp_server_exec_rewriter"; - let server_target = - common::compile(tcp_server_path.to_str().unwrap(), unique_name, true, false); - let client_target = common::compile( - tcp_client_path.to_str().unwrap(), - "tcp_client", - false, - false, - ); - - let child = std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_secs(2)); // wait for server to start - std::process::Command::new(client_target.to_str().unwrap()) - .arg("10.0.0.2") - .arg("12345") - .status() - .expect("failed to execute client"); - }); - Runner::new(&server_target, unique_name) - .arg("10.0.0.2") - .arg("12345") - .tun_device_name("tun99") - .run(); - child.join().unwrap(); -} - -/// Test network performance with iperf3 -/// -/// To run it with release build and see output, use: -/// ``` -/// cargo test --package litebox_runner_linux_userland --test run --release -- test_tun_and_runner_with_iperf3 --exact --nocapture -/// ``` -#[cfg(target_arch = "x86_64")] -#[test] -fn test_tun_and_runner_with_iperf3() { - const NUM_CLIENTS: usize = 1; - let iperf3_path = run_which("iperf3"); - let cloned_path = iperf3_path.clone(); - let has_started = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); - let has_started_clone = has_started.clone(); - std::thread::spawn(move || { - // Rewrite iperf3 and its dependencies may take some time, wait until it's done. - while !has_started_clone.load(std::sync::atomic::Ordering::Relaxed) { - std::thread::sleep(std::time::Duration::from_millis(100)); - } - std::println!("Connecting iperf3 client..."); - // Retry with a short connect-timeout instead of a fixed sleep, so we - // start the transfer as soon as the server is actually listening. - let mut connected = false; - for attempt in 1..=50 { - let status = std::process::Command::new(&cloned_path) - .args([ - "-c", - "10.0.0.2", - "-P", - NUM_CLIENTS.to_string().leak(), - "--connect-timeout", - "50", - "--time", - "1", - ]) - .status() - .expect("Failed to start iperf3 client"); - if status.success() { - connected = true; - break; - } - std::eprintln!("iperf3 client attempt {attempt} failed, retrying"); - std::thread::sleep(std::time::Duration::from_millis(100)); - } - assert!( - connected, - "iperf3 client failed to connect after 50 attempts" - ); - }); - let mut runner = Runner::new(&iperf3_path, "iperf3_server_rewriter"); - runner - .args([ - "-s", // run in server mode - "-1", // handle one client then exit - "-B", "10.0.0.2", // bind to this address - ]) - .tun_device_name("tun99"); - has_started.store(true, std::sync::atomic::Ordering::Relaxed); - runner.run(); -} - -#[cfg(target_arch = "x86_64")] -#[test] -fn test_tun_with_curl() { +fn test_broker_with_curl() { use std::io::{Read, Write}; - use std::net::TcpListener; + use std::net::{Ipv4Addr, TcpListener}; const RESPONSE_BODY: &str = "#!/bin/bash\necho 'Hello from litebox!'\n"; - // Bind to an OS-assigned port on all interfaces. - let listener = TcpListener::bind("0.0.0.0:0").expect("Failed to bind HTTP server"); + let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).expect("Failed to bind HTTP server"); let port = listener.local_addr().unwrap().port(); let server_thread = std::thread::spawn(move || { @@ -1189,18 +1087,168 @@ fn test_tun_with_curl() { }); let curl_path = run_which("curl"); - let url = format!("http://10.0.0.1:{port}/something"); + let control_socket_path = unique_test_socket_path("runner-broker-curl-control"); + let broker = spawn_test_broker( + &control_socket_path, + litebox_broker_core::PolicyEngine::with_host_guaranteed_rights( + litebox_broker_core::ObjectRights::all(), + ) + .with_socket_policy(litebox_broker_core::SocketPolicy::Ipv4Loopback), + 1, + ); + let url = format!("http://127.0.0.1:{port}/something"); let output = Runner::new(&curl_path, "curl_rewriter") .args(["-sS", &url]) - .tun_device_name("tun99") + .broker_socket(&control_socket_path) .output(); server_thread.join().expect("Server thread panicked"); + assert!(broker.next_close_object_count() > 0); + broker.join(); let output_str = String::from_utf8_lossy(&output); assert!(output_str.contains(RESPONSE_BODY), "Unexpected curl output"); } +/// Exercises sustained brokered TCP traffic and backpressure with iperf3. +/// +/// To run it with a release build and see output: +/// ``` +/// cargo test --package litebox_runner_linux_userland --test run --release -- test_broker_with_iperf3 --exact --nocapture +/// ``` +#[cfg(all(target_arch = "x86_64", target_os = "linux"))] +#[test] +fn test_broker_with_iperf3() { + use std::io::{BufRead, BufReader}; + use std::net::{Ipv4Addr, TcpListener}; + use std::process::Stdio; + + const IPERF_TEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); + + let iperf3_path = run_which("iperf3"); + let control_socket_path = unique_test_socket_path("runner-broker-iperf3-control"); + let broker = spawn_test_broker( + &control_socket_path, + litebox_broker_core::PolicyEngine::with_host_guaranteed_rights( + litebox_broker_core::ObjectRights::all(), + ) + .with_socket_policy(litebox_broker_core::SocketPolicy::Ipv4Loopback), + 1, + ); + + let mut last_server_output = String::new(); + let mut started_server = None; + for _ in 0..5 { + let port = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)) + .expect("failed to reserve iperf3 port") + .local_addr() + .unwrap() + .port(); + let mut server = std::process::Command::new(&iperf3_path) + .args([ + "-s", + "-1", + "--forceflush", + "-B", + "127.0.0.1", + "-p", + &port.to_string(), + ]) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .expect("failed to start iperf3 server"); + let server_stdout = server.stdout.take().unwrap(); + let (listening_tx, listening_rx) = std::sync::mpsc::channel(); + let server_output = std::thread::spawn(move || { + let mut stdout = BufReader::new(server_stdout); + let mut output = String::new(); + let mut listening_reported = false; + loop { + let mut line = String::new(); + if stdout + .read_line(&mut line) + .expect("failed to read iperf3 server output") + == 0 + { + break; + } + output.push_str(&line); + if !listening_reported && line.contains("Server listening") { + listening_reported = true; + let _ = listening_tx.send(()); + } + } + output + }); + if listening_rx.recv_timeout(BROKER_HELPER_TIMEOUT).is_ok() { + started_server = Some((port, server, server_output)); + break; + } + let _ = server.kill(); + let _ = server.wait(); + last_server_output = server_output.join().unwrap(); + } + let (port, mut server, server_output) = started_server + .unwrap_or_else(|| panic!("iperf3 server did not start; output:\n{last_server_output}")); + + let mut runner = Runner::new(&iperf3_path, "broker_iperf3_client_rewriter"); + runner + .args([ + "-c", + "127.0.0.1", + "-p", + &port.to_string(), + "--connect-timeout", + "1000", + "--bytes", + "1M", + ]) + .broker_socket(&control_socket_path); + + let mut guest = runner.spawn_with_stdio(Stdio::null(), Stdio::inherit(), Stdio::inherit()); + let deadline = std::time::Instant::now() + IPERF_TEST_TIMEOUT; + let mut guest_status = None; + let mut server_status = None; + while guest_status.is_none() || server_status.is_none() { + if guest_status.is_none() { + guest_status = guest.try_wait().expect("failed to wait for iperf3 guest"); + } + if server_status.is_none() { + server_status = server.try_wait().expect("failed to wait for iperf3 server"); + } + if guest_status.is_some() && server_status.is_some() { + break; + } + if std::time::Instant::now() >= deadline { + if guest_status.is_none() { + guest.kill().expect("failed to stop iperf3 guest"); + let _ = guest.wait(); + } + if server_status.is_none() { + server.kill().expect("failed to stop iperf3 server"); + let _ = server.wait(); + } + let output = server_output.join().unwrap(); + panic!("iperf3 test did not finish; server output:\n{output}"); + } + std::thread::sleep(std::time::Duration::from_millis(20)); + } + let guest_status = guest.wait().expect("failed to reap iperf3 guest"); + let server_status = server.wait().expect("failed to reap iperf3 server"); + let server_output = server_output.join().unwrap(); + assert!( + guest_status.success(), + "iperf3 guest failed; server output:\n{server_output}" + ); + assert!( + server_status.success(), + "iperf3 server failed; output:\n{server_output}" + ); + assert!(broker.next_close_object_count() > 0); + broker.join(); +} + #[cfg(target_arch = "x86_64")] #[test] fn test_shebang() { diff --git a/litebox_shim_linux/src/syscalls/net.rs b/litebox_shim_linux/src/syscalls/net.rs index d0ea018534..e365dc110d 100644 --- a/litebox_shim_linux/src/syscalls/net.rs +++ b/litebox_shim_linux/src/syscalls/net.rs @@ -2865,7 +2865,7 @@ mod tests { use litebox::utils::TruncateExt as _; use litebox_common_linux::{ AddressFamily, MapFlags, ProtFlags, ReceiveFlags, SendFlags, SockFlags, SockType, - SocketOption, SocketOptionName, TcpOption, errno::Errno, + SocketOption, SocketOptionName, errno::Errno, }; use zerocopy::FromZeros as _; @@ -2887,11 +2887,24 @@ mod tests { == core::mem::size_of::() ); - const TUN_IP_ADDR: [u8; 4] = [10, 0, 0, 2]; - const TUN_IP_ADDR_STR: &str = "10.0.0.2"; - const TUN_DEVICE_NAME: &str = "tun99"; - const SERVER_PORT: u16 = 8080; - const CLIENT_PORT: u16 = 8081; + const LOOPBACK_IP_ADDR: [u8; 4] = [127, 0, 0, 1]; + const LOOPBACK_IP_ADDR_STR: &str = "127.0.0.1"; + + fn find_free_tcp_port() -> u16 { + std::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, 0)) + .expect("failed to reserve TCP port") + .local_addr() + .unwrap() + .port() + } + + fn find_free_udp_port() -> u16 { + std::net::UdpSocket::bind((std::net::Ipv4Addr::LOCALHOST, 0)) + .expect("failed to reserve UDP port") + .local_addr() + .unwrap() + .port() + } fn close_socket(task: &TestTask, fd: u32) { task.sys_close(i32::try_from(fd).unwrap()) @@ -2899,6 +2912,7 @@ mod tests { } #[test] + #[ignore = "requires broker-backed socket test setup"] fn dropping_inet_socket_pin_reaps_deferred_close() { let task = init_platform(None); let fd = task @@ -2925,6 +2939,7 @@ mod tests { } #[test] + #[ignore = "requires broker-backed socket test setup"] fn socket_io_pin_keeps_backend_alive_for_send_after_close() { let task = init_platform(None); let fd = task @@ -2966,6 +2981,7 @@ mod tests { } #[test] + #[ignore = "requires broker-backed socket test setup"] fn raw_inet_socket_pin_does_not_follow_dup2_replacement() { let task = init_platform(None); let old_fd = task @@ -3080,6 +3096,7 @@ mod tests { } #[test] + #[ignore = "requires broker-backed socket test setup"] fn inet_socket_returns_emfile_at_raw_fd_limit() { let task = init_platform(None); let fd = task @@ -3174,22 +3191,21 @@ mod tests { let buf = "Hello, world!"; let child_handle = std::thread::spawn(move || { std::thread::sleep(core::time::Duration::from_millis(200)); // Give server time to start + let port = port.to_string(); match option { "sendto" | "sendmsg" => std::process::Command::new("nc") .args([ "-w", // timeout for connects and final net reads "1", - TUN_IP_ADDR_STR, - SERVER_PORT.to_string().as_str(), + LOOPBACK_IP_ADDR_STR, + &port, ]) .stdout(std::process::Stdio::piped()) .output(), "recvfrom" | "recvmsg" => std::process::Command::new("sh") .args([ "-c", - &alloc::format!( - "echo -n '{buf}' | nc -w 1 {TUN_IP_ADDR_STR} {SERVER_PORT}", - ), + &alloc::format!("echo -n '{buf}' | nc -w 1 {LOOPBACK_IP_ADDR_STR} {port}"), ]) .output(), _ => panic!("Unknown option"), @@ -3224,7 +3240,7 @@ mod tests { let super::SocketAddress::Inet(SocketAddr::V4(remote_addr)) = remote_addr else { panic!("Expected IPv4 address"); }; - assert_eq!(remote_addr.ip().octets(), [10, 0, 0, 1]); + assert_eq!(remote_addr.ip().octets(), LOOPBACK_IP_ADDR); assert_ne!(remote_addr.port(), 0); match option { @@ -3339,20 +3355,20 @@ mod tests { close_socket(task, server); } - fn test_tcp_socket_with_external_client(port: u16, is_nonblocking: bool, test_trunc: bool) { - let task = init_platform(Some(TUN_DEVICE_NAME)); + fn test_tcp_socket_with_external_client(is_nonblocking: bool, test_trunc: bool) { + let task = init_platform(None); test_tcp_socket_as_server( &task, - TUN_IP_ADDR, - port, + LOOPBACK_IP_ADDR, + find_free_tcp_port(), is_nonblocking, test_trunc, "recvfrom", ); test_tcp_socket_as_server( &task, - TUN_IP_ADDR, - port, + LOOPBACK_IP_ADDR, + find_free_tcp_port(), is_nonblocking, test_trunc, "recvmsg", @@ -3360,19 +3376,19 @@ mod tests { } fn test_tcp_socket_send(is_nonblocking: bool, test_trunc: bool) { - let task = init_platform(Some(TUN_DEVICE_NAME)); + let task = init_platform(None); test_tcp_socket_as_server( &task, - TUN_IP_ADDR, - SERVER_PORT, + LOOPBACK_IP_ADDR, + find_free_tcp_port(), is_nonblocking, test_trunc, "sendto", ); test_tcp_socket_as_server( &task, - TUN_IP_ADDR, - SERVER_PORT, + LOOPBACK_IP_ADDR, + find_free_tcp_port(), is_nonblocking, test_trunc, "sendmsg", @@ -3380,33 +3396,40 @@ mod tests { } #[test] - fn test_tun_blocking_send_tcp_socket() { + #[ignore = "requires broker-backed socket test setup"] + fn test_blocking_send_tcp_socket() { test_tcp_socket_send(false, false); } #[test] - fn test_tun_nonblocking_send_tcp_socket() { + #[ignore = "requires broker-backed socket test setup"] + fn test_nonblocking_send_tcp_socket() { test_tcp_socket_send(true, false); } #[test] - fn test_tun_blocking_recvfrom_tcp_socket() { - test_tcp_socket_with_external_client(SERVER_PORT, false, false); + #[ignore = "requires broker-backed socket test setup"] + fn test_blocking_recvfrom_tcp_socket() { + test_tcp_socket_with_external_client(false, false); } #[test] - fn test_tun_nonblocking_recvfrom_tcp_socket() { - test_tcp_socket_with_external_client(SERVER_PORT, true, false); + #[ignore = "requires broker-backed socket test setup"] + fn test_nonblocking_recvfrom_tcp_socket() { + test_tcp_socket_with_external_client(true, false); } #[test] - fn test_tun_blocking_recvfrom_tcp_socket_with_truncation() { - test_tcp_socket_with_external_client(SERVER_PORT, false, true); + #[ignore = "requires broker-backed socket test setup"] + fn test_blocking_recvfrom_tcp_socket_with_truncation() { + test_tcp_socket_with_external_client(false, true); } #[test] - fn test_tun_tcp_connection_refused() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_tcp_connection_refused() { + let task = init_platform(None); + let port = find_free_tcp_port(); let socket_fd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); @@ -3419,8 +3442,8 @@ mod tests { .do_connect( socket_fd2, SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( - core::net::Ipv4Addr::from([10, 0, 0, 1]), - SERVER_PORT, + core::net::Ipv4Addr::from(LOOPBACK_IP_ADDR), + port, ))), ) .unwrap_err(); @@ -3434,18 +3457,15 @@ mod tests { } #[test] - fn test_tun_tcp_socket_as_client() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_tcp_socket_as_client() { + let task = init_platform(None); + let port = find_free_tcp_port(); - let child_handle = std::thread::spawn(|| { + let child_handle = std::thread::spawn(move || { + let port = port.to_string(); std::process::Command::new("nc") - .args([ - "-w", - "1", - "-l", - "10.0.0.1", - SERVER_PORT.to_string().as_str(), - ]) + .args(["-w", "1", "-l", LOOPBACK_IP_ADDR_STR, &port]) .output() }); std::thread::sleep(core::time::Duration::from_secs(1)); @@ -3456,8 +3476,8 @@ mod tests { .expect("failed to create client socket"); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( - core::net::Ipv4Addr::from([10, 0, 0, 1]), - SERVER_PORT, + core::net::Ipv4Addr::from(LOOPBACK_IP_ADDR), + port, ))); task.do_connect(client_fd, server_addr) .expect("failed to connect to server"); @@ -3473,19 +3493,6 @@ mod tests { .unwrap(); assert_eq!(n, buf.len()); - let linger = litebox_common_linux::Linger { - onoff: 1, // enable linger - linger: 60, // timeout in seconds - }; - let optval = UserPtr::from_usize((&raw const linger).cast::() as usize); - task.do_setsockopt( - client_fd, - SocketOptionName::Socket(SocketOption::LINGER), - optval, - core::mem::size_of::(), - ) - .expect("Failed to set SO_LINGER"); - close_socket(&task, client_fd); let output = child_handle @@ -3503,6 +3510,9 @@ mod tests { is_nonblocking: bool, op: &str, ) { + let server_port = find_free_udp_port(); + let client_port = find_free_udp_port(); + // Server socket and bind let server_fd = task .do_socket( @@ -3517,8 +3527,8 @@ mod tests { ) .expect("failed to create server socket"); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( - core::net::Ipv4Addr::from(TUN_IP_ADDR), - SERVER_PORT, + core::net::Ipv4Addr::from(LOOPBACK_IP_ADDR), + server_port, ))); task.do_bind(server_fd, server_addr.clone()) .expect("failed to bind server"); @@ -3542,9 +3552,9 @@ mod tests { "-q", // quit after EOF on stdin and delay of secs "1", "-p", // Specify local port for remote connects - CLIENT_PORT.to_string().as_str(), - TUN_IP_ADDR_STR, - SERVER_PORT.to_string().as_str(), + client_port.to_string().as_str(), + LOOPBACK_IP_ADDR_STR, + server_port.to_string().as_str(), ]) .stdin(std::process::Stdio::piped()) .spawn() @@ -3636,7 +3646,7 @@ mod tests { let SocketAddress::Inet(sender_addr) = sender_addr.unwrap() else { panic!("Expected Inet socket address"); }; - assert_eq!(sender_addr.port(), CLIENT_PORT); + assert_eq!(sender_addr.port(), client_port); close_socket(task, server_fd); @@ -3644,32 +3654,35 @@ mod tests { } #[test] - fn test_tun_blocking_udp_server_socket() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_blocking_udp_server_socket() { + let task = init_platform(None); blocking_udp_server_socket(&task, false, false, false, "recvfrom"); blocking_udp_server_socket(&task, false, false, false, "recvmsg"); } #[test] - fn test_tun_nonblocking_udp_server_socket() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_nonblocking_udp_server_socket() { + let task = init_platform(None); blocking_udp_server_socket(&task, false, false, true, "recvfrom"); blocking_udp_server_socket(&task, false, false, true, "recvmsg"); } #[test] - fn test_tun_blocking_udp_server_socket_with_truncation() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_blocking_udp_server_socket_with_truncation() { + let task = init_platform(None); blocking_udp_server_socket(&task, true, true, false, "recvfrom"); blocking_udp_server_socket(&task, true, true, false, "recvmsg"); blocking_udp_server_socket(&task, true, false, false, "recvmsg"); } #[test] - fn test_tun_udp_client_socket_without_server() { - // We do not support loopback yet, so this test only checks that - // the client can send packets without a server. - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_udp_client_socket_without_server() { + let task = init_platform(None); + let server_port = find_free_udp_port(); // Client socket and explicit bind let client_fd = task @@ -3683,7 +3696,7 @@ mod tests { let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([127, 0, 0, 1]), - SERVER_PORT, + server_port, ))); // Send from client to server @@ -3717,46 +3730,13 @@ mod tests { } #[test] - fn test_tun_tcp_sockopt() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_tcp_keepalive_sockopt() { + let task = init_platform(None); let sockfd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); - let mut congestion_name = [0u8; 16]; - let optlen = task - .do_getsockopt( - sockfd, - SocketOptionName::TCP(TcpOption::CONGESTION), - UserPtrMut::from_usize(congestion_name.as_mut_ptr() as usize), - congestion_name.len().trunc(), - ) - .expect("Failed to get TCP_CONGESTION"); - assert_eq!(optlen, 4); - assert_eq!( - core::str::from_utf8(&congestion_name[..optlen]).unwrap(), - "none" - ); - - task.do_setsockopt( - sockfd, - SocketOptionName::TCP(TcpOption::CONGESTION), - UserPtr::from_usize(congestion_name.as_ptr() as usize), - optlen, - ) - .expect("Failed to set TCP_CONGESTION"); - - let congestion_name = b"cubic\0"; - let err = task - .do_setsockopt( - sockfd, - SocketOptionName::TCP(TcpOption::CONGESTION), - UserPtr::from_usize(congestion_name.as_ptr() as usize), - congestion_name.len(), - ) - .unwrap_err(); - assert_eq!(err, Errno::EINVAL); - let val: u32 = 1; let optval = UserPtr::from_usize((&raw const val).cast::() as usize); task.do_setsockopt( @@ -3780,37 +3760,11 @@ mod tests { .expect("failed to get SO_KEEPALIVE"); assert_eq!(len, core::mem::size_of::()); assert_eq!(result, 1); - } - - #[ignore = "timeout is 75s"] - #[test] - fn test_tun_tcp_so_error_network_unreachable() { - let task = init_platform(Some(TUN_DEVICE_NAME)); - let sockfd = task - .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) - .expect("failed to create socket"); - - // Connect to an off-subnet IP (TEST-NET, 192.0.2.1). - // smoltcp does not report errors when route table lookup fails. Instead, it just dicards the packets. - // Our current implementation returns `ETIMEDOUT` instead of `ENETUNREACH`. - let err = task - .do_connect( - sockfd, - SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( - core::net::Ipv4Addr::from([192, 0, 2, 1]), - SERVER_PORT, - ))), - ) - .unwrap_err(); - assert_eq!(err, Errno::ETIMEDOUT); - - let so_err = get_so_error(&task, sockfd); - assert_eq!(so_err, i32::from(Errno::ETIMEDOUT).cast_unsigned()); - close_socket(&task, sockfd); } #[test] + #[ignore = "requires broker-backed socket test setup"] fn test_socket_dup_and_close() { let task = init_platform(None); let socket_fd = task diff --git a/litebox_shim_linux/src/transport.rs b/litebox_shim_linux/src/transport.rs index a24017e535..7be8c91da6 100644 --- a/litebox_shim_linux/src/transport.rs +++ b/litebox_shim_linux/src/transport.rs @@ -148,8 +148,6 @@ mod tests { use super::*; - const TUN_DEVICE_NAME: &str = "tun99"; - fn find_free_port() -> u16 { let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind to port 0"); listener.local_addr().unwrap().port() @@ -267,7 +265,7 @@ mod tests { crate::syscalls::tests::TestPlatform, ShimTransport, > { - let addr = socket_addr([10, 0, 0, 1], server.port); + let addr = socket_addr([127, 0, 0, 1], server.port); let transport = ShimTransport::connect(task.global.clone(), addr) .expect("failed to connect to 9P server via shim network"); @@ -281,12 +279,13 @@ mod tests { } // ----------------------------------------------------------------------- - // Tests (require TUN device + diod) + // Tests (require broker-backed socket setup + diod) // ----------------------------------------------------------------------- #[test] - fn test_tun_nine_p_create_and_read_file() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_nine_p_create_and_read_file() { + let task = init_platform(None); let server = DiodServer::start(); let fs = connect_9p(&task, &server); @@ -319,8 +318,9 @@ mod tests { } #[test] - fn test_tun_nine_p_host_files_visible() { - let task = init_platform(Some(TUN_DEVICE_NAME)); + #[ignore = "requires broker-backed socket test setup"] + fn test_nine_p_host_files_visible() { + let task = init_platform(None); let server = DiodServer::start();