summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls <emils@mullvad.net>2026-01-04 20:59:12 +0100
committerEmīls <emils@mullvad.net>2026-03-30 12:19:30 +0200
commiteea4b07bf9a0534478e06c6e5918783959a503ae (patch)
tree5944a95695d5336ecd9ca7299a69c1ec6f40e16e
parentdd7e0ae00adff24ada3a939f56b24bd524b80fb8 (diff)
downloadmullvadvpn-eea4b07bf9a0534478e06c6e5918783959a503ae.tar.xz
mullvadvpn-eea4b07bf9a0534478e06c6e5918783959a503ae.zip
Support IPv6 in QUIC and Shadowsocks obfuscators
-rw-r--r--ios/MullvadREST/ApiHandlers/ServerRelaysResponse.swift10
-rw-r--r--ios/MullvadREST/Relay/Obfuscation/QuicObfuscator.swift12
-rw-r--r--ios/MullvadREST/Relay/Obfuscation/ShadowsocksObfuscator.swift10
-rw-r--r--mullvad-ios/src/tunnel_obfuscator_proxy/mod.rs15
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);