diff options
| author | Emīls <emils@mullvad.net> | 2026-01-04 20:59:12 +0100 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2026-03-30 12:19:30 +0200 |
| commit | eea4b07bf9a0534478e06c6e5918783959a503ae (patch) | |
| tree | 5944a95695d5336ecd9ca7299a69c1ec6f40e16e | |
| parent | dd7e0ae00adff24ada3a939f56b24bd524b80fb8 (diff) | |
| download | mullvadvpn-eea4b07bf9a0534478e06c6e5918783959a503ae.tar.xz mullvadvpn-eea4b07bf9a0534478e06c6e5918783959a503ae.zip | |
Support IPv6 in QUIC and Shadowsocks obfuscators
4 files changed, 40 insertions, 7 deletions
diff --git a/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift b/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift index cca5376634..508dc41fa1 100644 --- a/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift +++ b/ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift @@ -100,6 +100,16 @@ extension REST { features?.lwo != nil } + /// Returns true if the relay has IPv6 addresses in shadowsocksExtraAddrIn + public var hasShadowsocksIpv6: Bool { + shadowsocksExtraAddrIn?.contains(where: { IPv6Address($0) != nil }) ?? false + } + + /// Returns true if the relay has IPv6 addresses in QUIC addrIn + public var hasQuicIpv6: Bool { + features?.quic?.addrIn.contains(where: { IPv6Address($0) != nil }) ?? false + } + public func override(ipv4AddrIn: IPv4Address?, ipv6AddrIn: IPv6Address?) -> Self { ServerRelay( hostname: hostname, diff --git a/ios/MullvadREST/Relay/Obfuscation/QuicObfuscator.swift b/ios/MullvadREST/Relay/Obfuscation/QuicObfuscator.swift index c95c7dfb5d..21c2ab042b 100644 --- a/ios/MullvadREST/Relay/Obfuscation/QuicObfuscator.swift +++ b/ios/MullvadREST/Relay/Obfuscation/QuicObfuscator.swift @@ -24,13 +24,21 @@ struct QuicObfuscator: RelayObfuscating { } private func filterQuicRelays(from relays: REST.ServerRelaysResponse) -> REST.ServerRelaysResponse { - REST.ServerRelaysResponse( + var filteredRelays = relays.wireguard.relays.filter { $0.supportsQuic } + + // If IPv6 is required, filter to relays with QUIC IPv6 addresses + // Regular entry IPv6 addresses don't work with QUIC + if tunnelSettings.ipVersion.isIPv6 { + filteredRelays = filteredRelays.filter { $0.hasQuicIpv6 } + } + + return REST.ServerRelaysResponse( locations: relays.locations, wireguard: REST.ServerWireguardTunnels( ipv4Gateway: relays.wireguard.ipv4Gateway, ipv6Gateway: relays.wireguard.ipv6Gateway, portRanges: relays.wireguard.portRanges, - relays: relays.wireguard.relays.filter { $0.supportsQuic }, + relays: filteredRelays, shadowsocksPortRanges: relays.wireguard.shadowsocksPortRanges ), bridge: relays.bridge diff --git a/ios/MullvadREST/Relay/Obfuscation/ShadowsocksObfuscator.swift b/ios/MullvadREST/Relay/Obfuscation/ShadowsocksObfuscator.swift index fd78aa0a24..64b5a17059 100644 --- a/ios/MullvadREST/Relay/Obfuscation/ShadowsocksObfuscator.swift +++ b/ios/MullvadREST/Relay/Obfuscation/ShadowsocksObfuscator.swift @@ -36,6 +36,7 @@ struct ShadowsocksObfuscator: RelayObfuscating { let portRanges = RelaySelector.parseRawPortRanges(relays.wireguard.shadowsocksPortRanges) // If the selected port is within the shadowsocks port ranges we can select from all relays. + // Standard ports can use regular ipv6AddrIn for IPv6. guard case let .custom(port) = port, !portRanges.contains(where: { $0.contains(port) }) @@ -43,8 +44,13 @@ struct ShadowsocksObfuscator: RelayObfuscating { return relays } - let filteredRelays = relays.wireguard.relays.filter { relay in - relay.shadowsocksExtraAddrIn != nil + // Custom port outside standard ranges - require shadowsocksExtraAddrIn. + // If IPv6 is enabled, also require IPv6 addresses in shadowsocksExtraAddrIn. + let filteredRelays: [REST.ServerRelay] + if tunnelSettings.ipVersion.isIPv6 { + filteredRelays = relays.wireguard.relays.filter { $0.hasShadowsocksIpv6 } + } else { + filteredRelays = relays.wireguard.relays.filter { $0.shadowsocksExtraAddrIn != nil } } return REST.ServerRelaysResponse( diff --git a/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs b/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs index bb3d45f3ab..857914ea31 100644 --- a/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs +++ b/mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs @@ -1,6 +1,6 @@ use std::{ io, - net::{Ipv4Addr, SocketAddr}, + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, }; use talpid_types::net::wireguard::PublicKey; use tokio::task::JoinHandle; @@ -23,15 +23,24 @@ impl TunnelObfuscatorRuntime { } pub fn new_shadowsocks(peer: SocketAddr) -> Self { + let wireguard_endpoint = if peer.is_ipv4() { + SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)) + } else { + SocketAddr::from((Ipv6Addr::LOCALHOST, 51820)) + }; let settings = ObfuscationSettings::Shadowsocks(shadowsocks::Settings { shadowsocks_endpoint: peer, - wireguard_endpoint: SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)), + wireguard_endpoint, }); Self { settings } } pub fn new_quic(peer: SocketAddr, hostname: String, token: String) -> Self { - let wireguard_endpoint = SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)); + let wireguard_endpoint = if peer.is_ipv4() { + SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)) + } else { + SocketAddr::from((Ipv6Addr::LOCALHOST, 51820)) + }; let token: quic::AuthToken = token.parse().unwrap(); let quic = quic::Settings::new(peer, hostname, token, wireguard_endpoint); let settings = ObfuscationSettings::Quic(quic); |
