summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock30
-rw-r--r--mullvad-api/src/relay_list.rs4
-rw-r--r--mullvad-daemon/src/lib.rs62
-rw-r--r--mullvad-daemon/src/management_interface.rs34
-rw-r--r--mullvad-daemon/src/relays/matcher.rs37
-rw-r--r--mullvad-daemon/src/relays/mod.rs342
-rw-r--r--mullvad-daemon/src/settings.rs14
-rw-r--r--mullvad-management-interface/proto/management_interface.proto33
-rw-r--r--mullvad-management-interface/src/types.rs108
-rw-r--r--mullvad-types/src/custom_tunnel.rs1
-rw-r--r--mullvad-types/src/relay_constraints.rs79
-rw-r--r--mullvad-types/src/relay_list.rs34
-rw-r--r--mullvad-types/src/settings/mod.rs9
-rw-r--r--talpid-core/Cargo.toml2
-rw-r--r--talpid-core/src/tunnel/wireguard/config.rs7
-rw-r--r--talpid-core/src/tunnel/wireguard/mod.rs151
-rw-r--r--talpid-core/src/tunnel/wireguard/wireguard_nt.rs4
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs2
-rw-r--r--talpid-types/src/net/mod.rs76
-rw-r--r--talpid-types/src/net/obfuscation.rs7
-rw-r--r--talpid-types/src/net/wireguard.rs13
21 files changed, 762 insertions, 287 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 63abc2bbab..2b11029e7d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1890,19 +1890,6 @@ dependencies = [
[[package]]
name = "nix"
-version = "0.20.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945"
-dependencies = [
- "bitflags",
- "cc",
- "cfg-if 1.0.0",
- "libc",
- "memoffset",
-]
-
-[[package]]
-name = "nix"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
@@ -3130,7 +3117,7 @@ dependencies = [
"triggered",
"trust-dns-server",
"tun",
- "udp-over-tcp",
+ "tunnel-obfuscation",
"uuid",
"which",
"widestring 0.5.1",
@@ -3632,6 +3619,17 @@ dependencies = [
]
[[package]]
+name = "tunnel-obfuscation"
+version = "0.1.0"
+dependencies = [
+ "async-trait",
+ "err-derive",
+ "futures",
+ "tokio",
+ "udp-over-tcp",
+]
+
+[[package]]
name = "typenum"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3646,14 +3644,14 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "udp-over-tcp"
version = "0.1.0"
-source = "git+https://github.com/mullvad/udp-over-tcp?rev=1e27324362ed123b61fa2062b1599e5f9d569796#1e27324362ed123b61fa2062b1599e5f9d569796"
+source = "git+https://github.com/mullvad/udp-over-tcp?rev=27b9519b63244736b6f3c7c4af60976c88dc6b95#27b9519b63244736b6f3c7c4af60976c88dc6b95"
dependencies = [
"env_logger 0.8.4",
"err-context",
"futures",
"lazy_static",
"log",
- "nix 0.20.2",
+ "nix 0.23.1",
"structopt",
"tokio",
]
diff --git a/mullvad-api/src/relay_list.rs b/mullvad-api/src/relay_list.rs
index 5a8a01836f..7f72767a1b 100644
--- a/mullvad-api/src/relay_list.rs
+++ b/mullvad-api/src/relay_list.rs
@@ -4,7 +4,7 @@ use crate::rest;
use hyper::{header, Method, StatusCode};
use mullvad_types::{location, relay_list};
-use talpid_types::net::{wireguard, TransportProtocol};
+use talpid_types::net::wireguard;
use std::{
collections::BTreeMap,
@@ -182,7 +182,6 @@ impl ServerRelayList {
ipv4_gateway,
ipv6_gateway,
public_key,
- protocol: TransportProtocol::Udp,
};
for mut wireguard_relay in relays {
@@ -315,6 +314,7 @@ fn relay(relay: Relay, location: location::Location) -> relay_list::Relay {
weight: relay.weight,
tunnels: Default::default(),
bridges: Default::default(),
+ obfuscators: Default::default(),
location: Some(location),
}
}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 27aea9b79a..50bea189ea 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -41,8 +41,8 @@ use mullvad_types::{
endpoint::MullvadEndpoint,
location::{Coordinates, GeoIpLocation},
relay_constraints::{
- BridgeSettings, BridgeState, Constraint, InternalBridgeConstraints, RelaySettings,
- RelaySettingsUpdate,
+ BridgeSettings, BridgeState, Constraint, InternalBridgeConstraints, ObfuscationSettings,
+ RelaySettings, RelaySettingsUpdate, SelectedObfuscation,
},
relay_list::{Relay, RelayList},
settings::{DnsOptions, DnsState, Settings},
@@ -155,6 +155,9 @@ pub enum Error {
#[error(display = "No bridge available")]
NoBridgeAvailable,
+ #[error(display = "Failed to select a compatible obfuscator")]
+ NoObfuscator,
+
#[error(display = "No matching entry relay was found")]
NoEntryRelayAvailable,
@@ -318,6 +321,8 @@ pub enum DaemonCommand {
/// Notify the split tunnel monitor that a volume was mounted or dismounted
#[cfg(target_os = "windows")]
CheckVolumes(ResponseTx<(), Error>),
+ /// Register settings for WireGuard obfuscator
+ SetObfuscationSettings(ResponseTx<(), settings::Error>, ObfuscationSettings),
/// Makes the daemon exit the main loop and quit.
Shutdown,
/// Saves the target tunnel state and enters a blocking state. The state is restored
@@ -1043,6 +1048,7 @@ where
let result = self
.create_tunnel_parameters(
&exit_relay,
+ &entry_relay,
endpoint,
device.token,
retry_attempt,
@@ -1084,6 +1090,7 @@ where
async fn create_tunnel_parameters(
&mut self,
relay: &Relay,
+ entry_relay: &Option<Relay>,
endpoint: MullvadEndpoint,
account_token: String,
retry_attempt: u32,
@@ -1169,6 +1176,29 @@ where
wg_data.addresses.ipv6_address.ip().into(),
],
};
+
+ let obfuscation = match self.settings.obfuscation_settings.selected_obfuscation {
+ SelectedObfuscation::Off | SelectedObfuscation::Auto
+ if !self
+ .relay_selector
+ .should_use_auto_obfuscator(retry_attempt) =>
+ {
+ None
+ }
+ _ => {
+ let obfuscator = self
+ .relay_selector
+ .get_obfuscator(
+ &self.settings.obfuscation_settings,
+ entry_relay.as_ref().unwrap_or(relay),
+ &endpoint,
+ retry_attempt,
+ )
+ .ok_or(Error::NoObfuscator)?;
+ Some(obfuscator)
+ }
+ };
+
Ok(wireguard::TunnelParameters {
connection: wireguard::ConnectionConfig {
tunnel,
@@ -1179,6 +1209,7 @@ where
},
options: tunnel_options.wireguard.options,
generic_options: tunnel_options.generic,
+ obfuscation,
}
.into())
}
@@ -1280,6 +1311,9 @@ where
UseWireGuardNt(tx, state) => self.on_use_wireguard_nt(tx, state).await,
#[cfg(target_os = "windows")]
CheckVolumes(tx) => self.on_check_volumes(tx).await,
+ SetObfuscationSettings(tx, settings) => {
+ self.on_set_obfuscation_settings(tx, settings).await
+ }
Shutdown => self.trigger_shutdown_event(),
PrepareRestart => self.on_prepare_restart(),
#[cfg(target_os = "android")]
@@ -2215,6 +2249,30 @@ where
}
}
+ async fn on_set_obfuscation_settings(
+ &mut self,
+ tx: ResponseTx<(), settings::Error>,
+ new_settings: ObfuscationSettings,
+ ) {
+ match self.settings.set_obfuscation_settings(new_settings).await {
+ Ok(settings_changed) => {
+ if settings_changed {
+ self.event_listener
+ .notify_settings(self.settings.to_settings());
+ self.reconnect_tunnel();
+ }
+ Self::oneshot_send(tx, Ok(()), "set_obfuscation_settings");
+ }
+ Err(err) => {
+ log::error!(
+ "{}",
+ err.display_chain_with_msg("Failed to set obfuscation settings")
+ );
+ Self::oneshot_send(tx, Err(err), "set_obfuscation_settings");
+ }
+ }
+ }
+
async fn on_set_bridge_state(
&mut self,
tx: ResponseTx<(), settings::Error>,
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 4005ff7df0..c361fa763f 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -13,7 +13,7 @@ use mullvad_paths;
use mullvad_types::settings::DnsOptions;
use mullvad_types::{
account::AccountToken,
- relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate},
+ relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
relay_list::RelayList,
settings::Settings,
states::{TargetState, TunnelState},
@@ -171,7 +171,8 @@ impl ManagementService for ManagementServiceImpl {
) -> ServiceResult<()> {
log::debug!("update_relay_settings");
let (tx, rx) = oneshot::channel();
- let constraints_update = RelaySettingsUpdate::try_from(request.into_inner())?;
+ let constraints_update =
+ RelaySettingsUpdate::try_from(request.into_inner()).map_err(map_protobuf_type_err)?;
let message = DaemonCommand::UpdateRelaySettings(tx, constraints_update);
self.send_command_to_daemon(message)?;
@@ -226,7 +227,8 @@ impl ManagementService for ManagementServiceImpl {
&self,
request: Request<types::BridgeSettings>,
) -> ServiceResult<()> {
- let settings = BridgeSettings::try_from(request.into_inner())?;
+ let settings =
+ BridgeSettings::try_from(request.into_inner()).map_err(map_protobuf_type_err)?;
log::debug!("set_bridge_settings({:?})", settings);
@@ -238,8 +240,24 @@ impl ManagementService for ManagementServiceImpl {
.map_err(map_settings_error)
}
+ async fn set_obfuscation_settings(
+ &self,
+ request: Request<types::ObfuscationSettings>,
+ ) -> ServiceResult<()> {
+ let settings =
+ ObfuscationSettings::try_from(request.into_inner()).map_err(map_protobuf_type_err)?;
+ log::debug!("set_obfuscation_settings({:?})", settings);
+ let (tx, rx) = oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetObfuscationSettings(tx, settings))?;
+ let settings_result = self.wait_for_result(rx).await?;
+ settings_result
+ .map(Response::new)
+ .map_err(map_settings_error)
+ }
+
async fn set_bridge_state(&self, request: Request<types::BridgeState>) -> ServiceResult<()> {
- let bridge_state = BridgeState::try_from(request.into_inner())?;
+ let bridge_state =
+ BridgeState::try_from(request.into_inner()).map_err(map_protobuf_type_err)?;
log::debug!("set_bridge_state({:?})", bridge_state);
let (tx, rx) = oneshot::channel();
@@ -350,7 +368,7 @@ impl ManagementService for ManagementServiceImpl {
#[cfg(not(target_os = "android"))]
async fn set_dns_options(&self, request: Request<types::DnsOptions>) -> ServiceResult<()> {
- let options = DnsOptions::try_from(request.into_inner())?;
+ let options = DnsOptions::try_from(request.into_inner()).map_err(map_protobuf_type_err)?;
log::debug!("set_dns_options({:?})", options);
let (tx, rx) = oneshot::channel();
@@ -1003,3 +1021,9 @@ fn map_account_history_error(error: account_history::Error) -> Status {
}
}
}
+
+fn map_protobuf_type_err(err: types::FromProtobufTypeError) -> Status {
+ match err {
+ types::FromProtobufTypeError::InvalidArgument(err) => Status::invalid_argument(err),
+ }
+}
diff --git a/mullvad-daemon/src/relays/matcher.rs b/mullvad-daemon/src/relays/matcher.rs
index fb708d05fb..7d75141b16 100644
--- a/mullvad-daemon/src/relays/matcher.rs
+++ b/mullvad-daemon/src/relays/matcher.rs
@@ -2,7 +2,7 @@ use mullvad_types::{
endpoint::{MullvadEndpoint, MullvadWireguardEndpoint},
relay_constraints::{
Constraint, LocationConstraint, Match, OpenVpnConstraints, Providers, RelayConstraints,
- TransportPort, WireguardConstraints,
+ WireguardConstraints,
},
relay_list::{Relay, RelayTunnels, WireguardEndpointData},
};
@@ -167,7 +167,7 @@ pub struct WireguardMatcher {
/// The peer is an already selected peer relay to be used with multihop.
/// It's stored here so we can exclude it from further selections being made.
pub peer: Option<Relay>,
- pub port: Constraint<TransportPort>,
+ pub port: Constraint<u16>,
pub ip_version: Constraint<IpVersion>,
}
@@ -183,7 +183,6 @@ impl WireguardMatcher {
public_key: data.public_key,
endpoint: SocketAddr::new(host, port),
allowed_ips: all_of_the_internet(),
- protocol: data.protocol,
};
Some(MullvadEndpoint::Wireguard(MullvadWireguardEndpoint {
peer: peer_config,
@@ -201,12 +200,7 @@ impl WireguardMatcher {
}
fn get_port_for_wireguard_relay(&self, data: &WireguardEndpointData) -> Option<u16> {
- match self
- .port
- .as_ref()
- .map(|port| port.port)
- .unwrap_or(Constraint::Any)
- {
+ match self.port {
Constraint::Any => {
let get_port_amount =
|range: &(u16, u16)| -> u64 { (1 + range.1 - range.0) as u64 };
@@ -257,18 +251,10 @@ impl Match<WireguardEndpointData> for WireguardMatcher {
fn matches(&self, endpoint: &WireguardEndpointData) -> bool {
match self.port {
Constraint::Any => true,
- Constraint::Only(TransportPort { port, protocol }) => {
- if protocol != endpoint.protocol {
- return false;
- }
- match port {
- Constraint::Any => true,
- Constraint::Only(port) => endpoint
- .port_ranges
- .iter()
- .any(|range| (port >= range.0 && port <= range.1)),
- }
- }
+ Constraint::Only(port) => endpoint
+ .port_ranges
+ .iter()
+ .any(|range| (port >= range.0 && port <= range.1)),
}
}
}
@@ -303,16 +289,9 @@ impl TunnelMatcher for WireguardMatcher {
}
fn mullvad_endpoint(&self, relay: &Relay) -> Option<MullvadEndpoint> {
- let valid_relays = relay
+ relay
.tunnels
.wireguard
- .iter()
- .filter(|tunnel| match self.port {
- Constraint::Any => true,
- Constraint::Only(port) => port.protocol == tunnel.protocol,
- })
- .collect::<Vec<_>>();
- valid_relays
.choose(&mut rand::thread_rng())
.and_then(|wg_tunnel| self.wg_data_to_endpoint(relay, (*wg_tunnel).clone()))
}
diff --git a/mullvad-daemon/src/relays/mod.rs b/mullvad-daemon/src/relays/mod.rs
index 6f6a0279ba..31a27b6e97 100644
--- a/mullvad-daemon/src/relays/mod.rs
+++ b/mullvad-daemon/src/relays/mod.rs
@@ -9,21 +9,25 @@ use mullvad_types::{
location::{Coordinates, Location},
relay_constraints::{
BridgeState, Constraint, InternalBridgeConstraints, LocationConstraint, Match,
- OpenVpnConstraints, Providers, RelayConstraints, Set, TransportPort, WireguardConstraints,
+ ObfuscationSettings, OpenVpnConstraints, Providers, RelayConstraints, SelectedObfuscation,
+ Set, TransportPort, Udp2TcpObfuscationSettings, WireguardConstraints,
},
- relay_list::{Relay, RelayList, WireguardEndpointData},
+ relay_list::{Relay, RelayList, Udp2TcpEndpointData},
};
use parking_lot::Mutex;
use rand::{self, seq::SliceRandom, Rng};
use std::{
io,
- net::IpAddr,
+ net::{IpAddr, SocketAddr},
path::Path,
sync::Arc,
time::{self, SystemTime},
};
use talpid_types::{
- net::{openvpn::ProxySettings, wireguard, IpVersion, TransportProtocol, TunnelType},
+ net::{
+ obfuscation::ObfuscatorConfig, openvpn::ProxySettings, wireguard, IpVersion,
+ TransportProtocol, TunnelType,
+ },
ErrorExt,
};
@@ -43,13 +47,11 @@ const RELAYS_FILENAME: &str = "relays.json";
const DEFAULT_WIREGUARD_PORT: u16 = 51820;
const WIREGUARD_EXIT_CONSTRAINTS: WireguardMatcher = WireguardMatcher {
peer: None,
- port: Constraint::Only(TransportPort {
- protocol: TransportProtocol::Udp,
- port: Constraint::Only(DEFAULT_WIREGUARD_PORT),
- }),
+ port: Constraint::Only(DEFAULT_WIREGUARD_PORT),
ip_version: Constraint::Only(IpVersion::V4),
};
-const WIREGUARD_TCP_PORTS: [(u16, u16); 3] = [(80, 80), (443, 443), (5001, 5001)];
+
+const UDP2TCP_PORTS: [u16; 3] = [80, 443, 5001];
#[derive(err_derive::Error, Debug)]
#[error(no_from)]
@@ -105,8 +107,25 @@ impl ParsedRelays {
latitude,
longitude,
});
+
Self::filter_invalid_relays(&mut relay_with_location);
- Self::tack_on_wireguard_tcp_relays(&mut relay_with_location.tunnels.wireguard);
+
+ // TODO: The WireGuard data is incorrectly modelled.
+ // Using a vector here suggests that a relay may use multiple key pairs at a
+ // time. This is incorrect and will never be the case.
+ //
+ // Currently, the `wireguard` vector will have 0 or 1 entries.
+ // This should be changed into e.g. using an Option<_> instead.
+ //
+
+ if !relay.tunnels.wireguard.is_empty() {
+ for port in UDP2TCP_PORTS {
+ relay_with_location
+ .obfuscators
+ .udp2tcp
+ .push(Udp2TcpEndpointData { port });
+ }
+ }
relays.push(relay_with_location);
}
@@ -150,25 +169,6 @@ impl ParsedRelays {
}
}
- /// Add synthesized WireGuard TCP endpoints to a relay
- fn tack_on_wireguard_tcp_relays(endpoints: &mut Vec<WireguardEndpointData>) {
- *endpoints = endpoints
- .iter()
- .cloned()
- .map(|udp_endpoint| {
- [
- WireguardEndpointData {
- protocol: TransportProtocol::Tcp,
- port_ranges: WIREGUARD_TCP_PORTS.to_vec(),
- ..udp_endpoint.clone()
- },
- udp_endpoint,
- ]
- })
- .flatten()
- .collect();
- }
-
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
log::debug!("Reading relays from {}", path.as_ref().display());
let (last_modified, file) =
@@ -570,11 +570,7 @@ impl RelaySelector {
}
if relay_constraints.wireguard_constraints.port.is_any() {
- relay_constraints.wireguard_constraints.port =
- Constraint::Only(TransportPort {
- protocol: preferred_protocol,
- port: preferred_port,
- });
+ relay_constraints.wireguard_constraints.port = preferred_port;
}
relay_constraints.tunnel_protocol = Constraint::Only(preferred_tunnel);
@@ -607,10 +603,7 @@ impl RelaySelector {
};
if relay_constraints.wireguard_constraints.port.is_any() {
- relay_constraints.wireguard_constraints.port = Constraint::Only(TransportPort {
- port: preferred_port,
- protocol: TransportProtocol::Udp,
- });
+ relay_constraints.wireguard_constraints.port = preferred_port;
}
relay_constraints.tunnel_protocol = Constraint::Only(preferred_tunnel);
@@ -713,6 +706,85 @@ impl RelaySelector {
})
}
+ pub fn get_obfuscator(
+ &self,
+ obfuscation_settings: &ObfuscationSettings,
+ relay: &Relay,
+ endpoint: &MullvadWireguardEndpoint,
+ retry_attempt: u32,
+ ) -> Option<ObfuscatorConfig> {
+ match obfuscation_settings.selected_obfuscation {
+ SelectedObfuscation::Auto => {
+ self.get_auto_obfuscator(obfuscation_settings, relay, endpoint, retry_attempt)
+ }
+ SelectedObfuscation::Off => None,
+ SelectedObfuscation::Udp2Tcp => self.get_udp2tcp_obfuscator(
+ &obfuscation_settings.udp2tcp,
+ relay,
+ endpoint,
+ retry_attempt,
+ ),
+ }
+ }
+
+ fn get_auto_obfuscator(
+ &self,
+ obfuscation_settings: &ObfuscationSettings,
+ relay: &Relay,
+ endpoint: &MullvadWireguardEndpoint,
+ retry_attempt: u32,
+ ) -> Option<ObfuscatorConfig> {
+ if !self.should_use_auto_obfuscator(retry_attempt) {
+ return None;
+ }
+ // TODO FIX: The third obfuscator entry will never be chosen
+ // Because get_auto_obfuscator_retry_attempt() returns [0, 1]
+ // And the udp2tcp endpoints are defined in a vector with entries [0, 1, 2]
+ self.get_udp2tcp_obfuscator(
+ &obfuscation_settings.udp2tcp,
+ relay,
+ endpoint,
+ self.get_auto_obfuscator_retry_attempt(retry_attempt)
+ .unwrap(),
+ )
+ }
+
+ pub fn should_use_auto_obfuscator(&self, retry_attempt: u32) -> bool {
+ self.get_auto_obfuscator_retry_attempt(retry_attempt)
+ .is_some()
+ }
+
+ fn get_auto_obfuscator_retry_attempt(&self, retry_attempt: u32) -> Option<u32> {
+ match retry_attempt % 4 {
+ 0 | 1 => None,
+ filtered_retry => Some(filtered_retry - 2),
+ }
+ }
+
+ fn get_udp2tcp_obfuscator(
+ &self,
+ obfuscation_settings: &Udp2TcpObfuscationSettings,
+ relay: &Relay,
+ _endpoint: &MullvadWireguardEndpoint,
+ retry_attempt: u32,
+ ) -> Option<ObfuscatorConfig> {
+ let udp2tcp_endpoint = if obfuscation_settings.port.is_only() {
+ relay
+ .obfuscators
+ .udp2tcp
+ .iter()
+ .find(|&candidate| obfuscation_settings.port.matches_eq(&candidate.port))
+ } else {
+ relay
+ .obfuscators
+ .udp2tcp
+ .get(retry_attempt as usize % relay.obfuscators.udp2tcp.len())
+ };
+ udp2tcp_endpoint.map(|udp2tcp_endpoint| ObfuscatorConfig::Udp2Tcp {
+ endpoint: SocketAddr::new(relay.ipv4_addr_in.into(), udp2tcp_endpoint.port),
+ })
+ }
+
/// Returns preferred constraints
#[allow(unused_variables)]
fn preferred_tunnel_constraints(
@@ -773,18 +845,14 @@ impl RelaySelector {
}
}
- fn preferred_wireguard_port(retry_attempt: u32) -> Constraint<TransportPort> {
+ fn preferred_wireguard_port(retry_attempt: u32) -> Constraint<u16> {
// This ensures that if after the first 2 failed attempts the daemon does not
// connect, then afterwards 2 of each 4 successive attempts will try to connect
// on port 53.
- let port = match retry_attempt % 4 {
+ match retry_attempt % 4 {
0 | 1 => Constraint::Any,
_ => Constraint::Only(53),
- };
- Constraint::Only(TransportPort {
- port,
- protocol: TransportProtocol::Udp,
- })
+ }
}
fn preferred_openvpn_constraints(retry_attempt: u32) -> (Constraint<u16>, TransportProtocol) {
@@ -963,7 +1031,7 @@ mod test {
relay_constraints::RelayConstraints,
relay_list::{
OpenVpnEndpointData, Relay, RelayBridges, RelayListCity, RelayListCountry,
- RelayTunnels, WireguardEndpointData,
+ RelayObfuscators, RelayTunnels, WireguardEndpointData,
},
};
use talpid_types::net::wireguard::PublicKey;
@@ -999,13 +1067,15 @@ mod test {
ipv4_gateway: "10.64.0.1".parse().unwrap(),
ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
public_key: PublicKey::from_base64("BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=").unwrap(),
- protocol: TransportProtocol::Udp,
},
],
},
bridges: RelayBridges {
shadowsocks: vec![],
},
+ obfuscators: RelayObfuscators {
+ udp2tcp: vec![],
+ },
location: None,
},
Relay {
@@ -1025,13 +1095,15 @@ mod test {
ipv4_gateway: "10.64.0.1".parse().unwrap(),
ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
public_key: PublicKey::from_base64("veGD6/aEY6sMfN3Ls7YWPmNgu3AheO7nQqsFT47YSws=").unwrap(),
- protocol: TransportProtocol::Udp,
},
],
},
bridges: RelayBridges {
shadowsocks: vec![],
},
+ obfuscators: RelayObfuscators {
+ udp2tcp: vec![],
+ },
location: None,
},
Relay {
@@ -1063,6 +1135,9 @@ mod test {
bridges: RelayBridges {
shadowsocks: vec![],
},
+ obfuscators: RelayObfuscators {
+ udp2tcp: vec![],
+ },
location: None,
},
Relay {
@@ -1082,13 +1157,15 @@ mod test {
ipv4_gateway: "10.64.0.1".parse().unwrap(),
ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
public_key: PublicKey::from_base64("veGD6/aEY6sMfN3Ls7YWPmNgu3AheO7nQqsFT47YSws=").unwrap(),
- protocol: TransportProtocol::Udp,
},
],
},
bridges: RelayBridges {
shadowsocks: vec![],
},
+ obfuscators: RelayObfuscators {
+ udp2tcp: vec![],
+ },
location: None,
},
Relay {
@@ -1110,6 +1187,9 @@ mod test {
bridges: RelayBridges {
shadowsocks: vec![],
},
+ obfuscators: RelayObfuscators {
+ udp2tcp: vec![],
+ },
location: None,
}
],
@@ -1436,102 +1516,134 @@ mod test {
},
};
+ const WIREGUARD_SINGLEHOP_CONSTRAINTS: RelayConstraints = RelayConstraints {
+ location: Constraint::Any,
+ providers: Constraint::Any,
+ wireguard_constraints: WireguardConstraints {
+ use_multihop: false,
+ port: Constraint::Any,
+ ip_version: Constraint::Any,
+ entry_location: Constraint::Any,
+ },
+ tunnel_protocol: Constraint::Only(TunnelType::Wireguard),
+ openvpn_constraints: OpenVpnConstraints {
+ port: Constraint::Any,
+ },
+ };
+
#[test]
fn test_selecting_wireguard_location_will_consider_multihop() {
let relay_selector = new_relay_selector();
let result = relay_selector.get_tunnel_endpoint(&WIREGUARD_MULTIHOP_CONSTRAINTS, BridgeState::Off, 0)
-
- .expect("Failed to get relay when tunnel constraints are set to Any and retrying the selection");
+ .expect("Failed to get relay when tunnel constraints are set to default WireGuard multihop constraints");
assert!(result.entry_relay.is_some());
- let endpoint = result.endpoint.unwrap_wireguard();
- assert!(matches!(endpoint.peer.protocol, TransportProtocol::Udp));
- assert!(matches!(
- endpoint.exit_peer.as_ref().unwrap().protocol,
- TransportProtocol::Udp
- ));
+ // TODO: Verify that neither endpoint is using obfuscation for retry attempt 0
}
#[test]
- fn test_selecting_wg_multihop_tcp() {
- let mut relay_constraints = WIREGUARD_MULTIHOP_CONSTRAINTS.clone();
- relay_constraints.wireguard_constraints.port = Constraint::Only(TransportPort {
- port: Constraint::Any,
- protocol: TransportProtocol::Tcp,
- });
-
+ fn test_selecting_wg_endpoint_with_udp2tcp_obfuscation() {
let relay_selector = new_relay_selector();
- let result = relay_selector
- .get_tunnel_endpoint(&relay_constraints, BridgeState::Off, 0)
- .expect("Failed to get WireGuard TCP multihop relay");
+ let result = relay_selector.get_tunnel_endpoint(&WIREGUARD_SINGLEHOP_CONSTRAINTS, BridgeState::Off, 0)
+ .expect("Failed to get relay when tunnel constraints are set to default WireGuard constraints");
+
+ assert!(result.entry_relay.is_none());
+ assert!(matches!(result.endpoint, MullvadEndpoint::Wireguard { .. }));
+
+ let obfs_settings = ObfuscationSettings {
+ selected_obfuscation: SelectedObfuscation::Udp2Tcp,
+ ..ObfuscationSettings::default()
+ };
+
+ let obfs_config = relay_selector.get_obfuscator(
+ &obfs_settings,
+ &result.exit_relay,
+ result.endpoint.unwrap_wireguard(),
+ 0,
+ );
- assert!(result.entry_relay.is_some());
- let endpoint = result.endpoint.unwrap_wireguard();
- assert!(matches!(endpoint.peer.protocol, TransportProtocol::Tcp));
assert!(matches!(
- endpoint.exit_peer.as_ref().unwrap().protocol,
- TransportProtocol::Udp
+ obfs_config.unwrap(),
+ ObfuscatorConfig::Udp2Tcp { .. }
));
}
#[test]
- fn test_selecting_wg_tcp() {
- let relay_constraints = RelayConstraints {
- wireguard_constraints: WireguardConstraints {
- port: Constraint::Only(TransportPort {
- port: Constraint::Any,
- protocol: TransportProtocol::Tcp,
- }),
- ..WireguardConstraints::default()
- },
- tunnel_protocol: Constraint::Only(TunnelType::Wireguard),
- ..RelayConstraints::default()
+ fn test_selecting_wg_endpoint_with_auto_obfuscation() {
+ let relay_selector = new_relay_selector();
+
+ let result = relay_selector.get_tunnel_endpoint(&WIREGUARD_SINGLEHOP_CONSTRAINTS, BridgeState::Off, 0)
+ .expect("Failed to get relay when tunnel constraints are set to default WireGuard constraints");
+
+ assert!(result.entry_relay.is_none());
+ assert!(matches!(result.endpoint, MullvadEndpoint::Wireguard { .. }));
+
+ let obfs_settings = ObfuscationSettings {
+ selected_obfuscation: SelectedObfuscation::Auto,
+ ..ObfuscationSettings::default()
};
- let relay_selector = new_relay_selector();
+ assert!(relay_selector
+ .get_obfuscator(
+ &obfs_settings,
+ &result.exit_relay,
+ result.endpoint.unwrap_wireguard(),
+ 0,
+ )
+ .is_none());
- let result = relay_selector
- .get_tunnel_endpoint(&relay_constraints, BridgeState::Off, 0)
- .expect("Failed to get WireGuard TCP relay");
- let endpoint = result.endpoint.unwrap_wireguard();
- assert!(matches!(endpoint.peer.protocol, TransportProtocol::Tcp));
- assert!(endpoint.exit_peer.is_none());
+ assert!(relay_selector
+ .get_obfuscator(
+ &obfs_settings,
+ &result.exit_relay,
+ result.endpoint.unwrap_wireguard(),
+ 1,
+ )
+ .is_none());
+
+ assert!(relay_selector
+ .get_obfuscator(
+ &obfs_settings,
+ &result.exit_relay,
+ result.endpoint.unwrap_wireguard(),
+ 2,
+ )
+ .is_some());
}
#[test]
- fn test_selecting_wg_multihop_ports() {
- let mut relay_constraints = WIREGUARD_MULTIHOP_CONSTRAINTS.clone();
+ fn test_selected_endpoints_use_correct_port_ranges() {
let relay_selector = new_relay_selector();
- const INVALID_UDP_PORTS: [u16; 2] = [80, 443];
- for attempt in 0..1000 {
- let result = relay_selector
- .get_tunnel_endpoint(&relay_constraints, BridgeState::Off, attempt)
- .expect("Failed to get WireGuard TCP multihop relay");
- assert!(!INVALID_UDP_PORTS.contains(&result.endpoint.to_endpoint().address.port()));
- assert_eq!(
- result.endpoint.unwrap_wireguard().peer.protocol,
- TransportProtocol::Udp
- );
- }
+ const TCP2UDP_PORTS: [u16; 3] = [80, 443, 5001];
- relay_constraints.wireguard_constraints.port = Constraint::Only(TransportPort {
- port: Constraint::Any,
- protocol: TransportProtocol::Tcp,
- });
+ let obfs_settings = ObfuscationSettings {
+ selected_obfuscation: SelectedObfuscation::Udp2Tcp,
+ ..ObfuscationSettings::default()
+ };
- const VALID_TCP_PORTS: [u16; 3] = [80, 443, 5001];
for attempt in 0..1000 {
let result = relay_selector
- .get_tunnel_endpoint(&relay_constraints, BridgeState::Off, attempt)
- .expect("Failed to get WireGuard TCP multihop relay");
- assert!(VALID_TCP_PORTS.contains(&result.endpoint.to_endpoint().address.port()));
- assert_eq!(
- result.endpoint.unwrap_wireguard().peer.protocol,
- TransportProtocol::Tcp
- );
+ .get_tunnel_endpoint(&WIREGUARD_SINGLEHOP_CONSTRAINTS, BridgeState::Off, attempt)
+ .expect("Failed to select a WireGuard relay");
+ assert!(result.entry_relay.is_none());
+ assert!(!TCP2UDP_PORTS.contains(&result.endpoint.to_endpoint().address.port()));
+
+ let obfs_config = relay_selector
+ .get_obfuscator(
+ &obfs_settings,
+ &result.exit_relay,
+ result.endpoint.unwrap_wireguard(),
+ attempt,
+ )
+ .expect("Failed to get Tcp2Udp endpoint");
+
+ assert!(matches!(obfs_config, ObfuscatorConfig::Udp2Tcp { .. }));
+
+ let ObfuscatorConfig::Udp2Tcp { endpoint } = obfs_config;
+ assert!(TCP2UDP_PORTS.contains(&endpoint.port()));
}
}
diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs
index bf3fe710c8..52800028cc 100644
--- a/mullvad-daemon/src/settings.rs
+++ b/mullvad-daemon/src/settings.rs
@@ -1,7 +1,7 @@
#[cfg(not(target_os = "android"))]
use futures::TryFutureExt;
use mullvad_types::{
- relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate},
+ relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
settings::{DnsOptions, Settings},
wireguard::RotationInterval,
};
@@ -320,6 +320,18 @@ impl SettingsPersister {
}
}
+ pub async fn set_obfuscation_settings(
+ &mut self,
+ obfuscation_settings: ObfuscationSettings,
+ ) -> Result<bool, Error> {
+ let should_save = Self::update_field(
+ &mut self.settings.obfuscation_settings,
+ obfuscation_settings,
+ );
+
+ self.update(should_save).await
+ }
+
async fn update(&mut self, should_save: bool) -> Result<bool, Error> {
if should_save {
self.save().await.map(|_| true)
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 21eb6ab512..07e2d4e8c7 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -30,6 +30,7 @@ service ManagementService {
rpc GetCurrentLocation(google.protobuf.Empty) returns (GeoIpLocation) {}
rpc SetBridgeSettings(BridgeSettings) returns (google.protobuf.Empty) {}
rpc SetBridgeState(BridgeState) returns (google.protobuf.Empty) {}
+ rpc SetObfuscationSettings(ObfuscationSettings) returns (google.protobuf.Empty) {}
// Settings
rpc GetSettings(google.protobuf.Empty) returns (Settings) {}
@@ -190,7 +191,19 @@ message TunnelEndpoint {
TransportProtocol protocol = 2;
TunnelType tunnel_type = 3;
ProxyEndpoint proxy = 4;
- Endpoint entry_endpoint = 5;
+ ObfuscationEndpoint obfuscation = 5;
+ Endpoint entry_endpoint = 6;
+}
+
+enum ObfuscationType {
+ UDP2TCP = 0;
+}
+
+message ObfuscationEndpoint {
+ string address = 1;
+ uint32 port = 2;
+ TransportProtocol protocol = 3;
+ ObfuscationType obfuscation_type = 4;
}
enum ProxyType {
@@ -269,6 +282,20 @@ message BridgeState {
State state = 1;
}
+message Udp2TcpObfuscationSettings {
+ uint32 port = 1;
+}
+
+message ObfuscationSettings {
+ enum SelectedObfuscation {
+ AUTO = 0;
+ OFF = 1;
+ UDP2TCP = 2;
+ }
+ SelectedObfuscation selected_obfuscation = 1;
+ Udp2TcpObfuscationSettings udp2tcp = 2;
+}
+
message Settings {
RelaySettings relay_settings = 1;
BridgeSettings bridge_settings = 2;
@@ -279,6 +306,7 @@ message Settings {
TunnelOptions tunnel_options = 7;
bool show_beta_releases = 8;
SplitTunnelSettings split_tunnel = 9;
+ ObfuscationSettings obfuscation_settings = 10;
}
message SplitTunnelSettings {
@@ -341,7 +369,7 @@ message IpVersionConstraint {
}
message WireguardConstraints {
- TransportPort port = 1;
+ uint32 port = 1;
IpVersionConstraint ip_version = 2;
bool use_multihop = 3;
RelayLocation entry_location = 4;
@@ -368,7 +396,6 @@ message ConnectionConfig {
bytes public_key = 1;
repeated string allowed_ips = 2;
string endpoint = 3;
- TransportProtocol protocol = 4;
}
TunnelConfig tunnel = 1;
diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs
index c76ada98d1..d62958e78d 100644
--- a/mullvad-management-interface/src/types.rs
+++ b/mullvad-management-interface/src/types.rs
@@ -42,6 +42,18 @@ impl From<talpid_types::net::TunnelEndpoint> for TunnelEndpoint {
net::proxy::ProxyType::Custom => i32::from(ProxyType::Custom),
},
}),
+ obfuscation: endpoint
+ .obfuscation
+ .map(|obfuscation_endpoint| ObfuscationEndpoint {
+ address: obfuscation_endpoint.endpoint.address.ip().to_string(),
+ port: u32::from(obfuscation_endpoint.endpoint.address.port()),
+ protocol: i32::from(TransportProtocol::from(
+ obfuscation_endpoint.endpoint.protocol,
+ )),
+ obfuscation_type: match obfuscation_endpoint.obfuscation_type {
+ net::ObfuscationType::Udp2Tcp => i32::from(ObfuscationType::Udp2tcp),
+ },
+ }),
entry_endpoint: endpoint.entry_endpoint.map(|entry| Endpoint {
address: entry.address.to_string(),
protocol: i32::from(TransportProtocol::from(entry.protocol)),
@@ -308,7 +320,6 @@ impl From<mullvad_types::ConnectionConfig> for ConnectionConfig {
.map(|address| address.to_string())
.collect(),
endpoint: config.peer.endpoint.to_string(),
- protocol: i32::from(TransportProtocol::from(config.peer.protocol)),
}),
ipv4_gateway: config.ipv4_gateway.to_string(),
ipv6_gateway: config
@@ -431,6 +442,7 @@ impl From<&mullvad_types::settings::Settings> for Settings {
auto_connect: settings.auto_connect,
tunnel_options: Some(TunnelOptions::from(&settings.tunnel_options)),
show_beta_releases: settings.show_beta_releases,
+ obfuscation_settings: Some(ObfuscationSettings::from(&settings.obfuscation_settings)),
split_tunnel,
}
}
@@ -449,6 +461,31 @@ impl From<mullvad_types::relay_constraints::BridgeState> for BridgeState {
}
}
+impl From<&mullvad_types::relay_constraints::ObfuscationSettings> for ObfuscationSettings {
+ fn from(settings: &mullvad_types::relay_constraints::ObfuscationSettings) -> Self {
+ use mullvad_types::relay_constraints::SelectedObfuscation;
+ let selected_obfuscation = i32::from(match settings.selected_obfuscation {
+ SelectedObfuscation::Auto => obfuscation_settings::SelectedObfuscation::Auto,
+ SelectedObfuscation::Off => obfuscation_settings::SelectedObfuscation::Off,
+ SelectedObfuscation::Udp2Tcp => obfuscation_settings::SelectedObfuscation::Udp2tcp,
+ });
+ Self {
+ selected_obfuscation,
+ udp2tcp: Some(Udp2TcpObfuscationSettings::from(&settings.udp2tcp)),
+ }
+ }
+}
+
+impl From<&mullvad_types::relay_constraints::Udp2TcpObfuscationSettings>
+ for Udp2TcpObfuscationSettings
+{
+ fn from(settings: &mullvad_types::relay_constraints::Udp2TcpObfuscationSettings) -> Self {
+ Self {
+ port: u32::from(settings.port.unwrap_or(0)),
+ }
+ }
+}
+
impl From<mullvad_types::relay_constraints::BridgeSettings> for BridgeSettings {
fn from(settings: mullvad_types::relay_constraints::BridgeSettings) -> Self {
use mullvad_types::relay_constraints::BridgeSettings as MullvadBridgeSettings;
@@ -529,11 +566,7 @@ impl From<mullvad_types::relay_constraints::RelaySettings> for RelaySettings {
}),
wireguard_constraints: Some(WireguardConstraints {
- port: constraints
- .wireguard_constraints
- .port
- .option()
- .map(TransportPort::from),
+ port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)),
ip_version: constraints
.wireguard_constraints
.ip_version
@@ -756,10 +789,6 @@ impl TryFrom<&WireguardConstraints> for mullvad_types::relay_constraints::Wiregu
use mullvad_types::relay_constraints as mullvad_constraints;
use talpid_types::net;
- let wireguard_transport_port = match &constraints.port {
- Some(port) => Some(mullvad_constraints::TransportPort::try_from(port.clone())?),
- None => None,
- };
let ip_version = match &constraints.ip_version {
Some(constraint) => match IpVersion::from_i32(constraint.protocol) {
Some(IpVersion::V4) => Some(net::IpVersion::V4),
@@ -774,7 +803,11 @@ impl TryFrom<&WireguardConstraints> for mullvad_types::relay_constraints::Wiregu
};
Ok(mullvad_constraints::WireguardConstraints {
- port: Constraint::from(wireguard_transport_port),
+ port: if constraints.port == 0 {
+ Constraint::Any
+ } else {
+ Constraint::Only(constraints.port as u16)
+ },
ip_version: Constraint::from(ip_version),
use_multihop: constraints.use_multihop,
entry_location: constraints
@@ -1091,7 +1124,6 @@ impl TryFrom<ConnectionConfig> for mullvad_types::ConnectionConfig {
public_key,
allowed_ips,
endpoint,
- protocol: try_transport_protocol_from_i32(peer.protocol)?,
},
exit_peer: None,
ipv4_gateway,
@@ -1204,6 +1236,58 @@ impl TryFrom<BridgeSettings> for mullvad_types::relay_constraints::BridgeSetting
}
}
+impl TryFrom<ObfuscationSettings> for mullvad_types::relay_constraints::ObfuscationSettings {
+ type Error = FromProtobufTypeError;
+
+ fn try_from(settings: ObfuscationSettings) -> Result<Self, Self::Error> {
+ use mullvad_types::relay_constraints::SelectedObfuscation;
+ use obfuscation_settings::SelectedObfuscation as IpcSelectedObfuscation;
+ let selected_obfuscation =
+ match IpcSelectedObfuscation::from_i32(settings.selected_obfuscation) {
+ Some(IpcSelectedObfuscation::Auto) => SelectedObfuscation::Auto,
+ Some(IpcSelectedObfuscation::Off) => SelectedObfuscation::Off,
+ Some(IpcSelectedObfuscation::Udp2tcp) => SelectedObfuscation::Udp2Tcp,
+ None => {
+ return Err(FromProtobufTypeError::InvalidArgument(
+ "invalid selected obfuscator",
+ ));
+ }
+ };
+
+ let udp2tcp = match settings.udp2tcp {
+ Some(settings) => {
+ mullvad_types::relay_constraints::Udp2TcpObfuscationSettings::try_from(&settings)?
+ }
+ None => {
+ return Err(FromProtobufTypeError::InvalidArgument(
+ "invalid selected obfuscator",
+ ));
+ }
+ };
+
+ Ok(Self {
+ selected_obfuscation,
+ udp2tcp,
+ })
+ }
+}
+
+impl TryFrom<&Udp2TcpObfuscationSettings>
+ for mullvad_types::relay_constraints::Udp2TcpObfuscationSettings
+{
+ type Error = FromProtobufTypeError;
+
+ fn try_from(settings: &Udp2TcpObfuscationSettings) -> Result<Self, Self::Error> {
+ Ok(Self {
+ port: if settings.port == 0 {
+ Constraint::Any
+ } else {
+ Constraint::Only(settings.port as u16)
+ },
+ })
+ }
+}
+
impl TryFrom<BridgeState> for mullvad_types::relay_constraints::BridgeState {
type Error = FromProtobufTypeError;
diff --git a/mullvad-types/src/custom_tunnel.rs b/mullvad-types/src/custom_tunnel.rs
index cded541018..7cbfcc609f 100644
--- a/mullvad-types/src/custom_tunnel.rs
+++ b/mullvad-types/src/custom_tunnel.rs
@@ -60,6 +60,7 @@ impl CustomTunnelEndpoint {
connection,
options: tunnel_options.wireguard.options.clone(),
generic_options: tunnel_options.generic.clone(),
+ obfuscation: None,
}
.into(),
};
diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs
index 6962951eb1..0ee7452c60 100644
--- a/mullvad-types/src/relay_constraints.rs
+++ b/mullvad-types/src/relay_constraints.rs
@@ -426,7 +426,7 @@ impl Match<OpenVpnEndpointData> for OpenVpnConstraints {
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(default)]
pub struct WireguardConstraints {
- pub port: Constraint<TransportPort>,
+ pub port: Constraint<u16>,
pub ip_version: Constraint<IpVersion>,
pub use_multihop: bool,
pub entry_location: Constraint<LocationConstraint>,
@@ -436,13 +436,7 @@ impl fmt::Display for WireguardConstraints {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self.port {
Constraint::Any => write!(f, "any port")?,
- Constraint::Only(port) => {
- match port.port {
- Constraint::Any => write!(f, "any port")?,
- Constraint::Only(port) => write!(f, "port {}", port)?,
- }
- write!(f, " over {}", port.protocol)?;
- }
+ Constraint::Only(port) => write!(f, "port {}", port)?,
}
write!(f, " over ")?;
match self.ip_version {
@@ -470,6 +464,75 @@ pub enum BridgeSettings {
Custom(ProxySettings),
}
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum SelectedObfuscation {
+ Auto,
+ Off,
+ Udp2Tcp,
+}
+
+impl Default for SelectedObfuscation {
+ fn default() -> Self {
+ SelectedObfuscation::Off
+ }
+}
+
+impl fmt::Display for SelectedObfuscation {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ SelectedObfuscation::Auto => "auto",
+ SelectedObfuscation::Off => "off",
+ SelectedObfuscation::Udp2Tcp => "udp2tcp",
+ }
+ )
+ }
+}
+
+#[derive(Default, Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub struct Udp2TcpObfuscationSettings {
+ pub port: Constraint<u16>,
+}
+
+impl fmt::Display for Udp2TcpObfuscationSettings {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "port: {}",
+ match self.port {
+ Constraint::Any => "any".to_string(),
+ Constraint::Only(port) => port.to_string(),
+ }
+ )?;
+ Ok(())
+ }
+}
+
+/// Contains obfuscation settings
+#[derive(Default, Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
+#[serde(default)]
+pub struct ObfuscationSettings {
+ pub selected_obfuscation: SelectedObfuscation,
+ pub udp2tcp: Udp2TcpObfuscationSettings,
+}
+
+impl fmt::Display for ObfuscationSettings {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ write!(f, "selected obfuscation: ")?;
+ match self.selected_obfuscation {
+ SelectedObfuscation::Auto => write!(f, "auto")?,
+ SelectedObfuscation::Off => write!(f, "off")?,
+ SelectedObfuscation::Udp2Tcp => write!(f, "Udp2Tcp ({})", self.udp2tcp)?,
+ };
+ Ok(())
+ }
+}
+
/// Limits the set of bridge servers to use in `mullvad-daemon`.
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(default)]
diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs
index e638749fa5..e5a40a8f4e 100644
--- a/mullvad-types/src/relay_list.rs
+++ b/mullvad-types/src/relay_list.rs
@@ -83,6 +83,9 @@ pub struct Relay {
#[serde(skip_serializing_if = "RelayBridges::is_empty", default)]
#[cfg_attr(target_os = "android", jnix(skip))]
pub bridges: RelayBridges,
+ #[serde(skip_serializing_if = "RelayObfuscators::is_empty", default)]
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ pub obfuscators: RelayObfuscators,
#[cfg_attr(target_os = "android", jnix(skip))]
pub location: Option<Location>,
}
@@ -141,23 +144,15 @@ pub struct WireguardEndpointData {
pub ipv6_gateway: Ipv6Addr,
/// The peer's public key
pub public_key: wireguard::PublicKey,
- #[serde(default = "default_wg_transport")]
- #[serde(skip)]
- pub protocol: TransportProtocol,
-}
-
-fn default_wg_transport() -> TransportProtocol {
- TransportProtocol::Udp
}
impl fmt::Display for WireguardEndpointData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
- "gateways {} - {} {} port_ranges {{ {} }} public_key {}",
+ "gateways {} - {} port_ranges {{ {} }} public_key {}",
self.ipv4_gateway,
self.ipv6_gateway,
- self.protocol,
self.port_ranges
.iter()
.map(|range| format!("[{} - {}]", range.0, range.1))
@@ -203,3 +198,24 @@ impl ShadowsocksEndpointData {
})
}
}
+
+#[derive(Debug, Default, Clone, Deserialize, Serialize)]
+#[serde(default)]
+pub struct RelayObfuscators {
+ pub udp2tcp: Vec<Udp2TcpEndpointData>,
+}
+
+impl RelayObfuscators {
+ pub fn is_empty(&self) -> bool {
+ self.udp2tcp.is_empty()
+ }
+
+ pub fn clear(&mut self) {
+ self.udp2tcp.clear();
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
+pub struct Udp2TcpEndpointData {
+ pub port: u16,
+}
diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs
index 63ccb480a2..c2f0a17781 100644
--- a/mullvad-types/src/settings/mod.rs
+++ b/mullvad-types/src/settings/mod.rs
@@ -1,7 +1,8 @@
use crate::{
relay_constraints::{
BridgeConstraints, BridgeSettings, BridgeState, Constraint, LocationConstraint,
- RelayConstraints, RelaySettings, RelaySettingsUpdate,
+ ObfuscationSettings, RelayConstraints, RelaySettings, RelaySettingsUpdate,
+ SelectedObfuscation,
},
wireguard,
};
@@ -65,6 +66,8 @@ pub struct Settings {
#[cfg_attr(target_os = "android", jnix(skip))]
pub bridge_settings: BridgeSettings,
#[cfg_attr(target_os = "android", jnix(skip))]
+ pub obfuscation_settings: ObfuscationSettings,
+ #[cfg_attr(target_os = "android", jnix(skip))]
bridge_state: BridgeState,
/// If the daemon should allow communication with private (LAN) networks.
pub allow_lan: bool,
@@ -104,6 +107,10 @@ impl Default for Settings {
..Default::default()
}),
bridge_settings: BridgeSettings::Normal(BridgeConstraints::default()),
+ obfuscation_settings: ObfuscationSettings {
+ selected_obfuscation: SelectedObfuscation::Off,
+ ..Default::default()
+ },
bridge_state: BridgeState::Auto,
allow_lan: false,
block_when_disconnected: false,
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 9dd56ac5c0..d0cf685753 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -31,7 +31,7 @@ chrono = "0.4.19"
tokio = { version = "1.8", features = ["process", "rt-multi-thread", "fs"] }
tokio-stream = { version = "0.1", features = ["io-util"] }
rand = "0.7"
-udp-over-tcp = { git = "https://github.com/mullvad/udp-over-tcp", rev = "1e27324362ed123b61fa2062b1599e5f9d569796" }
+tunnel-obfuscation = { path = "../tunnel-obfuscation" }
socket2 = { version = "0.4.2", features = ["all"] }
[target.'cfg(not(target_os="android"))'.dependencies]
diff --git a/talpid-core/src/tunnel/wireguard/config.rs b/talpid-core/src/tunnel/wireguard/config.rs
index 307a713272..7566d5c141 100644
--- a/talpid-core/src/tunnel/wireguard/config.rs
+++ b/talpid-core/src/tunnel/wireguard/config.rs
@@ -3,7 +3,7 @@ use std::{
ffi::CString,
net::{Ipv4Addr, Ipv6Addr},
};
-use talpid_types::net::{wireguard, GenericTunnelOptions};
+use talpid_types::net::{obfuscation::ObfuscatorConfig, wireguard, GenericTunnelOptions};
/// Config required to set up a single WireGuard tunnel
pub struct Config {
@@ -26,6 +26,8 @@ pub struct Config {
/// Temporary switch for wireguard-nt
#[cfg(target_os = "windows")]
pub use_wireguard_nt: bool,
+ /// Obfuscator config to be used for reaching the relay.
+ pub obfuscator_config: Option<ObfuscatorConfig>,
}
const DEFAULT_MTU: u16 = 1380;
@@ -60,6 +62,7 @@ impl Config {
&params.connection,
&params.options,
&params.generic_options,
+ params.obfuscation.clone(),
)
}
@@ -70,6 +73,7 @@ impl Config {
connection_config: &wireguard::ConnectionConfig,
wg_options: &wireguard::TunnelOptions,
generic_options: &GenericTunnelOptions,
+ obfuscator_config: Option<ObfuscatorConfig>,
) -> Result<Config, Error> {
if peers.is_empty() {
return Err(Error::NoPeersSuppliedError);
@@ -114,6 +118,7 @@ impl Config {
enable_ipv6: generic_options.enable_ipv6,
#[cfg(target_os = "windows")]
use_wireguard_nt: wg_options.use_wireguard_nt,
+ obfuscator_config,
})
}
diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs
index 263bbaeabb..59098a00ca 100644
--- a/talpid-core/src/tunnel/wireguard/mod.rs
+++ b/talpid-core/src/tunnel/wireguard/mod.rs
@@ -5,7 +5,10 @@ use super::{tun_provider::TunProvider, TunnelEvent, TunnelMetadata};
use crate::routing::{self, RequiredRoute, RouteManagerHandle};
#[cfg(windows)]
use futures::{channel::mpsc, StreamExt};
-use futures::{channel::oneshot, future::abortable};
+use futures::{
+ channel::oneshot,
+ future::{abortable, AbortHandle as FutureAbortHandle},
+};
#[cfg(target_os = "linux")]
use lazy_static::lazy_static;
#[cfg(target_os = "linux")]
@@ -16,14 +19,16 @@ use std::env;
use std::io;
use std::{
convert::Infallible,
- net::{IpAddr, SocketAddr},
+ net::IpAddr,
path::Path,
sync::{mpsc as sync_mpsc, Arc, Mutex},
};
#[cfg(windows)]
use talpid_types::BoxedError;
-use talpid_types::{net::TransportProtocol, ErrorExt};
-use udp_over_tcp::{TcpOptions, Udp2Tcp};
+use talpid_types::{net::obfuscation::ObfuscatorConfig, ErrorExt};
+use tunnel_obfuscation::{
+ create_obfuscator, Error as ObfuscationError, Settings as ObfuscationSettings, Udp2TcpSettings,
+};
/// WireGuard config data-types
pub mod config;
@@ -56,13 +61,13 @@ pub enum Error {
#[error(display = "Tunnel failed")]
TunnelError(#[error(source)] TunnelError),
- /// Failed to set up Udp2Tcp
- #[error(display = "Failed to start UDP-over-TCP proxy")]
- Udp2TcpError(#[error(source)] udp_over_tcp::udp2tcp::ConnectError),
+ /// Failed to create tunnel obfuscator
+ #[error(display = "Failed to create tunnel obfuscator")]
+ CreateObfuscatorError(#[error(source)] ObfuscationError),
- /// Failed to obtain the local UDP socket address
- #[error(display = "Failed obtain local address for the UDP socket in Udp2Tcp")]
- GetLocalUdpAddress(#[error(source)] std::io::Error),
+ /// Failed to run tunnel obfuscator
+ #[error(display = "Tunnel obfuscator failed")]
+ ObfuscatorError(#[error(source)] ObfuscationError),
/// Failed to set up connectivity monitor
#[error(display = "Connectivity monitor failed")]
@@ -93,7 +98,24 @@ pub struct WireguardMonitor {
>,
close_msg_receiver: sync_mpsc::Receiver<CloseMsg>,
pinger_stop_sender: sync_mpsc::Sender<()>,
- _tcp_proxies: Vec<TcpProxy>,
+ _obfuscator: Option<ObfuscatorHandle>,
+}
+
+/// Simple wrapper that automatically cancels the future which runs an obfuscator.
+struct ObfuscatorHandle {
+ abort_handle: FutureAbortHandle,
+}
+
+impl ObfuscatorHandle {
+ pub fn new(abort_handle: FutureAbortHandle) -> Self {
+ Self { abort_handle }
+ }
+}
+
+impl Drop for ObfuscatorHandle {
+ fn drop(&mut self) {
+ self.abort_handle.abort();
+ }
}
#[cfg(target_os = "linux")]
@@ -108,52 +130,51 @@ lazy_static! {
.unwrap_or(false);
}
-struct TcpProxy {
- local_addr: SocketAddr,
- abort_handle: futures::future::AbortHandle,
-}
-
-impl TcpProxy {
- pub fn new(runtime: &tokio::runtime::Handle, endpoint: SocketAddr) -> Result<Self> {
- let listen_addr = if endpoint.is_ipv4() {
- SocketAddr::new("127.0.0.1".parse().unwrap(), 0)
- } else {
- SocketAddr::new("::1".parse().unwrap(), 0)
- };
+fn maybe_create_obfuscator(
+ runtime: &tokio::runtime::Handle,
+ config: &mut Config,
+ close_msg_sender: sync_mpsc::Sender<CloseMsg>,
+) -> Result<Option<ObfuscatorHandle>> {
+ // There are one or two peers.
+ // The first one is always the entry relay.
+ let mut first_peer = config.peers.get_mut(0).expect("missing peer");
- let udp2tcp = runtime
- .block_on(Udp2Tcp::new(
- listen_addr,
- endpoint,
- TcpOptions {
+ if let Some(ref obfuscator_config) = config.obfuscator_config {
+ match obfuscator_config {
+ ObfuscatorConfig::Udp2Tcp { endpoint } => {
+ log::trace!("Connecting to Udp2Tcp endpoint {:?}", *endpoint);
+ let settings = Udp2TcpSettings {
+ peer: *endpoint,
#[cfg(target_os = "linux")]
fwmark: Some(crate::linux::TUNNEL_FW_MARK),
- ..TcpOptions::default()
- },
- ))
- .map_err(Error::Udp2TcpError)?;
- let local_addr = udp2tcp
- .local_udp_addr()
- .map_err(Error::GetLocalUdpAddress)?;
-
- let (udp2tcp_future, abort_handle) = abortable(udp2tcp.run());
- runtime.spawn(udp2tcp_future);
-
- Ok(Self {
- local_addr,
- abort_handle,
- })
- }
-
- pub fn local_udp_addr(&self) -> SocketAddr {
- self.local_addr
- }
-}
-
-impl Drop for TcpProxy {
- fn drop(&mut self) {
- self.abort_handle.abort();
+ };
+ let obfuscator = runtime
+ .block_on(create_obfuscator(&ObfuscationSettings::Udp2Tcp(settings)))
+ .map_err(Error::CreateObfuscatorError)?;
+ let endpoint = obfuscator.endpoint();
+ log::trace!("Patching first WireGuard peer to become {:?}", endpoint);
+ first_peer.endpoint = endpoint;
+ let (runner, abort_handle) = abortable(async move {
+ match obfuscator.run().await {
+ Ok(_) => {
+ let _ = close_msg_sender.send(CloseMsg::ObfuscatorExpired);
+ }
+ Err(error) => {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Obfuscation controller failed")
+ );
+ let _ = close_msg_sender
+ .send(CloseMsg::ObfuscatorFailed(Error::ObfuscatorError(error)));
+ }
+ }
+ });
+ runtime.spawn(runner);
+ return Ok(Some(ObfuscatorHandle::new(abort_handle)));
+ }
+ }
}
+ Ok(None)
}
impl WireguardMonitor {
@@ -175,19 +196,11 @@ impl WireguardMonitor {
retry_attempt: u32,
tunnel_close_rx: oneshot::Receiver<()>,
) -> Result<WireguardMonitor> {
- let mut tcp_proxies = vec![];
- let mut endpoint_addrs = vec![];
-
- for peer in &mut config.peers {
- endpoint_addrs.push(peer.endpoint.ip());
- if peer.protocol == TransportProtocol::Tcp {
- let udp2tcp = TcpProxy::new(&runtime, peer.endpoint.clone())?;
+ let endpoint_addrs: Vec<IpAddr> =
+ config.peers.iter().map(|peer| peer.endpoint.ip()).collect();
+ let (close_msg_sender, close_msg_receiver) = sync_mpsc::channel();
- // Replace remote peer with proxy
- peer.endpoint = udp2tcp.local_udp_addr();
- tcp_proxies.push(udp2tcp);
- }
- }
+ let obfuscator = maybe_create_obfuscator(&runtime, &mut config, close_msg_sender.clone())?;
#[cfg(target_os = "windows")]
let (setup_done_tx, mut setup_done_rx) = mpsc::channel(0);
@@ -203,7 +216,6 @@ impl WireguardMonitor {
let iface_name = tunnel.get_interface_name().to_string();
let event_callback = Box::new(on_event.clone());
- let (close_msg_sender, close_msg_receiver) = sync_mpsc::channel();
let (pinger_tx, pinger_rx) = sync_mpsc::channel();
let monitor = WireguardMonitor {
runtime: runtime.clone(),
@@ -211,7 +223,7 @@ impl WireguardMonitor {
event_callback,
close_msg_receiver,
pinger_stop_sender: pinger_tx,
- _tcp_proxies: tcp_proxies,
+ _obfuscator: obfuscator,
};
let gateway = config.ipv4_gateway;
@@ -413,8 +425,9 @@ impl WireguardMonitor {
pub fn wait(mut self) -> Result<()> {
let wait_result = match self.close_msg_receiver.recv() {
Ok(CloseMsg::PingErr) => Err(Error::TimeoutError),
- Ok(CloseMsg::Stop) => Ok(()),
+ Ok(CloseMsg::Stop) | Ok(CloseMsg::ObfuscatorExpired) => Ok(()),
Ok(CloseMsg::SetupError(error)) => Err(error),
+ Ok(CloseMsg::ObfuscatorFailed(error)) => Err(error),
Err(_) => Ok(()),
};
@@ -570,6 +583,8 @@ enum CloseMsg {
Stop,
PingErr,
SetupError(Error),
+ ObfuscatorExpired,
+ ObfuscatorFailed(Error),
}
pub(crate) trait Tunnel: Send {
diff --git a/talpid-core/src/tunnel/wireguard/wireguard_nt.rs b/talpid-core/src/tunnel/wireguard/wireguard_nt.rs
index 705d09892d..21c9b705ce 100644
--- a/talpid-core/src/tunnel/wireguard/wireguard_nt.rs
+++ b/talpid-core/src/tunnel/wireguard/wireguard_nt.rs
@@ -982,7 +982,7 @@ impl Tunnel for WgNtTunnel {
mod tests {
use super::*;
use lazy_static::lazy_static;
- use talpid_types::net::{wireguard, TransportProtocol};
+ use talpid_types::net::wireguard;
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(C)]
@@ -1006,12 +1006,12 @@ mod tests {
public_key: WG_PUBLIC_KEY.clone(),
allowed_ips: vec!["1.3.3.0/24".parse().unwrap()],
endpoint: "1.2.3.4:1234".parse().unwrap(),
- protocol: TransportProtocol::Udp,
}],
ipv4_gateway: "0.0.0.0".parse().unwrap(),
ipv6_gateway: None,
mtu: 0,
use_wireguard_nt: true,
+ obfuscator_config: None,
}
};
static ref WG_STRUCT_CONFIG: Interface = Interface {
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index 950ee1a43f..88319a737e 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -479,7 +479,7 @@ fn should_retry(error: &tunnel::Error, retry_attempt: u32) -> bool {
use tunnel::wireguard::{Error, TunnelError};
match error {
- tunnel::Error::WireguardTunnelMonitoringError(Error::Udp2TcpError(_)) => true,
+ tunnel::Error::WireguardTunnelMonitoringError(Error::CreateObfuscatorError(_)) => true,
#[cfg(not(windows))]
tunnel::Error::WireguardTunnelMonitoringError(Error::TunnelError(
diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs
index e758d0e02f..fdfeeb8b33 100644
--- a/talpid-types/src/net/mod.rs
+++ b/talpid-types/src/net/mod.rs
@@ -1,5 +1,6 @@
#[cfg(target_os = "android")]
use jnix::IntoJava;
+use obfuscation::ObfuscatorConfig;
use serde::{Deserialize, Serialize};
#[cfg(windows)]
use std::path::PathBuf;
@@ -9,6 +10,7 @@ use std::{
str::FromStr,
};
+pub mod obfuscation;
pub mod openvpn;
pub mod proxy;
pub mod wireguard;
@@ -29,6 +31,7 @@ impl TunnelParameters {
tunnel_type: TunnelType::OpenVpn,
endpoint: params.config.endpoint,
proxy: params.proxy.as_ref().map(|proxy| proxy.get_endpoint()),
+ obfuscation: None,
entry_endpoint: None,
},
TunnelParameters::Wireguard(params) => TunnelEndpoint {
@@ -38,6 +41,10 @@ impl TunnelParameters {
.get_exit_endpoint()
.unwrap_or(params.connection.get_endpoint()),
proxy: None,
+ obfuscation: params
+ .obfuscation
+ .as_ref()
+ .map(|obfs| ObfuscationEndpoint::from(obfs)),
entry_endpoint: params
.connection
.get_exit_endpoint()
@@ -54,7 +61,20 @@ impl TunnelParameters {
.as_ref()
.map(|proxy| proxy.get_endpoint().endpoint)
.unwrap_or(params.config.endpoint),
- TunnelParameters::Wireguard(params) => params.connection.get_endpoint(),
+ TunnelParameters::Wireguard(params) => params
+ .obfuscation
+ .as_ref()
+ .map(|obfuscator| Self::get_obfuscator_endpoint(obfuscator))
+ .unwrap_or(params.connection.get_endpoint()),
+ }
+ }
+
+ fn get_obfuscator_endpoint(obfuscator: &ObfuscatorConfig) -> Endpoint {
+ match obfuscator {
+ ObfuscatorConfig::Udp2Tcp { endpoint } => Endpoint {
+ address: *endpoint,
+ protocol: TransportProtocol::Tcp,
+ },
}
}
@@ -119,6 +139,8 @@ pub struct TunnelEndpoint {
#[cfg_attr(target_os = "android", jnix(skip))]
pub proxy: Option<proxy::ProxyEndpoint>,
#[cfg_attr(target_os = "android", jnix(skip))]
+ pub obfuscation: Option<ObfuscationEndpoint>,
+ #[cfg_attr(target_os = "android", jnix(skip))]
pub entry_endpoint: Option<Endpoint>,
}
@@ -139,12 +161,64 @@ impl fmt::Display for TunnelEndpoint {
if let Some(ref entry_endpoint) = self.entry_endpoint {
write!(f, " via {}", entry_endpoint)?;
}
+ if let Some(ref obfuscation) = self.obfuscation {
+ write!(f, " via {}", obfuscation)?;
+ }
}
}
Ok(())
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[serde(rename = "obfuscation_type")]
+pub enum ObfuscationType {
+ #[serde(rename = "udp2tcp")]
+ Udp2Tcp,
+}
+
+impl fmt::Display for ObfuscationType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ let obfuscation = match self {
+ ObfuscationType::Udp2Tcp => "Udp2Tcp",
+ };
+ write!(f, "{}", obfuscation)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[serde(rename = "obfuscation_endpoint")]
+pub struct ObfuscationEndpoint {
+ pub endpoint: Endpoint,
+ pub obfuscation_type: ObfuscationType,
+}
+
+impl From<&ObfuscatorConfig> for ObfuscationEndpoint {
+ fn from(config: &ObfuscatorConfig) -> ObfuscationEndpoint {
+ let (endpoint, obfuscation_type) = match config {
+ ObfuscatorConfig::Udp2Tcp { endpoint } => (
+ Endpoint {
+ address: *endpoint,
+ protocol: TransportProtocol::Tcp,
+ },
+ ObfuscationType::Udp2Tcp,
+ ),
+ };
+
+ ObfuscationEndpoint {
+ endpoint,
+ obfuscation_type,
+ }
+ }
+}
+
+impl fmt::Display for ObfuscationEndpoint {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ write!(f, "{} {}", self.obfuscation_type, self.endpoint)?;
+ Ok(())
+ }
+}
+
/// Represents a network layer IP address together with the transport layer protocol and port.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(target_os = "android", derive(IntoJava))]
diff --git a/talpid-types/src/net/obfuscation.rs b/talpid-types/src/net/obfuscation.rs
new file mode 100644
index 0000000000..a39e9bf919
--- /dev/null
+++ b/talpid-types/src/net/obfuscation.rs
@@ -0,0 +1,7 @@
+use serde::{Deserialize, Serialize};
+use std::net::SocketAddr;
+
+#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)]
+pub enum ObfuscatorConfig {
+ Udp2Tcp { endpoint: SocketAddr },
+}
diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs
index 8219d772ec..2b3a9054b5 100644
--- a/talpid-types/src/net/wireguard.rs
+++ b/talpid-types/src/net/wireguard.rs
@@ -17,6 +17,7 @@ pub struct TunnelParameters {
pub connection: ConnectionConfig,
pub options: TunnelOptions,
pub generic_options: GenericTunnelOptions,
+ pub obfuscation: Option<super::obfuscation::ObfuscatorConfig>,
}
/// Connection-specific configuration in [`TunnelParameters`].
@@ -34,14 +35,14 @@ impl ConnectionConfig {
pub fn get_endpoint(&self) -> Endpoint {
Endpoint {
address: self.peer.endpoint,
- protocol: self.peer.protocol,
+ protocol: TransportProtocol::Udp,
}
}
pub fn get_exit_endpoint(&self) -> Option<Endpoint> {
self.exit_peer.as_ref().map(|peer| Endpoint {
address: peer.endpoint,
- protocol: peer.protocol,
+ protocol: TransportProtocol::Udp,
})
}
}
@@ -54,14 +55,6 @@ pub struct PeerConfig {
pub allowed_ips: Vec<IpNetwork>,
/// IP address of the WireGuard server.
pub endpoint: SocketAddr,
- /// Transport protocol. WireGuard only supports UDP directly.
- /// If this is set to TCP, then traffic is proxied using [udp_over_tcp](https://github.com/mullvad/udp-over-tcp).
- #[serde(default = "default_peer_transport")]
- pub protocol: TransportProtocol,
-}
-
-fn default_peer_transport() -> TransportProtocol {
- TransportProtocol::Udp
}
#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)]