Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WireGuard over Shadowsocks #6326

Merged
merged 11 commits into from
Aug 16, 2024
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,13 @@ internal fun ManagementInterface.ObfuscationEndpoint.toDomain(): ObfuscationEndp
obfuscationType = obfuscationType.toDomain()
)

internal fun ManagementInterface.ObfuscationType.toDomain(): ObfuscationType =
internal fun ManagementInterface.ObfuscationEndpoint.ObfuscationType.toDomain(): ObfuscationType =
when (this) {
ManagementInterface.ObfuscationType.UDP2TCP -> ObfuscationType.Udp2Tcp
ManagementInterface.ObfuscationType.UNRECOGNIZED ->
ManagementInterface.ObfuscationEndpoint.ObfuscationType.UDP2TCP -> ObfuscationType.Udp2Tcp
ManagementInterface.ObfuscationEndpoint.ObfuscationType.UNRECOGNIZED ->
throw IllegalArgumentException("Unrecognized obfuscation type")
ManagementInterface.ObfuscationEndpoint.ObfuscationType.SHADOWSOCKS ->
throw IllegalArgumentException("Shadowsocks is unsupported")
}

internal fun ManagementInterface.TransportProtocol.toDomain(): TransportProtocol =
Expand Down Expand Up @@ -340,6 +342,8 @@ internal fun ManagementInterface.ObfuscationSettings.SelectedObfuscation.toDomai
ManagementInterface.ObfuscationSettings.SelectedObfuscation.OFF -> SelectedObfuscation.Off
ManagementInterface.ObfuscationSettings.SelectedObfuscation.UDP2TCP ->
SelectedObfuscation.Udp2Tcp
ManagementInterface.ObfuscationSettings.SelectedObfuscation.SHADOWSOCKS ->
throw IllegalArgumentException("Shadowsocks is unsupported")
ManagementInterface.ObfuscationSettings.SelectedObfuscation.UNRECOGNIZED ->
throw IllegalArgumentException("Unrecognized selected obfuscation")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.lib.model

enum class ObfuscationType {
Udp2Tcp
Udp2Tcp,
Shadowsocks
}
20 changes: 12 additions & 8 deletions gui/src/main/daemon-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
DeviceEvent,
DeviceState,
DirectMethod,
EndpointObfuscationType,
ErrorState,
ErrorStateCause,
FirewallPolicyError,
Expand Down Expand Up @@ -403,6 +402,8 @@ export class DaemonRpc {
grpcObfuscationSettings.setUdp2tcp(grpcUdp2tcpSettings);
}

grpcObfuscationSettings.setShadowsocks(new grpcTypes.ShadowsocksSettings());

await this.call<grpcTypes.ObfuscationSettings, Empty>(
this.client.setObfuscationSettings,
grpcObfuscationSettings,
Expand Down Expand Up @@ -1136,9 +1137,9 @@ function convertFromTunnelType(tunnelType: grpcTypes.TunnelType): TunnelType {
}

function convertFromProxyEndpoint(proxyEndpoint: grpcTypes.ProxyEndpoint.AsObject): IProxyEndpoint {
const proxyTypeMap: Record<grpcTypes.ProxyType, ProxyType> = {
[grpcTypes.ProxyType.CUSTOM]: 'custom',
[grpcTypes.ProxyType.SHADOWSOCKS]: 'shadowsocks',
const proxyTypeMap: Record<grpcTypes.ProxyEndpoint.ProxyType, ProxyType> = {
[grpcTypes.ProxyEndpoint.ProxyType.CUSTOM]: 'custom',
[grpcTypes.ProxyEndpoint.ProxyType.SHADOWSOCKS]: 'shadowsocks',
};

return {
Expand All @@ -1151,14 +1152,17 @@ function convertFromProxyEndpoint(proxyEndpoint: grpcTypes.ProxyEndpoint.AsObjec
function convertFromObfuscationEndpoint(
obfuscationEndpoint: grpcTypes.ObfuscationEndpoint.AsObject,
): IObfuscationEndpoint {
const obfuscationTypes: Record<grpcTypes.ObfuscationType, EndpointObfuscationType> = {
[grpcTypes.ObfuscationType.UDP2TCP]: 'udp2tcp',
};
// TODO: Handle Shadowsocks (and other implemented protocols)
if (
dlon marked this conversation as resolved.
Show resolved Hide resolved
obfuscationEndpoint.obfuscationType !== grpcTypes.ObfuscationEndpoint.ObfuscationType.UDP2TCP
) {
throw new Error('unsupported obfuscation protocol');
}

return {
...obfuscationEndpoint,
protocol: convertFromTransportProtocol(obfuscationEndpoint.protocol),
obfuscationType: obfuscationTypes[obfuscationEndpoint.obfuscationType],
obfuscationType: 'udp2tcp',
};
}

Expand Down
25 changes: 23 additions & 2 deletions mullvad-api/src/relay_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use talpid_types::net::wireguard;
use std::{
collections::BTreeMap,
future::Future,
net::{Ipv4Addr, Ipv6Addr},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
ops::RangeInclusive,
time::Duration,
};

Expand Down Expand Up @@ -244,20 +245,37 @@ struct Wireguard {
port_ranges: Vec<(u16, u16)>,
ipv4_gateway: Ipv4Addr,
ipv6_gateway: Ipv6Addr,
/// Shadowsocks port ranges available on all WireGuard relays
#[serde(default)]
shadowsocks_port_ranges: Vec<(u16, u16)>,
relays: Vec<WireGuardRelay>,
}

impl From<&Wireguard> for relay_list::WireguardEndpointData {
fn from(wg: &Wireguard) -> Self {
Self {
port_ranges: wg.port_ranges.clone(),
port_ranges: inclusive_range_from_pair_set(wg.port_ranges.clone()).collect(),
ipv4_gateway: wg.ipv4_gateway,
ipv6_gateway: wg.ipv6_gateway,
shadowsocks_port_ranges: inclusive_range_from_pair_set(
wg.shadowsocks_port_ranges.clone(),
)
.collect(),
udp2tcp_ports: vec![],
}
}
}

fn inclusive_range_from_pair_set<T>(
set: impl IntoIterator<Item = (T, T)>,
) -> impl Iterator<Item = RangeInclusive<T>> {
set.into_iter().map(inclusive_range_from_pair)
}

fn inclusive_range_from_pair<T>(pair: (T, T)) -> RangeInclusive<T> {
RangeInclusive::new(pair.0, pair.1)
}

impl Wireguard {
/// Consumes `self` and appends all its relays to `countries`.
fn extract_relays(
Expand Down Expand Up @@ -305,6 +323,8 @@ struct WireGuardRelay {
public_key: wireguard::PublicKey,
#[serde(default)]
daita: bool,
#[serde(default)]
shadowsocks_extra_addr_in: Vec<IpAddr>,
}

impl WireGuardRelay {
Expand All @@ -315,6 +335,7 @@ impl WireGuardRelay {
relay_list::RelayEndpointData::Wireguard(relay_list::WireguardRelayEndpointData {
public_key: self.public_key,
daita: self.daita,
shadowsocks_extra_addr_in: self.shadowsocks_extra_addr_in,
}),
)
}
Expand Down
2 changes: 1 addition & 1 deletion mullvad-cli/src/cmds/relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ impl Relay {
let is_valid_port = wireguard
.port_ranges
.into_iter()
.any(|(first, last)| first <= specific_port && specific_port <= last);
.any(|range| range.contains(&specific_port));
if !is_valid_port {
return Err(anyhow!("The specified port is invalid"));
}
Expand Down
8 changes: 8 additions & 0 deletions mullvad-daemon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2931,6 +2931,13 @@ impl Daemon {
.as_ref()
.filter(|obfuscation| obfuscation.obfuscation_type == ObfuscationType::Udp2Tcp)
.is_some();
let shadowsocks = endpoint
.obfuscation
.as_ref()
.filter(|obfuscation| {
obfuscation.obfuscation_type == ObfuscationType::Shadowsocks
})
.is_some();

let mtu = settings.tunnel_options.wireguard.mtu.is_some();

Expand All @@ -2941,6 +2948,7 @@ impl Daemon {
(quantum_resistant, FeatureIndicator::QuantumResistance),
(multihop, FeatureIndicator::Multihop),
(udp_tcp, FeatureIndicator::Udp2Tcp),
(shadowsocks, FeatureIndicator::Shadowsocks),
(mtu, FeatureIndicator::CustomMtu),
#[cfg(daita)]
(daita, FeatureIndicator::Daita),
Expand Down
1 change: 1 addition & 0 deletions mullvad-daemon/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub const SILENCED_CRATES: &[&str] = &[
"hickory_proto",
"hickory_server",
"hickory_resolver",
"shadowsocks::relay::udprelay",
];
const SLIGHTLY_SILENCED_CRATES: &[&str] = &["mnl", "nftnl", "udp_over_tcp"];

Expand Down
42 changes: 25 additions & 17 deletions mullvad-management-interface/proto/management_interface.proto
Original file line number Diff line number Diff line change
Expand Up @@ -254,37 +254,39 @@ enum FeatureIndicator {
SPLIT_TUNNELING = 3;
LOCKDOWN_MODE = 4;
UDP_2_TCP = 5;
LAN_SHARING = 6;
DNS_CONTENT_BLOCKERS = 7;
CUSTOM_DNS = 8;
SERVER_IP_OVERRIDE = 9;
CUSTOM_MTU = 10;
CUSTOM_MSS_FIX = 11;
DAITA = 12;
}

enum ObfuscationType {
UDP2TCP = 0;
SHADOWSOCKS = 6;
LAN_SHARING = 7;
DNS_CONTENT_BLOCKERS = 8;
CUSTOM_DNS = 9;
SERVER_IP_OVERRIDE = 10;
CUSTOM_MTU = 11;
CUSTOM_MSS_FIX = 12;
DAITA = 13;
}

message ObfuscationEndpoint {
enum ObfuscationType {
UDP2TCP = 0;
SHADOWSOCKS = 1;
}

string address = 1;
uint32 port = 2;
TransportProtocol protocol = 3;
ObfuscationType obfuscation_type = 4;
}

enum ProxyType {
SHADOWSOCKS = 0;
CUSTOM = 1;
}

message Endpoint {
string address = 1;
TransportProtocol protocol = 2;
}

message ProxyEndpoint {
enum ProxyType {
SHADOWSOCKS = 0;
CUSTOM = 1;
}

string address = 1;
TransportProtocol protocol = 2;
ProxyType proxy_type = 3;
Expand Down Expand Up @@ -352,14 +354,18 @@ message BridgeState {

message Udp2TcpObfuscationSettings { optional uint32 port = 1; }

message ShadowsocksSettings { optional uint32 port = 1; }

message ObfuscationSettings {
enum SelectedObfuscation {
AUTO = 0;
OFF = 1;
UDP2TCP = 2;
SHADOWSOCKS = 3;
}
SelectedObfuscation selected_obfuscation = 1;
Udp2TcpObfuscationSettings udp2tcp = 2;
ShadowsocksSettings shadowsocks = 3;
}

message CustomList {
Expand Down Expand Up @@ -631,6 +637,7 @@ message Relay {
message WireguardRelayEndpointData {
bytes public_key = 1;
bool daita = 2;
repeated string shadowsocks_extra_addr_in = 3;
}

message Location {
Expand Down Expand Up @@ -686,7 +693,8 @@ message WireguardEndpointData {
repeated PortRange port_ranges = 1;
string ipv4_gateway = 2;
string ipv6_gateway = 3;
repeated uint32 udp2tcp_ports = 4;
repeated PortRange shadowsocks_port_ranges = 4;
repeated uint32 udp2tcp_ports = 5;
}

message PortRange {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ impl From<mullvad_types::features::FeatureIndicator> for proto::FeatureIndicator
mullvad_types::features::FeatureIndicator::SplitTunneling => SplitTunneling,
mullvad_types::features::FeatureIndicator::LockdownMode => LockdownMode,
mullvad_types::features::FeatureIndicator::Udp2Tcp => Udp2Tcp,
mullvad_types::features::FeatureIndicator::Shadowsocks => Shadowsocks,
mullvad_types::features::FeatureIndicator::LanSharing => LanSharing,
mullvad_types::features::FeatureIndicator::DnsContentBlockers => DnsContentBlockers,
mullvad_types::features::FeatureIndicator::CustomDns => CustomDns,
Expand All @@ -30,6 +31,7 @@ impl From<proto::FeatureIndicator> for mullvad_types::features::FeatureIndicator
proto::FeatureIndicator::SplitTunneling => Self::SplitTunneling,
proto::FeatureIndicator::LockdownMode => Self::LockdownMode,
proto::FeatureIndicator::Udp2Tcp => Self::Udp2Tcp,
proto::FeatureIndicator::Shadowsocks => Self::Shadowsocks,
proto::FeatureIndicator::LanSharing => Self::LanSharing,
proto::FeatureIndicator::DnsContentBlockers => Self::DnsContentBlockers,
proto::FeatureIndicator::CustomDns => Self::CustomDns,
Expand Down
53 changes: 35 additions & 18 deletions mullvad-management-interface/src/types/conversions/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ impl From<talpid_types::net::TunnelEndpoint> for proto::TunnelEndpoint {
address: proxy_ep.endpoint.address.to_string(),
protocol: i32::from(proto::TransportProtocol::from(proxy_ep.endpoint.protocol)),
proxy_type: match proxy_ep.proxy_type {
net::proxy::ProxyType::Shadowsocks => i32::from(proto::ProxyType::Shadowsocks),
net::proxy::ProxyType::Custom => i32::from(proto::ProxyType::Custom),
net::proxy::ProxyType::Shadowsocks => {
i32::from(proto::proxy_endpoint::ProxyType::Shadowsocks)
}
net::proxy::ProxyType::Custom => {
i32::from(proto::proxy_endpoint::ProxyType::Custom)
}
},
}),
obfuscation: endpoint.obfuscation.map(|obfuscation_endpoint| {
Expand All @@ -29,7 +33,12 @@ impl From<talpid_types::net::TunnelEndpoint> for proto::TunnelEndpoint {
obfuscation_endpoint.endpoint.protocol,
)),
obfuscation_type: match obfuscation_endpoint.obfuscation_type {
net::ObfuscationType::Udp2Tcp => i32::from(proto::ObfuscationType::Udp2tcp),
net::ObfuscationType::Udp2Tcp => {
i32::from(proto::obfuscation_endpoint::ObfuscationType::Udp2tcp)
}
net::ObfuscationType::Shadowsocks => {
i32::from(proto::obfuscation_endpoint::ObfuscationType::Shadowsocks)
}
},
}
}),
Expand Down Expand Up @@ -72,11 +81,15 @@ impl TryFrom<proto::TunnelEndpoint> for talpid_types::net::TunnelEndpoint {
)?,
protocol: try_transport_protocol_from_i32(proxy_ep.protocol)?,
},
proxy_type: match proto::ProxyType::try_from(proxy_ep.proxy_type) {
Ok(proto::ProxyType::Shadowsocks) => {
proxy_type: match proto::proxy_endpoint::ProxyType::try_from(
proxy_ep.proxy_type,
) {
Ok(proto::proxy_endpoint::ProxyType::Shadowsocks) => {
talpid_net::proxy::ProxyType::Shadowsocks
}
Ok(proto::ProxyType::Custom) => talpid_net::proxy::ProxyType::Custom,
Ok(proto::proxy_endpoint::ProxyType::Custom) => {
talpid_net::proxy::ProxyType::Custom
}
Err(_) => {
return Err(FromProtobufTypeError::InvalidArgument(
"unknown proxy type",
Expand All @@ -100,18 +113,22 @@ impl TryFrom<proto::TunnelEndpoint> for talpid_types::net::TunnelEndpoint {
),
protocol: try_transport_protocol_from_i32(obfs_ep.protocol)?,
},
obfuscation_type: match proto::ObfuscationType::try_from(
obfs_ep.obfuscation_type,
) {
Ok(proto::ObfuscationType::Udp2tcp) => {
talpid_net::ObfuscationType::Udp2Tcp
}
Err(_) => {
return Err(FromProtobufTypeError::InvalidArgument(
"unknown obfuscation type",
))
}
},
obfuscation_type:
match proto::obfuscation_endpoint::ObfuscationType::try_from(
obfs_ep.obfuscation_type,
) {
Ok(proto::obfuscation_endpoint::ObfuscationType::Udp2tcp) => {
talpid_net::ObfuscationType::Udp2Tcp
}
Ok(proto::obfuscation_endpoint::ObfuscationType::Shadowsocks) => {
talpid_net::ObfuscationType::Shadowsocks
}
Err(_) => {
return Err(FromProtobufTypeError::InvalidArgument(
"unknown obfuscation type",
))
}
},
})
})
.transpose()?,
Expand Down
Loading
Loading