summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoakim Hulthe <joakim.hulthe@mullvad.net>2024-10-18 15:41:06 +0200
committerJoakim Hulthe <joakim.hulthe@mullvad.net>2024-10-21 13:49:33 +0200
commite577b765388ee08461745f52911b3e2ab15bff9c (patch)
tree368ec2a10074b29adc58b703c38dfdc4813b88e8
parent837da623777207d8097b571168772f0c6027f778 (diff)
downloadmullvadvpn-e577b765388ee08461745f52911b3e2ab15bff9c.tar.xz
mullvadvpn-e577b765388ee08461745f52911b3e2ab15bff9c.zip
Take obfuscation overhead into account when setting MTU
-rw-r--r--talpid-wireguard/src/lib.rs9
-rw-r--r--talpid-wireguard/src/obfuscation.rs32
-rw-r--r--tunnel-obfuscation/src/lib.rs5
-rw-r--r--tunnel-obfuscation/src/shadowsocks.rs15
-rw-r--r--tunnel-obfuscation/src/udp2tcp.rs12
5 files changed, 51 insertions, 22 deletions
diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs
index 64c65d2c4e..bc178a6c64 100644
--- a/talpid-wireguard/src/lib.rs
+++ b/talpid-wireguard/src/lib.rs
@@ -165,12 +165,16 @@ impl WireguardMonitor {
let endpoint_addrs: Vec<IpAddr> = config.peers().map(|peer| peer.endpoint.ip()).collect();
let (close_obfs_sender, close_obfs_listener) = sync_mpsc::channel();
+ // Start obfuscation server and patch the WireGuard config to point the endpoint to it.
let obfuscator = args
.runtime
.block_on(obfuscation::apply_obfuscation_config(
&mut config,
close_obfs_sender.clone(),
))?;
+ if let Some(obfuscator) = obfuscator.as_ref() {
+ config.mtu = config.mtu.saturating_sub(obfuscator.packet_overhead());
+ }
#[cfg(target_os = "windows")]
let (setup_done_tx, setup_done_rx) = mpsc::channel(0);
@@ -374,7 +378,7 @@ impl WireguardMonitor {
args: TunnelArgs<'_, F>,
) -> Result<WireguardMonitor> {
let (close_obfs_sender, close_obfs_listener) = sync_mpsc::channel();
- // TODO: Document the side effect of starting this before opening the tunnel!
+ // Start obfuscation server and patch the WireGuard config to point the endpoint to it.
let obfuscator = args
.runtime
.block_on(obfuscation::apply_obfuscation_config(
@@ -382,6 +386,9 @@ impl WireguardMonitor {
close_obfs_sender.clone(),
args.tun_provider.clone(),
))?;
+ if let Some(obfuscator) = obfuscator.as_ref() {
+ config.mtu = config.mtu.saturating_sub(obfuscator.packet_overhead());
+ }
let should_negotiate_ephemeral_peer = config.quantum_resistant || config.daita;
let tunnel = Self::open_tunnel(
diff --git a/talpid-wireguard/src/obfuscation.rs b/talpid-wireguard/src/obfuscation.rs
index 0e1e7273e8..7317c4bfaf 100644
--- a/talpid-wireguard/src/obfuscation.rs
+++ b/talpid-wireguard/src/obfuscation.rs
@@ -32,29 +32,15 @@ pub async fn apply_obfuscation_config(
#[cfg(target_os = "linux")]
config.fwmark,
);
- apply_obfuscation_config_inner(
- config,
- settings,
- close_msg_sender,
- #[cfg(target_os = "android")]
- tun_provider,
- )
- .await
- .map(Some)
-}
-async fn apply_obfuscation_config_inner(
- config: &mut Config,
- settings: ObfuscationSettings,
- close_msg_sender: sync_mpsc::Sender<CloseMsg>,
- #[cfg(target_os = "android")] tun_provider: Arc<Mutex<TunProvider>>,
-) -> Result<ObfuscatorHandle> {
log::trace!("Obfuscation settings: {settings:?}");
let obfuscator = create_obfuscator(&settings)
.await
.map_err(Error::ObfuscationError)?;
+ let packet_overhead = obfuscator.packet_overhead();
+
#[cfg(target_os = "android")]
bypass_vpn(tun_provider, obfuscator.remote_socket_fd()).await;
@@ -76,7 +62,10 @@ async fn apply_obfuscation_config_inner(
}
});
- Ok(ObfuscatorHandle::new(obfuscation_task))
+ Ok(Some(ObfuscatorHandle {
+ obfuscation_task,
+ packet_overhead,
+ }))
}
/// Patch the first peer in the WireGuard configuration to use the local proxy endpoint
@@ -129,16 +118,17 @@ async fn bypass_vpn(
/// Simple wrapper that automatically cancels the future which runs an obfuscator.
pub struct ObfuscatorHandle {
obfuscation_task: tokio::task::JoinHandle<()>,
+ packet_overhead: u16,
}
impl ObfuscatorHandle {
- pub fn new(obfuscation_task: tokio::task::JoinHandle<()>) -> Self {
- Self { obfuscation_task }
- }
-
pub fn abort(&self) {
self.obfuscation_task.abort();
}
+
+ pub fn packet_overhead(&self) -> u16 {
+ self.packet_overhead
+ }
}
impl Drop for ObfuscatorHandle {
diff --git a/tunnel-obfuscation/src/lib.rs b/tunnel-obfuscation/src/lib.rs
index 62528ed609..9b4ca08d48 100644
--- a/tunnel-obfuscation/src/lib.rs
+++ b/tunnel-obfuscation/src/lib.rs
@@ -31,6 +31,11 @@ pub trait Obfuscator: Send {
/// Returns the file descriptor of the outbound socket.
#[cfg(target_os = "android")]
fn remote_socket_fd(&self) -> std::os::unix::io::RawFd;
+
+ /// The overhead (in bytes) of this obfuscation protocol.
+ ///
+ /// This is used when deciding on MTUs.
+ fn packet_overhead(&self) -> u16;
}
#[derive(Debug)]
diff --git a/tunnel-obfuscation/src/shadowsocks.rs b/tunnel-obfuscation/src/shadowsocks.rs
index c529f65a19..87622506f5 100644
--- a/tunnel-obfuscation/src/shadowsocks.rs
+++ b/tunnel-obfuscation/src/shadowsocks.rs
@@ -58,6 +58,7 @@ pub enum Error {
pub struct Shadowsocks {
udp_client_addr: SocketAddr,
+ wireguard_endpoint: SocketAddr,
server: tokio::task::JoinHandle<Result<()>>,
// The receiver will implicitly shut down when this is dropped
_shutdown_tx: oneshot::Sender<()>,
@@ -101,6 +102,7 @@ impl Shadowsocks {
Ok(Shadowsocks {
udp_client_addr,
+ wireguard_endpoint: settings.wireguard_endpoint,
server,
_shutdown_tx: shutdown_tx,
#[cfg(target_os = "android")]
@@ -285,6 +287,19 @@ impl Obfuscator for Shadowsocks {
fn remote_socket_fd(&self) -> std::os::unix::io::RawFd {
self.outbound_fd
}
+
+ fn packet_overhead(&self) -> u16 {
+ // This math relies on the packet structure of Shadowsocks AEAD UDP packets.
+ // https://shadowsocks.org/doc/aead.html
+ // Those packets look like this: [salt][address][payload][tag]
+ debug_assert!(SHADOWSOCKS_CIPHER.is_aead());
+
+ let overhead = SHADOWSOCKS_CIPHER.salt_len()
+ + Address::from(self.wireguard_endpoint).serialized_len()
+ + SHADOWSOCKS_CIPHER.tag_len();
+
+ u16::try_from(overhead).expect("packet overhead is less than u16::MAX")
+ }
}
/// Return whether retrying is a lost cause
diff --git a/tunnel-obfuscation/src/udp2tcp.rs b/tunnel-obfuscation/src/udp2tcp.rs
index 450d8d3bba..6532e0624b 100644
--- a/tunnel-obfuscation/src/udp2tcp.rs
+++ b/tunnel-obfuscation/src/udp2tcp.rs
@@ -85,4 +85,16 @@ impl Obfuscator for Udp2Tcp {
fn remote_socket_fd(&self) -> std::os::unix::io::RawFd {
self.instance.remote_tcp_fd()
}
+
+ fn packet_overhead(&self) -> u16 {
+ let max_tcp_header_len = 60; // https://datatracker.ietf.org/doc/html/rfc9293#section-3.1-6.22.1
+ let udp_header_len = 8; // https://datatracker.ietf.org/doc/html/rfc768
+
+ // TODO: Make `HEADER_LEN` constant public in udp-over-tcp lib and use it instead
+ let udp_over_tcp_header_len = size_of::<u16>();
+
+ let overhead = max_tcp_header_len - udp_header_len + udp_over_tcp_header_len;
+
+ u16::try_from(overhead).expect("packet overhead is less than u16::MAX")
+ }
}