diff --git a/easytier-core/Cargo.toml b/easytier-core/Cargo.toml index ed623493..8d23387c 100644 --- a/easytier-core/Cargo.toml +++ b/easytier-core/Cargo.toml @@ -110,7 +110,10 @@ browser-config = ["config-write", "easytier-proto/api", "easytier-proto/json-rpc chacha20 = ["dep:chacha20poly1305"] openssl-crypto = ["dep:openssl"] ring-crypto = ["dep:ring"] -wasi-crypto-offload = ["ring-crypto"] +# The WASI host performs AEAD for the guest (easytier_host.crypto_aead_seal/open), +# so the guest must not link a software backend: ring would emit env.ring_core_* imports +# that the host module cannot provide. +wasi-crypto-offload = [] config-write = [] endpoint-discovery = [ "dep:http-body-util", diff --git a/easytier-core/src/instance/config.rs b/easytier-core/src/instance/config.rs index 4fe72848..51eb3404 100644 --- a/easytier-core/src/instance/config.rs +++ b/easytier-core/src/instance/config.rs @@ -92,6 +92,16 @@ impl Default for CoreInstanceHostConfig { } impl CoreInstanceHostConfig { + /// Record whether this host can serve the TCP subnet proxy from a user-space TCP stack. + /// + /// Hosts without an OS TUN device (WASI guests, sandboxes) can only forward proxied TCP + /// through such a stack, so they must advertise it for the proxy runtime to pick that path. + /// `requires_smoltcp` is sticky: once the host needs the stack it keeps needing it. + pub fn set_smoltcp_stack(&mut self, available: bool) { + self.smoltcp_available = available; + self.requires_smoltcp |= available; + } + pub(crate) fn accepts_runtime_url(&self, url: &url::Url) -> bool { !self.ignore_unsupported_config || self @@ -765,3 +775,24 @@ data_compress_algo = "Zstd" ); } } + +#[cfg(test)] +mod host_capability_tests { + use super::CoreInstanceHostConfig; + + #[test] + fn set_smoltcp_stack_marks_the_host_as_capable_and_dependent() { + let mut config = CoreInstanceHostConfig::default(); + config.set_smoltcp_stack(true); + assert!(config.smoltcp_available); + assert!(config.requires_smoltcp); + } + + #[test] + fn set_smoltcp_stack_does_not_claim_a_missing_stack() { + let mut config = CoreInstanceHostConfig::default(); + config.set_smoltcp_stack(false); + assert!(!config.smoltcp_available); + assert!(!config.requires_smoltcp); + } +} diff --git a/easytier-core/src/wasi/runtime.rs b/easytier-core/src/wasi/runtime.rs index a3b616e4..11147ec3 100644 --- a/easytier-core/src/wasi/runtime.rs +++ b/easytier-core/src/wasi/runtime.rs @@ -152,6 +152,12 @@ pub(super) fn new_wasi_core_runtime( adapters.config.ignore_unsupported_config = true; adapters.config.endpoint_protocols = host_tunnel_schemes.to_vec(); } + // A WASI guest has no OS TUN, so the TCP subnet proxy can only forward through the + // user-space stack. Advertise it whenever it is compiled in; never clear the flag, since + // the host-tunnel mode above may rely on it independently of this feature. + if cfg!(feature = "proxy-smoltcp-stack") { + adapters.config.set_smoltcp_stack(true); + } #[cfg(feature = "wasm-host-tunnel")] #[cfg(not(feature = "wasm-host-tunnel-outbound"))] let tunnel_ingress = crate::wasi::adapter::tunnel::WasiHostTunnelIngress::new( diff --git a/script/build-wasi-core.sh b/script/build-wasi-core.sh index c1b01a4e..57c81181 100755 --- a/script/build-wasi-core.sh +++ b/script/build-wasi-core.sh @@ -10,7 +10,10 @@ cd "$repository_root" readonly target_dir="${CARGO_TARGET_DIR:-target}" readonly raw_artifact="${target_dir}/wasm32-wasip1/release/easytier_core.wasm" readonly artifact="${target_dir}/wasm32-wasip1/release/easytier_core_go_host.wasm" -readonly core_features="management-rpc,proxy-smoltcp-stack,ring-crypto,wasi-crypto-offload" +# The WASI host performs AEAD for the guest, and the guest has no TLS stack of its own, so the +# default feature set (endpoint-discovery -> rustls -> ring) must stay off: ring compiles C +# shims into wasm32-wasip1 that become env.ring_core_* imports, which the Go host cannot provide. +readonly core_features="management,proxy-smoltcp-stack,wasi-crypto-offload,aes-gcm,tcp-hole-punch" sha256_file() { if command -v sha256sum >/dev/null; then @@ -120,6 +123,7 @@ if [[ "$version" != *"version ${binaryen_version} "* ]]; then fi cargo build --release --target wasm32-wasip1 -p easytier-core \ + --no-default-features \ --features "$core_features" optimized="${artifact}.wasm-opt.$$"