summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock65
-rw-r--r--talpid-core/src/tunnel/mod.rs4
-rw-r--r--talpid-routing/src/lib.rs2
-rw-r--r--talpid-routing/src/unix/mod.rs12
-rw-r--r--talpid-tunnel/src/lib.rs4
-rw-r--r--talpid-wireguard/Cargo.toml4
-rw-r--r--talpid-wireguard/src/boringtun/mod.rs301
-rw-r--r--talpid-wireguard/src/connectivity/check.rs20
-rw-r--r--talpid-wireguard/src/connectivity/mock.rs37
-rw-r--r--talpid-wireguard/src/connectivity/monitor.rs9
-rw-r--r--talpid-wireguard/src/ephemeral.rs27
-rw-r--r--talpid-wireguard/src/lib.rs131
-rw-r--r--talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap2
-rw-r--r--talpid-wireguard/src/stats.rs23
-rw-r--r--talpid-wireguard/src/wireguard_go/mod.rs45
-rw-r--r--talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs11
-rw-r--r--talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs11
-rw-r--r--talpid-wireguard/src/wireguard_kernel/stats.rs1
-rw-r--r--talpid-wireguard/src/wireguard_nt/mod.rs6
-rw-r--r--wireguard-go-rs/Cargo.toml2
20 files changed, 433 insertions, 284 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7e30a542e3..c16e83be5a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -388,7 +388,7 @@ dependencies = [
"arrayvec",
"cc",
"cfg-if",
- "constant_time_eq",
+ "constant_time_eq 0.3.0",
]
[[package]]
@@ -440,7 +440,7 @@ dependencies = [
[[package]]
name = "boringtun"
version = "0.6.0"
-source = "git+https://github.com/mullvad/boringtun?rev=83b3b5bb1bf3ec8b9849cc0d250d96d623b6181f#83b3b5bb1bf3ec8b9849cc0d250d96d623b6181f"
+source = "git+https://github.com/mullvad/gotatun?rev=d8ddd4232c9636aa98ab4bd14093e256f7d09abb#d8ddd4232c9636aa98ab4bd14093e256f7d09abb"
dependencies = [
"aead",
"async-trait",
@@ -449,29 +449,34 @@ dependencies = [
"blake2",
"bytes",
"chacha20poly1305",
+ "constant_time_eq 0.4.2",
"duplicate",
"either",
"eyre",
+ "futures",
"hex",
"hmac",
"ip_network",
"ip_network_table",
"libc",
"log",
+ "maybenot",
"nix 0.30.1",
"parking_lot",
"pcap-file",
"pnet_packet 0.35.0",
"rand 0.9.2",
+ "rand_chacha 0.9.0",
"rand_core 0.6.4",
"ring",
- "socket2 0.4.10",
+ "socket2 0.6.0",
"thiserror 1.0.59",
"tokio",
"tracing",
"tun 0.7.13",
"typed-builder 0.21.0",
"untrusted",
+ "windows-sys 0.61.1",
"x25519-dalek",
"zerocopy",
]
@@ -811,6 +816,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]]
+name = "constant_time_eq"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
+
+[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1500,9 +1511,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
-version = "1.0.34"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
+checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -2815,16 +2826,16 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "maybenot"
-version = "2.0.1"
+version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fd2cdf418470d9b87dd88e7fd57c479d7d84d22e765691ee814d51e0cf6782"
+checksum = "44731ed644f441efeb5ca66a440a84555a40883e2873e20c9afde89b5b4836c8"
dependencies = [
"base64 0.22.1",
"bincode",
"enum-map",
"flate2",
- "rand 0.8.5",
- "rand_core 0.6.4",
+ "rand 0.9.2",
+ "rand_core 0.9.3",
"rand_distr",
"serde",
"sha256",
@@ -2832,13 +2843,13 @@ dependencies = [
[[package]]
name = "maybenot-ffi"
-version = "2.0.1"
+version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c0111f55c83db74f51cdb1d8ffdad16149200d936d0a227fdfe5d579318ef78"
+checksum = "6b2682b6da446844fad3c7ced24be8c66853efefa4674a48072cfd6b45e4fb0d"
dependencies = [
"maybenot",
- "rand 0.8.5",
- "rand_chacha 0.3.1",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
]
[[package]]
@@ -2883,9 +2894,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
-version = "0.8.0"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
@@ -4635,12 +4646,12 @@ dependencies = [
[[package]]
name = "rand_distr"
-version = "0.4.3"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463"
dependencies = [
"num-traits",
- "rand 0.8.5",
+ "rand 0.9.2",
]
[[package]]
@@ -5307,16 +5318,6 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
-version = "0.4.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
@@ -7331,18 +7332,18 @@ dependencies = [
[[package]]
name = "zerocopy"
-version = "0.8.26"
+version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
+checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.8.26"
+version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
+checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs
index a23f45e097..96bd504412 100644
--- a/talpid-core/src/tunnel/mod.rs
+++ b/talpid-core/src/tunnel/mod.rs
@@ -46,10 +46,6 @@ pub enum Error {
/// There was an error listening for events from the Wireguard tunnel
#[error("Failed while listening for events from the Wireguard tunnel")]
WireguardTunnelMonitoringError(#[from] talpid_wireguard::Error),
-
- /// Could not detect and assign the correct mtu
- #[error("Could not detect and assign a correct MTU for the Wireguard tunnel")]
- AssignMtuError,
}
impl From<Error> for ErrorStateCause {
diff --git a/talpid-routing/src/lib.rs b/talpid-routing/src/lib.rs
index 22ba95fadf..5a63d1d20d 100644
--- a/talpid-routing/src/lib.rs
+++ b/talpid-routing/src/lib.rs
@@ -190,7 +190,7 @@ impl RequiredRoute {
/// Set route MTU to the given value.
#[cfg(any(target_os = "linux", target_os = "macos"))]
- pub fn mtu(mut self, mtu: u16) -> Self {
+ pub fn with_mtu(mut self, mtu: u16) -> Self {
self.mtu = Some(mtu);
self
}
diff --git a/talpid-routing/src/unix/mod.rs b/talpid-routing/src/unix/mod.rs
index a3c669db4c..55068ccf57 100644
--- a/talpid-routing/src/unix/mod.rs
+++ b/talpid-routing/src/unix/mod.rs
@@ -377,7 +377,17 @@ impl RouteManagerHandle {
.map_err(Error::PlatformError)
}
- /// Listen for route changes.
+ /// Get the link-MTU of the route to `ip`.
+ ///
+ /// 1. Get the route to `ip`
+ /// 2. Get the link associated with that route
+ /// 3. Get the MTU of that link
+ ///
+ /// Or, expressed in sh:
+ /// ```sh
+ /// ip route get 127.0.0.1 | grep -o 'dev [a-z]*' # outputs "dev lo"
+ /// ip link show dev lo | grep -o 'mtu [0-9]*' # outputs "mtu 65536"
+ /// ```
#[cfg(target_os = "linux")]
pub async fn get_mtu_for_route(&self, ip: IpAddr) -> Result<u16, Error> {
let (response_tx, response_rx) = oneshot::channel();
diff --git a/talpid-tunnel/src/lib.rs b/talpid-tunnel/src/lib.rs
index ab5c291e56..5eca4c9dbd 100644
--- a/talpid-tunnel/src/lib.rs
+++ b/talpid-tunnel/src/lib.rs
@@ -24,8 +24,8 @@ use tun_provider::TunProvider;
pub const IPV4_HEADER_SIZE: u16 = 20;
/// Size of IPv6 header in bytes
pub const IPV6_HEADER_SIZE: u16 = 40;
-/// Size of wireguard header in bytes
-pub const WIREGUARD_HEADER_SIZE: u16 = 40;
+/// WireGuard overhead. Size of UDP header, plus header and footer of a WireGuard data packet.
+pub const WIREGUARD_HEADER_SIZE: u16 = 8 + 32;
/// Size of ICMP header in bytes
pub const ICMP_HEADER_SIZE: u16 = 8;
/// Smallest allowed MTU for IPv4 in bytes
diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml
index 5da612f577..ec3f7aee86 100644
--- a/talpid-wireguard/Cargo.toml
+++ b/talpid-wireguard/Cargo.toml
@@ -45,8 +45,8 @@ tokio-stream = { version = "0.1", features = ["io-util"] }
[dependencies.boringtun]
optional = true
features = ["device", "tun"]
-git = "https://github.com/mullvad/boringtun"
-rev = "83b3b5bb1bf3ec8b9849cc0d250d96d623b6181f"
+git = "https://github.com/mullvad/gotatun"
+rev = "d8ddd4232c9636aa98ab4bd14093e256f7d09abb"
[target.'cfg(unix)'.dependencies]
nix = { workspace = true, features = ["fs"] }
diff --git a/talpid-wireguard/src/boringtun/mod.rs b/talpid-wireguard/src/boringtun/mod.rs
index f687458f0b..d82c19ac92 100644
--- a/talpid-wireguard/src/boringtun/mod.rs
+++ b/talpid-wireguard/src/boringtun/mod.rs
@@ -3,7 +3,7 @@ use crate::config::patch_allowed_ips;
use crate::{
Tunnel, TunnelError,
config::Config,
- stats::{Stats, StatsMap},
+ stats::{DaitaStats, Stats, StatsMap},
};
#[cfg(target_os = "android")]
use boringtun::udp::UdpTransportFactory;
@@ -13,9 +13,15 @@ use boringtun::{
api::{ApiClient, ApiServer, command::*},
peer::AllowedIP,
},
+ packet::{Ipv4Header, Ipv6Header, UdpHeader, WgData},
+ tun::{
+ IpRecv,
+ channel::{TunChannelRx, TunChannelTx},
+ tun_async_device::TunDevice as GotaTunDevice,
+ },
udp::{
- UdpSocketFactory,
- channel::{PacketChannelUdp, TunChannelRx, TunChannelTx, get_packet_channels},
+ channel::{UdpChannelFactory, new_udp_tun_channel},
+ socket::UdpSocketFactory,
},
};
#[cfg(not(target_os = "android"))]
@@ -35,7 +41,7 @@ use tun07::{AbstractDevice, AsyncDevice};
#[cfg(all(feature = "multihop-pcap", target_os = "linux"))]
use boringtun::tun::{
- IpRecv, IpSend,
+ IpSend,
pcap::{PcapSniffer, PcapStream},
};
@@ -45,8 +51,8 @@ type UdpFactory = AndroidUdpSocketFactory;
#[cfg(not(target_os = "android"))]
type UdpFactory = UdpSocketFactory;
-type SinglehopDevice = DeviceHandle<(UdpFactory, Arc<tun07::AsyncDevice>, Arc<tun07::AsyncDevice>)>;
-type ExitDevice = DeviceHandle<(PacketChannelUdp, Arc<AsyncDevice>, Arc<AsyncDevice>)>;
+type SinglehopDevice = DeviceHandle<(UdpFactory, GotaTunDevice)>;
+type ExitDevice = DeviceHandle<(UdpChannelFactory, GotaTunDevice)>;
#[cfg(not(all(feature = "multihop-pcap", target_os = "linux")))]
type EntryDevice = DeviceHandle<(UdpFactory, TunChannelTx, TunChannelRx)>;
@@ -64,7 +70,7 @@ pub struct BoringTun {
// TODO: Can we not store this in an option?
devices: Option<Devices>,
- tun: Arc<AsyncDevice>,
+ tun_dev: GotaTunDevice,
#[cfg(target_os = "android")]
android_tun: Arc<Tun>,
@@ -78,22 +84,27 @@ pub struct BoringTun {
impl BoringTun {
async fn new(
- tun: Arc<AsyncDevice>,
+ tun_dev: AsyncDevice,
#[cfg(target_os = "android")] android_tun: Arc<Tun>,
config: Config,
interface_name: String,
) -> Result<Self, TunnelError> {
+ let tun_dev = GotaTunDevice::from_tun_device(tun_dev)
+ .map_err(|e| TunnelError::RecoverableStartWireguardError(Box::new(e)))?;
+
let devices = create_devices(
&config,
- tun.clone(),
+ None,
+ tun_dev.clone(),
#[cfg(target_os = "android")]
android_tun.clone(),
)
.await?;
+
Ok(Self {
config,
interface_name,
- tun,
+ tun_dev,
#[cfg(target_os = "android")]
android_tun,
devices: Some(devices),
@@ -134,6 +145,15 @@ impl Devices {
}
}
+/// Errors that can happen when setting up / restarting / reconfiguring GotaTun devices.
+#[derive(thiserror::Error, Debug)]
+pub enum ConfigureGotaTunDeviceError {
+ #[error("Multihop devices were provided with a single config")]
+ ExpectedSinglehopDevice,
+ #[error("Single devices were provided with a multihop config")]
+ ExpectedMultihopDevice,
+}
+
#[cfg(target_os = "android")]
struct AndroidUdpSocketFactory {
pub tun: Arc<Tun>,
@@ -169,7 +189,7 @@ pub async fn open_boringtun_tunnel(
log::info!("BoringTun::start_tunnel");
let routes = config.get_tunnel_destinations();
- log::info!("calling get_tunnel_for_userspace");
+ log::trace!("calling get_tunnel_for_userspace");
#[cfg(not(target_os = "android"))]
let async_tun = {
let tun = get_tunnel_for_userspace(tun_provider, config, routes)?;
@@ -208,16 +228,19 @@ pub async fn open_boringtun_tunnel(
let mut tun_config = tun07::Configuration::default();
tun_config.raw_fd(fd);
- let device = tun07::Device::new(&tun_config).unwrap();
+ let mut device = tun07::Device::new(&tun_config).unwrap();
+
+ // HACK: the `tun` crate does not implement AbstractDevice::(set_)mtu on Android, instead
+ // they are stubbed. `mtu()` will simply return the value set by `set_mtu()`, or 1500.
+ //
+ // GotaTun will try to read the MTU from this, so call set_mtu here with the correct value.
+ device.set_mtu(config.mtu).unwrap();
(Arc::new(tun), tun07::AsyncDevice::new(device).unwrap())
};
let interface_name = async_tun.deref().tun_name().unwrap();
- log::info!("passing tunnel dev to boringtun");
- let async_tun = Arc::new(async_tun);
-
let config = config.clone();
#[cfg(target_os = "android")]
let config = match gateway_only {
@@ -226,6 +249,7 @@ pub async fn open_boringtun_tunnel(
false => config,
};
+ log::trace!("passing tunnel dev to boringtun");
let boringtun = BoringTun::new(
async_tun,
#[cfg(target_os = "android")]
@@ -237,32 +261,40 @@ pub async fn open_boringtun_tunnel(
.inspect_err(|e| log::error!("Failed to open BoringTun: {e:?}"))?;
log::info!(
- "This tunnel was brought to you by...
- .........................................................
- ..*...*.. .--. .---. ..*....*.
- ...*..... | ) o | ......*..
- .*..*..*. |--: .-. .--.. .--. .-..|. . .--. ...*.....
- ...*..... | )( )| | | |( ||| | | | .*.....*.
- *.....*.. '--' `-' ' -' `-' `-`-`|'`--`-' `- .....*...
- ......... ._.' ..*...*..
- ..*...*.............................................*...."
+ r#"This tunnel was brought to you by...
+ _______ _ __ ______
+ / ____(_)_(_) /_____ /_ __/_ ______
+ / / __/ __ \/ __/ __ `// / / / / / __ \
+ / /_/ / /_/ / /_/ /_/ // / / /_/ / / / /
+ \____/\____/\__/\__,_//_/ \__,_/_/ /_/"#
);
Ok(boringtun)
}
+/// Create and configure boringtun devices.
+///
+/// Will create an [EntryDevice] and an [ExitDevice] if `config` is a multihop config,
+/// and a [SinglehopDevice] otherwise.
async fn create_devices(
- config: &Config,
- async_tun: Arc<AsyncDevice>,
- #[cfg(target_os = "android")] tun: Arc<Tun>,
+ config: &Config, // TODO: do not include config to reduce confusion
+ daita: Option<&DaitaSettings>,
+ tun_dev: GotaTunDevice,
+ #[cfg(target_os = "android")] android_tun: Arc<Tun>,
) -> Result<Devices, TunnelError> {
let (entry_api, entry_api_server) = ApiServer::new();
let boringtun_entry_config = DeviceConfig {
api: Some(entry_api_server),
};
- if let Some(exit_peer) = &config.exit_peer {
- // multihop
+ #[cfg(target_os = "android")]
+ let udp_factory = AndroidUdpSocketFactory { tun: android_tun };
+
+ #[cfg(not(target_os = "android"))]
+ let udp_factory = UdpSocketFactory;
+
+ let mut devices = if let Some(exit_peer) = &config.exit_peer {
+ // Multihop setup
let source_v4 = config
.tunnel
@@ -284,32 +316,92 @@ async fn create_devices(
})
.unwrap_or(Ipv6Addr::UNSPECIFIED);
- let (tun_tx, tun_rx, udp_channels) =
- get_packet_channels(PACKET_CHANNEL_CAPACITY, source_v4, source_v6);
+ // Calculate length of extra headers, assuming no optional header fields (i.e. IP options)
+ let multihop_overhead = match exit_peer.endpoint.ip() {
+ IpAddr::V4(..) => Ipv4Header::LEN + UdpHeader::LEN + WgData::OVERHEAD,
+ IpAddr::V6(..) => Ipv6Header::LEN + UdpHeader::LEN + WgData::OVERHEAD,
+ };
+
+ let exit_mtu = tun_dev.mtu();
+ let entry_mtu = exit_mtu.increase(multihop_overhead as u16).unwrap(/* TODO: this can happen if tun mtu is max i think*/);
+
+ let (tun_channel_tx, tun_channel_rx, udp_channels) =
+ new_udp_tun_channel(PACKET_CHANNEL_CAPACITY, source_v4, source_v6, entry_mtu);
let (exit_api, exit_api_server) = ApiServer::new();
let exit_device = ExitDevice::new(
udp_channels,
- async_tun.clone(),
- async_tun,
+ tun_dev.clone(),
+ tun_dev,
DeviceConfig {
api: Some(exit_api_server),
},
)
.await;
- #[cfg(target_os = "android")]
- let factory = AndroidUdpSocketFactory { tun };
-
- #[cfg(not(target_os = "android"))]
- let factory = UdpSocketFactory;
-
// Hacky way of dumping entry<->exit traffic to a unix socket which wireshark can read.
// See docs on wrap_in_pcap_sniffer for an explanation.
#[cfg(all(feature = "multihop-pcap", target_os = "linux"))]
- let (tun_tx, tun_rx) = wrap_in_pcap_sniffer(tun_tx, tun_rx);
+ let (tun_channel_tx, tun_channel_rx) = wrap_in_pcap_sniffer(tun_channel_tx, tun_channel_rx);
+
+ let entry_device = EntryDevice::new(
+ udp_factory,
+ tun_channel_tx,
+ tun_channel_rx,
+ boringtun_entry_config,
+ )
+ .await;
+
+ Devices::Multihop {
+ entry_device,
+ entry_api,
+ exit_device,
+ exit_api,
+ }
+ } else {
+ // Singlehop setup
- let entry_device = EntryDevice::new(factory, tun_tx, tun_rx, boringtun_entry_config).await;
+ let device = SinglehopDevice::new(
+ udp_factory,
+ tun_dev.clone(),
+ tun_dev,
+ boringtun_entry_config,
+ )
+ .await;
+
+ Devices::Singlehop {
+ device,
+ api: entry_api,
+ }
+ };
+
+ configure_devices(&mut devices, config, daita).await?;
+
+ Ok(devices)
+}
+
+/// (Re)Configure boringtun devices.
+async fn configure_devices(
+ devices: &mut Devices,
+ config: &Config,
+ daita: Option<&DaitaSettings>,
+) -> Result<(), TunnelError> {
+ if let Some(exit_peer) = &config.exit_peer {
+ log::trace!(
+ "configuring boringtun multihop device (daita={})",
+ daita.is_some()
+ );
+
+ let Devices::Multihop {
+ entry_api,
+ exit_api,
+ ..
+ } = devices
+ else {
+ return Err(TunnelError::ConfigureGotaTunDevice(
+ ConfigureGotaTunDeviceError::ExpectedMultihopDevice,
+ ));
+ };
let private_key = &config.tunnel.private_key;
let peer = &config.entry_peer;
@@ -318,6 +410,7 @@ async fn create_devices(
config.fwmark,
private_key,
peer,
+ daita,
);
entry_api.send(set_cmd).await.map_err(|err| {
log::error!("Failed to set boringtun config: {err:#}");
@@ -329,34 +422,24 @@ async fn create_devices(
config.fwmark,
private_key,
exit_peer,
+ None, // exit peer never has daita
);
exit_api.send(set_cmd).await.map_err(|err| {
log::error!("Failed to set boringtun config: {err:#}");
TunnelError::SetConfigError
})?;
-
- Ok(Devices::Multihop {
- entry_device,
- entry_api,
- exit_device,
- exit_api,
- })
} else {
- #[cfg(target_os = "android")]
- let factory = AndroidUdpSocketFactory { tun };
-
- #[cfg(not(target_os = "android"))]
- let factory = UdpSocketFactory;
+ log::trace!(
+ "configuring boringtun singlehop device (daita={})",
+ daita.is_some()
+ );
- let device = SinglehopDevice::new(
- factory,
- async_tun.clone(),
- async_tun,
- boringtun_entry_config,
- )
- .await;
+ let Devices::Singlehop { api, .. } = devices else {
+ return Err(TunnelError::ConfigureGotaTunDevice(
+ ConfigureGotaTunDeviceError::ExpectedSinglehopDevice,
+ ));
+ };
- log::info!("configuring boringtun device");
let private_key = &config.tunnel.private_key;
let peer = &config.entry_peer;
let set_cmd = create_set_command(
@@ -364,18 +447,15 @@ async fn create_devices(
config.fwmark,
private_key,
peer,
+ daita,
);
-
- entry_api.send(set_cmd).await.map_err(|err| {
+ api.send(set_cmd).await.map_err(|err| {
log::error!("Failed to set boringtun config: {err:#}");
TunnelError::SetConfigError
})?;
-
- Ok(Devices::Singlehop {
- device,
- api: entry_api,
- })
}
+
+ Ok(())
}
#[async_trait::async_trait]
@@ -385,7 +465,6 @@ impl Tunnel for BoringTun {
}
fn stop(mut self: Box<Self>) -> Result<(), TunnelError> {
- log::info!("BoringTun::stop"); // remove me
tokio::runtime::Handle::current().block_on(async {
// TODO: devices should never be None while this BoringTun instance is running.
debug_assert!(self.devices.is_some());
@@ -423,12 +502,22 @@ impl Tunnel for BoringTun {
Some(SystemTime::now() - Duration::new(handshake_sec, handshake_nsec))
};
+ let daita = || -> Option<DaitaStats> {
+ Some(DaitaStats {
+ tx_padding_bytes: peer.tx_padding_bytes?,
+ tx_padding_packet_bytes: peer.tx_padding_packet_bytes?,
+ rx_padding_bytes: peer.rx_padding_bytes?,
+ rx_padding_packet_bytes: peer.rx_padding_packet_bytes?,
+ })
+ };
+
stats.insert(
peer.peer.public_key.0,
Stats {
tx_bytes: peer.tx_bytes.unwrap_or_default(),
rx_bytes: peer.rx_bytes.unwrap_or_default(),
last_handshake_time: last_handshake(),
+ daita: daita(),
},
);
}
@@ -440,38 +529,55 @@ impl Tunnel for BoringTun {
fn set_config<'a>(
&'a mut self,
config: Config,
+ daita: Option<DaitaSettings>,
) -> std::pin::Pin<Box<dyn Future<Output = Result<(), TunnelError>> + Send + 'a>> {
Box::pin(async move {
- let _old_config = std::mem::replace(&mut self.config, config);
- // TODO: diff with _old_config to see if devices need to be recreated.
- // TODO: devices should never be None while this BoringTun instance is running.
- debug_assert!(self.devices.is_some());
- if let Some(devices) = self.devices.take() {
- devices.stop().await;
+ self.config = config;
+
+ // if we're switching to/from multihop, we'll need to tear down the old device(s)
+ // and set them up with the new DeviceTransports
+ // TODO: Debug configure_devices. Currently we need to tear down the old devices
+ // after having exchanged tunnel params with the ephemeral peer. Empirically this
+ // is true for both DAITA & PQ.
+ // let recreate_devices = old_config.is_multihop() != self.config.is_multihop();
+ let recreate_devices = true;
+
+ if recreate_devices {
+ // TODO: devices should never be None while this BoringTun instance is running.
+ debug_assert!(self.devices.is_some());
+ if let Some(devices) = self.devices.take() {
+ devices.stop().await;
+ }
}
- self.devices = Some(
- create_devices(
- &self.config,
- self.tun.clone(),
- #[cfg(target_os = "android")]
- self.android_tun.clone(),
- )
- .await?,
- );
+
+ match &mut self.devices {
+ Some(devices) => {
+ configure_devices(devices, &self.config, daita.as_ref()).await?;
+ }
+ None => {
+ self.devices = Some(
+ create_devices(
+ &self.config,
+ daita.as_ref(),
+ self.tun_dev.clone(),
+ #[cfg(target_os = "android")]
+ self.android_tun.clone(),
+ )
+ .await?,
+ )
+ }
+ };
+
Ok(())
})
}
-
- fn start_daita(&mut self, _settings: DaitaSettings) -> Result<(), TunnelError> {
- log::info!("Haha no");
- Ok(())
- }
}
fn create_set_command(
#[cfg(target_os = "linux")] fwmark: Option<u32>,
private_key: &talpid_types::net::wireguard::PrivateKey,
peer: &talpid_types::net::wireguard::PeerConfig,
+ daita: Option<&DaitaSettings>,
) -> Set {
let mut set_cmd = Set::builder()
.private_key(private_key.to_bytes())
@@ -502,9 +608,20 @@ fn create_set_command(
boring_peer.preshared_key = Some(SetUnset::Set((*psk.as_bytes()).into()));
}
- set_cmd
- .peers
- .push(SetPeer::builder().peer(boring_peer).build());
+ let mut set_peer = SetPeer::builder().peer(boring_peer).build();
+
+ if let Some(daita) = daita {
+ set_peer.daita_settings = Some(boringtun::device::daita::api::DaitaSettings {
+ maybenot_machines: daita.client_machines.clone(),
+ max_padding_frac: daita.max_padding_frac,
+ max_blocking_frac: daita.max_blocking_frac,
+ // TODO: tweak to sane values
+ max_blocked_packets: 1024,
+ min_blocking_capacity: 50,
+ });
+ }
+
+ set_cmd.peers.push(set_peer);
set_cmd
}
diff --git a/talpid-wireguard/src/connectivity/check.rs b/talpid-wireguard/src/connectivity/check.rs
index 89043fb54c..c7182f57df 100644
--- a/talpid-wireguard/src/connectivity/check.rs
+++ b/talpid-wireguard/src/connectivity/check.rs
@@ -514,8 +514,7 @@ mod test {
[0u8; 32],
Stats {
rx_bytes: 1,
- tx_bytes: 0,
- last_handshake_time: None,
+ ..Default::default()
},
);
conn_state.update(Instant::now(), stats);
@@ -540,8 +539,7 @@ mod test {
[0u8; 32],
Stats {
rx_bytes: 1,
- tx_bytes: 0,
- last_handshake_time: None,
+ ..Default::default()
},
);
conn_state.update(connect_time, stats);
@@ -565,8 +563,7 @@ mod test {
[0u8; 32],
Stats {
rx_bytes: 1,
- tx_bytes: 0,
- last_handshake_time: None,
+ ..Default::default()
},
);
conn_state.update(start, stats);
@@ -578,7 +575,7 @@ mod test {
Stats {
rx_bytes: 1,
tx_bytes: 1,
- last_handshake_time: None,
+ ..Default::default()
},
);
conn_state.update(update_time, stats);
@@ -670,14 +667,7 @@ mod test {
let tunnel = {
let mut tunnel_stats = StatsMap::new();
- tunnel_stats.insert(
- [0u8; 32],
- Stats {
- tx_bytes: 0,
- rx_bytes: 0,
- last_handshake_time: None,
- },
- );
+ tunnel_stats.insert([0u8; 32], Stats::default());
MockTunnel::new(move || Ok(tunnel_stats.clone())).boxed()
};
diff --git a/talpid-wireguard/src/connectivity/mock.rs b/talpid-wireguard/src/connectivity/mock.rs
index ef8bf4103f..103c691c9a 100644
--- a/talpid-wireguard/src/connectivity/mock.rs
+++ b/talpid-wireguard/src/connectivity/mock.rs
@@ -1,5 +1,6 @@
use std::future::Future;
use std::pin::Pin;
+use talpid_tunnel_config_client::DaitaSettings;
use tokio::time::Instant;
use super::Check;
@@ -30,14 +31,7 @@ pub fn mock_checker(now: Instant, pinger: Box<dyn Pinger>) -> (Check, CancelToke
pub fn connected_state(timestamp: Instant) -> ConnState {
const PEER: [u8; 32] = [0u8; 32];
let mut stats = StatsMap::new();
- stats.insert(
- PEER,
- Stats {
- tx_bytes: 0,
- rx_bytes: 0,
- last_handshake_time: None,
- },
- );
+ stats.insert(PEER, Stats::default());
ConnState::Connected {
rx_timestamp: timestamp,
tx_timestamp: timestamp,
@@ -61,14 +55,7 @@ impl MockTunnel {
pub fn always_incrementing() -> Self {
let mut map = StatsMap::new();
- map.insert(
- Self::PEER,
- Stats {
- tx_bytes: 0,
- rx_bytes: 0,
- last_handshake_time: None,
- },
- );
+ map.insert(Self::PEER, Stats::default());
let peers = std::sync::Mutex::new(map);
Self {
on_get_stats: Box::new(move || {
@@ -86,14 +73,7 @@ impl MockTunnel {
Self {
on_get_stats: Box::new(|| {
let mut map = StatsMap::new();
- map.insert(
- Self::PEER,
- Stats {
- tx_bytes: 0,
- rx_bytes: 0,
- last_handshake_time: None,
- },
- );
+ map.insert(Self::PEER, Stats::default());
Ok(map)
}),
}
@@ -117,17 +97,10 @@ impl Tunnel for MockTunnel {
fn set_config(
&mut self,
_config: Config,
+ _daita: Option<DaitaSettings>,
) -> Pin<Box<dyn Future<Output = std::result::Result<(), TunnelError>> + Send>> {
Box::pin(async { Ok(()) })
}
-
- #[cfg(daita)]
- fn start_daita(
- &mut self,
- _: talpid_tunnel_config_client::DaitaSettings,
- ) -> std::result::Result<(), TunnelError> {
- Ok(())
- }
}
#[async_trait::async_trait]
diff --git a/talpid-wireguard/src/connectivity/monitor.rs b/talpid-wireguard/src/connectivity/monitor.rs
index 644f2c9d80..6d5bc569cf 100644
--- a/talpid-wireguard/src/connectivity/monitor.rs
+++ b/talpid-wireguard/src/connectivity/monitor.rs
@@ -126,14 +126,7 @@ mod test {
let stop_bytes_rx_inner = stop_bytes_rx.clone();
let mut map = StatsMap::new();
- map.insert(
- [0u8; 32],
- Stats {
- tx_bytes: 0,
- rx_bytes: 0,
- last_handshake_time: None,
- },
- );
+ map.insert([0u8; 32], Stats::default());
let tunnel_stats = std::sync::Mutex::new(map);
let pinger = MockPinger::default();
diff --git a/talpid-wireguard/src/ephemeral.rs b/talpid-wireguard/src/ephemeral.rs
index 501de4fdb6..57a1fe9598 100644
--- a/talpid-wireguard/src/ephemeral.rs
+++ b/talpid-wireguard/src/ephemeral.rs
@@ -14,7 +14,7 @@ use std::{
use talpid_tunnel::tun_provider::TunProvider;
use ipnetwork::IpNetwork;
-use talpid_tunnel_config_client::EphemeralPeer;
+use talpid_tunnel_config_client::{DaitaSettings, EphemeralPeer};
use talpid_types::net::wireguard::{PrivateKey, PublicKey};
use tokio::sync::Mutex as AsyncMutex;
@@ -136,6 +136,7 @@ async fn config_ephemeral_peers_inner(
let entry_config = reconfigure_tunnel(
tunnel,
entry_tun_config,
+ None,
obfuscation_mtu,
obfuscator.clone(),
close_obfs_sender,
@@ -143,6 +144,7 @@ async fn config_ephemeral_peers_inner(
&tun_provider,
)
.await?;
+
let entry_ephemeral_peer = request_ephemeral_peer(
retry_attempt,
&entry_config,
@@ -159,6 +161,7 @@ async fn config_ephemeral_peers_inner(
config.exit_peer_mut().psk = exit_ephemeral_peer.psk;
if config.daita {
+ // NOTE: this option does nothing for GotaTun, and should be removed in future.
log::trace!("Enabling constant packet size for entry peer");
config.entry_peer.constant_packet_size = true;
}
@@ -168,6 +171,7 @@ async fn config_ephemeral_peers_inner(
*config = reconfigure_tunnel(
tunnel,
config.clone(),
+ daita,
obfuscation_mtu,
obfuscator,
close_obfs_sender,
@@ -176,21 +180,6 @@ async fn config_ephemeral_peers_inner(
)
.await?;
- if config.daita {
- let Some(daita) = daita else {
- unreachable!("missing DAITA settings");
- };
-
- // Start local DAITA machines
- let mut tunnel = tunnel.lock().await;
- if let Some(tunnel) = tunnel.as_mut() {
- tunnel
- .start_daita(daita)
- .map_err(Error::TunnelError)
- .map_err(CloseMsg::SetupError)?;
- }
- }
-
Ok(())
}
@@ -200,6 +189,7 @@ async fn config_ephemeral_peers_inner(
async fn reconfigure_tunnel(
tunnel: &Arc<AsyncMutex<Option<TunnelType>>>,
mut config: Config,
+ daita: Option<DaitaSettings>,
obfuscation_mtu: u16,
obfuscator: Arc<AsyncMutex<Option<ObfuscatorHandle>>>,
close_obfs_sender: sync_mpsc::Sender<CloseMsg>,
@@ -223,7 +213,7 @@ async fn reconfigure_tunnel(
let mut tunnel = shared_tunnel.take().expect("tunnel was None");
tunnel
- .set_config(config.clone())
+ .set_config(config.clone(), daita)
.await
.map_err(Error::TunnelError)
.map_err(CloseMsg::SetupError)?;
@@ -239,6 +229,7 @@ async fn reconfigure_tunnel(
async fn reconfigure_tunnel(
tunnel: &Arc<AsyncMutex<Option<TunnelType>>>,
mut config: Config,
+ daita: Option<DaitaSettings>,
obfuscation_mtu: u16,
obfuscator: Arc<AsyncMutex<Option<ObfuscatorHandle>>>,
close_obfs_sender: sync_mpsc::Sender<CloseMsg>,
@@ -260,7 +251,7 @@ async fn reconfigure_tunnel(
let set_config_future = tunnel
.as_mut()
- .map(|tunnel| tunnel.set_config(config.clone()));
+ .map(|tunnel| tunnel.set_config(config.clone(), daita));
if let Some(f) = set_config_future {
f.await
diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs
index 9531a1066f..d95e38d9b8 100644
--- a/talpid-wireguard/src/lib.rs
+++ b/talpid-wireguard/src/lib.rs
@@ -9,10 +9,9 @@ use futures::future::Future;
use obfuscation::ObfuscatorHandle;
#[cfg(windows)]
use std::io;
-#[cfg(not(target_os = "android"))]
-use std::net::IpAddr;
use std::{
convert::Infallible,
+ net::IpAddr,
path::Path,
pin::Pin,
sync::{Arc, mpsc as sync_mpsc},
@@ -22,6 +21,7 @@ use std::{env, sync::LazyLock};
#[cfg(not(target_os = "android"))]
use talpid_routing::{self, RequiredRoute};
use talpid_tunnel::{EventHook, TunnelArgs, TunnelEvent, TunnelMetadata, tun_provider};
+use talpid_tunnel::{IPV4_HEADER_SIZE, IPV6_HEADER_SIZE, WIREGUARD_HEADER_SIZE};
#[cfg(daita)]
use talpid_tunnel_config_client::DaitaSettings;
@@ -158,13 +158,16 @@ impl WireguardMonitor {
args: TunnelArgs<'_>,
_log_path: Option<&Path>,
) -> Result<WireguardMonitor> {
+ // NOTE: We force userspace WireGuard while boringtun is enabled to more easily test it
+ // TODO: Consider removing `cfg!(feature = "boringtun")`
+ let userspace_wireguard =
+ *FORCE_USERSPACE_WIREGUARD || params.options.daita || cfg!(feature = "boringtun");
+ let userspace_multihop = userspace_wireguard && cfg!(feature = "boringtun");
+
let route_mtu = args
.runtime
.block_on(get_route_mtu(params, &args.route_manager));
-
- let tunnel_mtu = params.options.mtu.unwrap_or_else(|| {
- clamp_tunnel_mtu(params, route_mtu.saturating_sub(wireguard_overhead(params)))
- });
+ let tunnel_mtu = calculate_tunnel_mtu(route_mtu, params, userspace_multihop);
let mut config = crate::config::Config::from_parameters(params, tunnel_mtu)
.map_err(Error::WireguardConfigError)?;
@@ -195,12 +198,6 @@ impl WireguardMonitor {
);
}
- // NOTE: We force userspace WireGuard while boringtun is enabled to more easily test
- // the implementation, as DAITA is not currently supported by boringtun.
- // TODO: Remove `cfg!(feature = "boringtun")`.
- let userspace_wireguard =
- *FORCE_USERSPACE_WIREGUARD || config.daita || cfg!(feature = "boringtun");
-
#[cfg(target_os = "windows")]
let (setup_done_tx, setup_done_rx) = mpsc::channel(0);
let tunnel = Self::open_tunnel(
@@ -426,10 +423,11 @@ impl WireguardMonitor {
.runtime
.block_on(get_route_mtu(params, &args.route_manager));
- let tunnel_mtu = params.options.mtu.unwrap_or_else(|| {
- clamp_tunnel_mtu(params, route_mtu.saturating_sub(wireguard_overhead(params)))
- });
+ // TODO: previously, we didn't account for userspace multihop on android.
+ // but it seems correct to do so.
+ let userspace_multihop = true;
+ let tunnel_mtu = calculate_tunnel_mtu(route_mtu, params, userspace_multihop);
let mut config = crate::config::Config::from_parameters(params, tunnel_mtu)
.map_err(Error::WireguardConfigError)?;
@@ -834,8 +832,10 @@ impl WireguardMonitor {
self.pinger_stop_sender.close();
- self.runtime
- .block_on(self.event_hook.on_event(TunnelEvent::Down));
+ self.runtime.block_on(async {
+ self.event_hook.on_event(TunnelEvent::Down).await;
+ log_daita_overhead(&self.tunnel).await;
+ });
self.stop_tunnel();
@@ -984,6 +984,7 @@ impl WireguardMonitor {
config: &Config,
userspace_wireguard: bool,
) -> RequiredRoute {
+ // TODO: surely this applies to all kinds of userspace multihop, not just gotatun?
// For userspace multihop, per-route MTU is unnecessary. Packets are not sent back to
// the tunnel interface, so we're not constrained by its MTU.
let using_boringtun = userspace_wireguard && cfg!(feature = "boringtun");
@@ -991,18 +992,16 @@ impl WireguardMonitor {
if !config.is_multihop() || using_boringtun {
route
} else {
- use talpid_tunnel::{IPV4_HEADER_SIZE, IPV6_HEADER_SIZE, WIREGUARD_HEADER_SIZE};
-
+ // FIXME: this presumably refers to the fact that wireguard can pad data packet
+ // payload lengths to a multiple of 16 bytes, but that number must not
+ // according to wg spec, exceed the MTU anyway, so why do we subtract 15 here?
+ //
// Set route MTU by subtracting the WireGuard overhead from the tunnel MTU. Plus
// some margin to make room for padding bytes.
- let ip_overhead = match route.prefix.is_ipv4() {
- true => IPV4_HEADER_SIZE,
- false => IPV6_HEADER_SIZE,
- };
const PADDING_BYTES_MARGIN: u16 = 15;
- let mtu = config.mtu - ip_overhead - WIREGUARD_HEADER_SIZE - PADDING_BYTES_MARGIN;
+ let mtu = config.mtu - wireguard_overhead(route.prefix.ip()) - PADDING_BYTES_MARGIN;
- route.mtu(mtu)
+ route.with_mtu(mtu)
}
}
@@ -1040,6 +1039,36 @@ async fn log_tunnel_data_usage(config: &Config, tunnel: &Arc<AsyncMutex<Option<T
}
}
+async fn log_daita_overhead(tunnel: &Arc<AsyncMutex<Option<TunnelType>>>) {
+ let tunnel = tunnel.lock().await;
+ let Some(tunnel) = &*tunnel else { return };
+ let Ok(tunnel_stats) = tunnel.get_tunnel_stats().await else {
+ return;
+ };
+ if let Some(stats) = tunnel_stats.values().find(|stats| stats.daita.is_some()) {
+ let daita = stats.daita.as_ref().unwrap();
+ log::info!("DAITA overhead stats:");
+ log::info!("Total (outgoing) {} MiB", stats.tx_bytes / 1024 / 1024);
+ log::info!("Total (incoming) {} MiB", stats.rx_bytes / 1024 / 1024);
+ log::info!(
+ "Padding packet overhead (outgoing) {} MiB",
+ daita.tx_padding_packet_bytes / 1024 / 1024
+ );
+ log::info!(
+ "Padding packet overhead (incoming) {} MiB",
+ daita.rx_padding_packet_bytes / 1024 / 1024
+ );
+ log::info!(
+ "Constant packet size overhead (outgoing) {} MiB",
+ daita.tx_padding_bytes / 1024 / 1024
+ );
+ log::info!(
+ "Constant packet size overhead (incoming) {} MiB",
+ daita.rx_padding_bytes / 1024 / 1024
+ );
+ }
+}
+
#[derive(Debug)]
enum CloseMsg {
Stop,
@@ -1059,10 +1088,8 @@ pub(crate) trait Tunnel: Send + Sync {
fn set_config<'a>(
&'a mut self,
_config: Config,
+ _daita: Option<DaitaSettings>,
) -> Pin<Box<dyn Future<Output = std::result::Result<(), TunnelError>> + Send + 'a>>;
- #[cfg(daita)]
- /// A [`Tunnel`] capable of using DAITA.
- fn start_daita(&mut self, settings: DaitaSettings) -> std::result::Result<(), TunnelError>;
}
/// Errors to be returned from WireGuard implementations, namely implementers of the Tunnel trait
@@ -1146,6 +1173,11 @@ pub enum TunnelError {
#[cfg(feature = "boringtun")]
#[error("Boringtun: {0:?}")]
BoringTunDevice(::boringtun::device::Error),
+
+ /// Failed to configure GotaTun device.
+ #[cfg(feature = "boringtun")]
+ #[error("Failed to configure the GotaTun device")]
+ ConfigureGotaTunDevice(#[source] boringtun::ConfigureGotaTunDeviceError),
}
#[cfg(target_os = "linux")]
@@ -1173,13 +1205,12 @@ const DEFAULT_MTU: u16 = if cfg!(target_os = "android") {
1380
};
-/// Get MTU based on the physical interface route
+/// Get the link-MTU of the route to the (entry) peer.
#[cfg(any(target_os = "linux", target_os = "windows"))]
async fn get_route_mtu(
params: &TunnelParameters,
route_manager: &talpid_routing::RouteManagerHandle,
) -> u16 {
- // Get the MTU of the device/route
route_manager
.get_mtu_for_route(params.connection.peer.endpoint.ip())
.await
@@ -1197,6 +1228,28 @@ async fn get_route_mtu(
DEFAULT_MTU
}
+/// Calculate what the MTU on the tunnel link should be.
+fn calculate_tunnel_mtu(
+ link_mtu_for_peer: u16,
+ params: &TunnelParameters,
+ userspace_multihop: bool,
+) -> u16 {
+ if let Some(mtu) = params.options.mtu {
+ return mtu;
+ }
+
+ let mut overhead = wireguard_overhead(params.connection.peer.endpoint.ip());
+
+ // only reduce tunnel_mtu for *userspace* multihop.
+ // For kernel-multihop, traffic to the exit peer is routed back through the tunnel link,
+ // so the MTU on that link must be larger to account for multihop overhead
+ if userspace_multihop && let Some(exit_peer) = &params.connection.exit_peer {
+ overhead += wireguard_overhead(exit_peer.endpoint.ip());
+ }
+
+ clamp_tunnel_mtu(params, link_mtu_for_peer.saturating_sub(overhead))
+}
+
/// Clamp WireGuard tunnel MTU to reasonable values
fn clamp_tunnel_mtu(params: &TunnelParameters, mtu: u16) -> u16 {
use talpid_tunnel::{MIN_IPV4_MTU, MIN_IPV6_MTU};
@@ -1214,17 +1267,17 @@ fn clamp_tunnel_mtu(params: &TunnelParameters, mtu: u16) -> u16 {
const MTU_SAFETY_MARGIN: u16 = 60;
// The largest peer MTU that we allow
- let max_peer_mtu: u16 = 1500 - MTU_SAFETY_MARGIN - wireguard_overhead(params);
+ // TODO: userspace multihop?
+ let max_peer_mtu: u16 =
+ 1500 - MTU_SAFETY_MARGIN - wireguard_overhead(params.connection.peer.endpoint.ip());
mtu.clamp(min_mtu, max_peer_mtu)
}
-/// Calculates total overhead due to WireGuard
-const fn wireguard_overhead(params: &TunnelParameters) -> u16 {
- use talpid_tunnel::{IPV4_HEADER_SIZE, IPV6_HEADER_SIZE, WIREGUARD_HEADER_SIZE};
- WIREGUARD_HEADER_SIZE
- + match params.connection.peer.endpoint.is_ipv6() {
- false => IPV4_HEADER_SIZE,
- true => IPV6_HEADER_SIZE,
- }
+/// Calculates WireGuard per-packet overhead
+const fn wireguard_overhead(ip_version: IpAddr) -> u16 {
+ match ip_version {
+ IpAddr::V4(..) => IPV4_HEADER_SIZE + WIREGUARD_HEADER_SIZE,
+ IpAddr::V6(..) => IPV6_HEADER_SIZE + WIREGUARD_HEADER_SIZE,
+ }
}
diff --git a/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap b/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap
index a3dc8c3b6a..59f59bdd32 100644
--- a/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap
+++ b/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap
@@ -1,10 +1,10 @@
---
source: talpid-wireguard/src/stats.rs
expression: "StatsDebug { now, stats: &stats }"
-snapshot_kind: text
---
Stats {
tx_bytes: 100,
rx_bytes: 100,
last_handshake: "60000 ms ago",
+ daita: None,
}
diff --git a/talpid-wireguard/src/stats.rs b/talpid-wireguard/src/stats.rs
index 4707339f75..7adacb5338 100644
--- a/talpid-wireguard/src/stats.rs
+++ b/talpid-wireguard/src/stats.rs
@@ -2,11 +2,29 @@ use std::fmt;
use std::time::{Duration, SystemTime};
/// Contains bytes sent and received through a tunnel
-#[derive(Default, PartialEq, Eq, Clone, Copy)]
+#[derive(Default, PartialEq, Eq, Clone)]
pub struct Stats {
pub tx_bytes: u64,
pub rx_bytes: u64,
pub last_handshake_time: Option<SystemTime>,
+ // Optional DAITA stats
+ // Currently only available for GotaTun
+ pub daita: Option<DaitaStats>,
+}
+
+#[derive(Debug, Default, PartialEq, Eq, Clone)]
+pub struct DaitaStats {
+ /// Extra bytes added due to constant-size padding of data packets
+ pub tx_padding_bytes: u64,
+
+ /// Bytes of standalone padding packets transmitted
+ pub tx_padding_packet_bytes: u64,
+
+ /// Total extra bytes removed due to constant-size padding of data packets
+ pub rx_padding_bytes: u64,
+
+ /// Bytes of standalone padding packets received
+ pub rx_padding_packet_bytes: u64,
}
impl fmt::Debug for Stats {
@@ -45,6 +63,8 @@ impl fmt::Debug for StatsDebug<'_> {
dbg.field("last_handshake", &"no handshake");
}
+ dbg.field("daita", &self.stats.daita);
+
dbg.finish()
}
}
@@ -64,6 +84,7 @@ mod test {
tx_bytes: 100,
rx_bytes: 100,
last_handshake_time: Some(SystemTime::UNIX_EPOCH),
+ ..Default::default()
};
insta::assert_debug_snapshot!(StatsDebug { now, stats: &stats });
diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs
index 55058f2a0c..343d777ddb 100644
--- a/talpid-wireguard/src/wireguard_go/mod.rs
+++ b/talpid-wireguard/src/wireguard_go/mod.rs
@@ -690,32 +690,34 @@ impl Tunnel for WgGoTunnel {
fn set_config(
&mut self,
config: Config,
+ daita: Option<DaitaSettings>,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>> {
- Box::pin(async move { self.set_config(config).await })
- }
+ Box::pin(async move {
+ self.set_config(config).await?;
- #[cfg(daita)]
- fn start_daita(&mut self, settings: DaitaSettings) -> Result<()> {
- log::info!("Initializing DAITA for wireguard device");
- let peer_public_key = self.handle().config.entry_peer.public_key.clone();
+ if let Some(daita) = daita {
+ log::info!("Initializing DAITA for wireguard device");
+ let peer_public_key = self.handle().config.entry_peer.public_key.clone();
- let machines = settings.client_machines.join("\n");
- let machines =
- CString::new(machines).map_err(|err| TunnelError::StartDaita(Box::new(err)))?;
+ let machines = daita.client_machines.join("\n");
+ let machines =
+ CString::new(machines).map_err(|err| TunnelError::StartDaita(Box::new(err)))?;
- self.handle()
- .tunnel_handle
- .activate_daita(
- peer_public_key.as_bytes(),
- &machines,
- settings.max_padding_frac,
- settings.max_blocking_frac,
- DAITA_EVENTS_CAPACITY,
- DAITA_ACTIONS_CAPACITY,
- )
- .map_err(|e| TunnelError::StartDaita(Box::new(e)))?;
+ self.handle()
+ .tunnel_handle
+ .activate_daita(
+ peer_public_key.as_bytes(),
+ &machines,
+ daita.max_padding_frac,
+ daita.max_blocking_frac,
+ DAITA_EVENTS_CAPACITY,
+ DAITA_ACTIONS_CAPACITY,
+ )
+ .map_err(|e| TunnelError::StartDaita(Box::new(e)))?;
+ }
- Ok(())
+ Ok(())
+ })
}
}
@@ -816,6 +818,7 @@ mod stats {
tx_bytes: tx_bytes_val,
rx_bytes: rx_bytes_val,
last_handshake_time: last_handshake_time(),
+ ..Default::default()
},
);
peer = None;
diff --git a/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs
index b7735b68b1..407b4cdd25 100644
--- a/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs
+++ b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs
@@ -120,10 +120,16 @@ impl Tunnel for NetlinkTunnel {
fn set_config(
&mut self,
config: Config,
+ daita: Option<DaitaSettings>,
) -> Pin<Box<dyn Future<Output = std::result::Result<(), TunnelError>> + Send + 'static>> {
let mut wg = self.netlink_connections.wg_handle.clone();
let interface_index = self.interface_index;
Box::pin(async move {
+ if daita.is_some() {
+ // Outright fail to start - this tunnel type does not support DAITA.
+ return Err(TunnelError::DaitaNotSupported);
+ }
+
wg.set_config(interface_index, &config)
.await
.map_err(|err| {
@@ -132,9 +138,4 @@ impl Tunnel for NetlinkTunnel {
})
})
}
-
- /// Outright fail to start - this tunnel type does not support DAITA.
- fn start_daita(&mut self, _: DaitaSettings) -> std::result::Result<(), TunnelError> {
- Err(TunnelError::DaitaNotSupported)
- }
}
diff --git a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs
index 46a394a59d..c35503fa82 100644
--- a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs
+++ b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs
@@ -97,10 +97,16 @@ impl Tunnel for NetworkManagerTunnel {
fn set_config(
&mut self,
config: Config,
+ daita: Option<DaitaSettings>,
) -> Pin<Box<dyn Future<Output = std::result::Result<(), TunnelError>> + Send>> {
let interface_name = self.interface_name.clone();
let mut wg = self.netlink_connections.wg_handle.clone();
Box::pin(async move {
+ if daita.is_some() {
+ // Outright fail to start - this tunnel type does not support DAITA.
+ return Err(TunnelError::DaitaNotSupported);
+ }
+
let index = iface_index(&interface_name).map_err(|err| {
log::error!("Failed to fetch WireGuard device index: {}", err);
TunnelError::SetConfigError
@@ -111,11 +117,6 @@ impl Tunnel for NetworkManagerTunnel {
})
})
}
-
- /// Outright fail to start - this tunnel type does not support DAITA.
- fn start_daita(&mut self, _: DaitaSettings) -> std::result::Result<(), TunnelError> {
- Err(TunnelError::DaitaNotSupported)
- }
}
fn convert_config_to_dbus(config: &Config) -> DeviceConfig {
diff --git a/talpid-wireguard/src/wireguard_kernel/stats.rs b/talpid-wireguard/src/wireguard_kernel/stats.rs
index a055a35d10..c74aebc140 100644
--- a/talpid-wireguard/src/wireguard_kernel/stats.rs
+++ b/talpid-wireguard/src/wireguard_kernel/stats.rs
@@ -43,6 +43,7 @@ impl Stats {
tx_bytes,
rx_bytes,
last_handshake_time,
+ ..Default::default()
},
);
}
diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs
index a1472bb2ff..d7a6b1e5f7 100644
--- a/talpid-wireguard/src/wireguard_nt/mod.rs
+++ b/talpid-wireguard/src/wireguard_nt/mod.rs
@@ -951,6 +951,7 @@ impl Tunnel for WgNtTunnel {
tx_bytes: peer.tx_bytes,
rx_bytes: peer.rx_bytes,
last_handshake_time,
+ ..Default::default()
},
);
}
@@ -968,6 +969,7 @@ impl Tunnel for WgNtTunnel {
fn set_config(
&mut self,
config: Config,
+ _daita: Option<DaitaSettings>,
) -> Pin<Box<dyn Future<Output = std::result::Result<(), super::TunnelError>> + Send>> {
let device = self.device.clone();
let current_config = self.config.clone();
@@ -988,10 +990,6 @@ impl Tunnel for WgNtTunnel {
})
})
}
-
- fn start_daita(&mut self, _settings: DaitaSettings) -> std::result::Result<(), TunnelError> {
- unimplemented!("DAITA is not supported on wireguard-nt")
- }
}
pub fn as_uninit_byte_slice<T: Copy + Sized>(value: &T) -> &[mem::MaybeUninit<u8>] {
diff --git a/wireguard-go-rs/Cargo.toml b/wireguard-go-rs/Cargo.toml
index 40d70623a5..7efbac12ab 100644
--- a/wireguard-go-rs/Cargo.toml
+++ b/wireguard-go-rs/Cargo.toml
@@ -22,7 +22,7 @@ talpid-types.path = "../talpid-types"
# NOTE: for other platforms, maybenot-ffi is NOT declared here, but instead built directly from
# wireguard-go-rs/libwg/wireguard-go/maybenot-ffi
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
-maybenot-ffi = "2.0.1"
+maybenot-ffi = "2.2.2"
[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { workspace = true, features = [