From 803de68fdcfe4097e78920e8545b2947d04dd6a7 Mon Sep 17 00:00:00 2001 From: Momics Date: Mon, 20 Jul 2026 14:42:21 +0200 Subject: [PATCH 1/2] fix(core): disable QUIC GSO on Android --- crates/iroh-http-core/src/endpoint/bind.rs | 45 +++++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/crates/iroh-http-core/src/endpoint/bind.rs b/crates/iroh-http-core/src/endpoint/bind.rs index 41461d45..f025637f 100644 --- a/crates/iroh-http-core/src/endpoint/bind.rs +++ b/crates/iroh-http-core/src/endpoint/bind.rs @@ -25,6 +25,16 @@ use super::{ EndpointInner, IrohEndpoint, }; +/// Whether QUIC may batch UDP datagrams using segmentation offload. +/// +/// Android kernels can advertise this capability and then reject batched sends. +/// Until noq retries rejected batches as individual datagrams, disabling the +/// optimization avoids losing the affected packets on Android. Remove this +/// policy after is released. +fn segmentation_offload_enabled(target_os: &str) -> bool { + target_os != "android" +} + impl IrohEndpoint { /// Bind an Iroh endpoint with the supplied options. pub async fn bind(opts: NodeOptions) -> Result { @@ -142,21 +152,16 @@ impl IrohEndpoint { builder = builder.secret_key(SecretKey::from_bytes(&key_bytes)); } + let mut transport = QuicTransportConfig::builder() + .max_concurrent_bidi_streams(128u32.into()) + .enable_segmentation_offload(segmentation_offload_enabled(std::env::consts::OS)); if let Some(ms) = opts.networking.idle_timeout_ms { let timeout = IdleTimeout::try_from(Duration::from_millis(ms)).map_err(|e| { crate::CoreError::invalid_input(format!("idle_timeout_ms out of range: {e}")) })?; - let transport = QuicTransportConfig::builder() - .max_idle_timeout(Some(timeout)) - .max_concurrent_bidi_streams(128u32.into()) - .build(); - builder = builder.transport_config(transport); - } else { - let transport = QuicTransportConfig::builder() - .max_concurrent_bidi_streams(128u32.into()) - .build(); - builder = builder.transport_config(transport); + transport = transport.max_idle_timeout(Some(timeout)); } + builder = builder.transport_config(transport.build()); // Bind address(es). for addr_str in &opts.networking.bind_addrs { @@ -567,3 +572,23 @@ mod direct_addr_tests { } } } + +#[cfg(test)] +mod transport_policy_tests { + use super::segmentation_offload_enabled; + + #[test] + fn android_disables_udp_segmentation_offload() { + assert!(!segmentation_offload_enabled("android")); + } + + #[test] + fn non_android_platforms_keep_udp_segmentation_offload() { + for target in ["ios", "macos", "linux", "windows"] { + assert!( + segmentation_offload_enabled(target), + "{target} should retain the upstream default" + ); + } + } +} From b9e7a507805d28e8f067e58952e01e045f79fc2d Mon Sep 17 00:00:00 2001 From: Momics Date: Mon, 20 Jul 2026 15:39:50 +0200 Subject: [PATCH 2/2] docs(core): link Android GSO removal tracker --- crates/iroh-http-core/src/endpoint/bind.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/iroh-http-core/src/endpoint/bind.rs b/crates/iroh-http-core/src/endpoint/bind.rs index f025637f..e04a40d7 100644 --- a/crates/iroh-http-core/src/endpoint/bind.rs +++ b/crates/iroh-http-core/src/endpoint/bind.rs @@ -29,8 +29,10 @@ use super::{ /// /// Android kernels can advertise this capability and then reject batched sends. /// Until noq retries rejected batches as individual datagrams, disabling the -/// optimization avoids losing the affected packets on Android. Remove this -/// policy after is released. +/// optimization avoids losing the affected packets on Android. Removal is +/// tracked in ; wait until +/// ships through a released iroh +/// version and the physical-device matrix passes. fn segmentation_offload_enabled(target_os: &str) -> bool { target_os != "android" }