diff options
| author | David Lönnhager <david.l@mullvad.net> | 2021-03-01 13:50:10 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2021-03-01 13:50:10 +0100 |
| commit | b37f17ee40f928c02ace811b3f3f3f10f7bb60b3 (patch) | |
| tree | df07af6eb14bc1745be256928561e64a2d488086 | |
| parent | 10878d01b27aed6e1ba976e6a59302edd84ab888 (diff) | |
| parent | 50d057ae92f59f2fadca4fd96568f4401dbffa41 (diff) | |
| download | mullvadvpn-b37f17ee40f928c02ace811b3f3f3f10f7bb60b3.tar.xz mullvadvpn-b37f17ee40f928c02ace811b3f3f3f10f7bb60b3.zip | |
Merge branch 'wireguard-over-ipv6'
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | Cargo.lock | 14 | ||||
| -rw-r--r-- | mullvad-cli/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/relay.rs | 67 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 28 | ||||
| -rw-r--r-- | mullvad-daemon/src/relays.rs | 75 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 11 | ||||
| -rw-r--r-- | mullvad-types/src/relay_constraints.rs | 13 | ||||
| -rw-r--r-- | talpid-types/src/net/mod.rs | 23 |
9 files changed, 183 insertions, 50 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d9eed6e3..469bbe90a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Line wrap the file at 100 chars. Th - Enable isolation of the Electron renderer process to protect against potentially malicious third party dependencies. - Add 51820 to list of WireGuard ports in app settings. +- Add option to connect to WireGuard relays over IPv6. #### Android - Allow reaching the API server when connecting, disconnecting or in a blocked state. diff --git a/Cargo.lock b/Cargo.lock index 9601349809..17328f4e9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -977,6 +977,15 @@ dependencies = [ ] [[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + +[[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1231,6 +1240,7 @@ dependencies = [ "env_logger 0.8.2", "err-derive 0.3.0", "futures", + "itertools 0.10.0", "mullvad-management-interface", "mullvad-paths", "mullvad-types", @@ -1952,7 +1962,7 @@ checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" dependencies = [ "bytes 0.5.6", "heck", - "itertools", + "itertools 0.8.2", "log 0.4.14", "multimap", "petgraph", @@ -1969,7 +1979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" dependencies = [ "anyhow", - "itertools", + "itertools 0.8.2", "proc-macro2", "quote", "syn", diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml index 2090fd5796..603197fa32 100644 --- a/mullvad-cli/Cargo.toml +++ b/mullvad-cli/Cargo.toml @@ -20,6 +20,7 @@ env_logger = "0.8.2" futures = "0.3" natord = "1.0.9" serde = "1.0" +itertools = "0.10" mullvad-types = { path = "../mullvad-types" } mullvad-paths = { path = "../mullvad-paths" } diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 4ad950fe7a..6343b9e5d5 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -1,5 +1,6 @@ use crate::{location, new_rpc_client, Command, Error, Result}; use clap::{value_t, values_t}; +use itertools::Itertools; use std::{ io::{self, BufRead}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, @@ -8,10 +9,11 @@ use std::{ use mullvad_management_interface::types::{ connection_config::{self, OpenvpnConfig, WireguardConfig}, - relay_settings, relay_settings_update, ConnectionConfig, CustomRelaySettings, - NormalRelaySettingsUpdate, OpenvpnConstraints, ProviderUpdate, RelayListCountry, RelayLocation, - RelaySettingsUpdate, TransportProtocol, TransportProtocolConstraint, TunnelType, - TunnelTypeConstraint, TunnelTypeUpdate, WireguardConstraints, + relay_settings, relay_settings_update, ConnectionConfig, CustomRelaySettings, IpVersion, + IpVersionConstraint, NormalRelaySettingsUpdate, OpenvpnConstraints, ProviderUpdate, + RelayListCountry, RelayLocation, RelaySettingsUpdate, TransportProtocol, + TransportProtocolConstraint, TunnelType, TunnelTypeConstraint, TunnelTypeUpdate, + WireguardConstraints, }; use mullvad_types::relay_constraints::Constraint; use talpid_types::net::all_of_the_internet; @@ -153,6 +155,13 @@ impl Command for Relay { .required(false) .default_value("any") .possible_values(&["any", "udp", "tcp"]), + ) + .arg( + clap::Arg::with_name("ip version") + .long("ipv") + .required(false) + .default_value("any") + .possible_values(&["any", "4", "6"]), ), ) @@ -466,6 +475,7 @@ impl Relay { let vpn_protocol = matches.value_of("vpn protocol").unwrap(); let port = parse_port_constraint(matches.value_of("port").unwrap())?; let protocol = parse_protocol_constraint(matches.value_of("transport protocol").unwrap()); + let ip_version = parse_ip_version_constraint(matches.value_of("ip version").unwrap()); match vpn_protocol { "wireguard" => { @@ -477,6 +487,11 @@ impl Relay { NormalRelaySettingsUpdate { wireguard_constraints: Some(WireguardConstraints { port: port.unwrap_or(0) as u32, + ip_version: ip_version.option().map(|protocol| { + IpVersionConstraint { + protocol: protocol as i32, + } + }), }), ..Default::default() }, @@ -485,6 +500,11 @@ impl Relay { .await } "openvpn" => { + if let Constraint::Only(_) = ip_version { + return Err(Error::InvalidCommand( + "OpenVPN does not support the IP version constraint", + )); + } self.update_constraints(RelaySettingsUpdate { r#type: Some(relay_settings_update::Type::Normal( NormalRelaySettingsUpdate { @@ -624,9 +644,16 @@ impl Relay { (false, true) => "WireGuard", _ => unreachable!("Bug in relay filtering earlier on"), }; + let mut addresses = vec![&relay.ipv4_addr_in]; + if !relay.ipv6_addr_in.is_empty() { + addresses.push(&relay.ipv6_addr_in); + } println!( "\t\t{} ({}) - {}, hosted by {}", - relay.hostname, relay.ipv4_addr_in, support_msg, relay.provider + relay.hostname, + addresses.iter().join(", "), + support_msg, + relay.provider ); } } @@ -641,6 +668,14 @@ impl Relay { Ok(()) } + fn format_ip_version(protocol: Option<IpVersion>) -> &'static str { + match protocol { + None => "IPv4 or IPv6", + Some(IpVersion::V4) => "IPv4", + Some(IpVersion::V6) => "IPv6", + } + } + fn format_transport_protocol(protocol: Option<TransportProtocol>) -> &'static str { match protocol { None => "any transport protocol", @@ -676,9 +711,18 @@ impl Relay { fn format_wireguard_constraints(constraints: Option<&WireguardConstraints>) -> String { if let Some(constraints) = constraints { - Self::format_port(constraints.port) + format!( + "{} over {}", + Self::format_port(constraints.port), + Self::format_ip_version( + constraints + .ip_version + .clone() + .map(|protocol| IpVersion::from_i32(protocol.protocol).unwrap()) + ) + ) } else { - "any port".to_string() + "any port over IPv4 or IPv6".to_string() } } @@ -739,3 +783,12 @@ fn parse_protocol_constraint(raw_protocol: &str) -> Constraint<TransportProtocol _ => unreachable!(), } } + +fn parse_ip_version_constraint(raw_protocol: &str) -> Constraint<IpVersion> { + match raw_protocol { + "any" => Constraint::Any, + "4" => Constraint::Only(IpVersion::V4), + "6" => Constraint::Only(IpVersion::V6), + _ => unreachable!(), + } +} diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index c87b846387..732e648199 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -27,7 +27,7 @@ use std::{ sync::{mpsc, Arc}, }; use talpid_types::{ - net::{TransportProtocol, TunnelType}, + net::{IpVersion, TransportProtocol, TunnelType}, ErrorExt, }; @@ -949,6 +949,20 @@ fn convert_relay_settings_update( } else { None }; + let ip_version = if let Some(ref constraints) = settings.wireguard_constraints { + match &constraints.ip_version { + Some(constraint) => match types::IpVersion::from_i32(constraint.protocol) { + Some(types::IpVersion::V4) => Some(IpVersion::V4), + Some(types::IpVersion::V6) => Some(IpVersion::V6), + None => { + return Err(Status::invalid_argument("unknown ip protocol version")) + } + }, + None => None, + } + } else { + None + }; Ok(RelaySettingsUpdate::Normal(RelayConstraintsUpdate { location, @@ -961,6 +975,7 @@ fn convert_relay_settings_update( } else { Constraint::Any }, + ip_version: Constraint::from(ip_version), } }), openvpn_constraints: settings.openvpn_constraints.map(|constraints| { @@ -1003,6 +1018,17 @@ fn convert_relay_settings(settings: &RelaySettings) -> types::RelaySettings { wireguard_constraints: Some(types::WireguardConstraints { port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)), + ip_version: constraints + .wireguard_constraints + .ip_version + .option() + .map(|version| match version { + IpVersion::V4 => types::IpVersion::V4, + IpVersion::V6 => types::IpVersion::V6, + }) + .map(|version| types::IpVersionConstraint { + protocol: i32::from(version), + }), }), openvpn_constraints: Some(types::OpenvpnConstraints { diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index 26b855b3b4..2e2a009c4d 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -30,7 +30,10 @@ use std::{ }; use talpid_core::future_retry::{retry_future_with_backoff, ExponentialBackoff, Jittered}; use talpid_types::{ - net::{all_of_the_internet, openvpn::ProxySettings, wireguard, TransportProtocol, TunnelType}, + net::{ + all_of_the_internet, openvpn::ProxySettings, wireguard, IpVersion, TransportProtocol, + TunnelType, + }, ErrorExt, }; use tokio::fs::File; @@ -464,12 +467,13 @@ impl RelaySelector { self.pick_random_relay(&matching_relays) .and_then(|selected_relay| { - info!( - "Selected relay {} at {}", - selected_relay.hostname, selected_relay.ipv4_addr_in - ); - self.get_random_tunnel(&selected_relay, &constraints) - .map(|endpoint| (selected_relay.clone(), endpoint)) + let endpoint = self.get_random_tunnel(&selected_relay, &constraints); + let addr_in = endpoint + .as_ref() + .map(|endpoint| endpoint.to_endpoint().address.ip()) + .unwrap_or(IpAddr::from(selected_relay.ipv4_addr_in)); + info!("Selected relay {} at {}", selected_relay.hostname, addr_in); + endpoint.map(|endpoint| (selected_relay.clone(), endpoint)) }) } @@ -638,52 +642,38 @@ impl RelaySelector { relay: &Relay, constraints: &RelayConstraints, ) -> Option<MullvadEndpoint> { - match constraints.tunnel_protocol { - // TODO: Handle Constraint::Any case by selecting from both openvpn and wireguard - // tunnels once wireguard is mature enough - #[cfg(not(target_os = "android"))] - Constraint::Only(TunnelType::OpenVpn) | Constraint::Any => relay - .tunnels - .openvpn - .choose(&mut self.rng) - .cloned() - .map(|endpoint| endpoint.into_mullvad_endpoint(relay.ipv4_addr_in.into())), - Constraint::Only(TunnelType::Wireguard) => relay + let mut new_wg_endpoint = || { + relay .tunnels .wireguard .choose(&mut self.rng) .cloned() .and_then(|wg_tunnel| { - self.wg_data_to_endpoint( - relay.ipv4_addr_in.into(), - wg_tunnel, - constraints.wireguard_constraints, - ) - }), - #[cfg(target_os = "android")] - Constraint::Any => relay + self.wg_data_to_endpoint(relay, wg_tunnel, constraints.wireguard_constraints) + }) + }; + + #[cfg(not(target_os = "android"))] + match constraints.tunnel_protocol { + Constraint::Only(TunnelType::OpenVpn) | Constraint::Any => relay .tunnels - .wireguard + .openvpn .choose(&mut self.rng) .cloned() - .and_then(|wg_tunnel| { - self.wg_data_to_endpoint( - relay.ipv4_addr_in.into(), - wg_tunnel, - WireguardConstraints::default(), - ) - }), - #[cfg(target_os = "android")] - Constraint::Only(TunnelType::OpenVpn) => None, + .map(|endpoint| endpoint.into_mullvad_endpoint(relay.ipv4_addr_in.into())), + Constraint::Only(TunnelType::Wireguard) => new_wg_endpoint(), } + #[cfg(target_os = "android")] + new_wg_endpoint() } fn wg_data_to_endpoint( &mut self, - host: IpAddr, + relay: &Relay, data: WireguardEndpointData, constraints: WireguardConstraints, ) -> Option<MullvadEndpoint> { + let host = self.get_address_for_wireguard_relay(relay, constraints)?; let port = self.get_port_for_wireguard_relay(&data, constraints)?; let peer_config = wireguard::PeerConfig { public_key: data.public_key, @@ -697,6 +687,17 @@ impl RelaySelector { }) } + fn get_address_for_wireguard_relay( + &mut self, + relay: &Relay, + constraints: WireguardConstraints, + ) -> Option<IpAddr> { + match constraints.ip_version { + Constraint::Any | Constraint::Only(IpVersion::V4) => Some(relay.ipv4_addr_in.into()), + Constraint::Only(IpVersion::V6) => relay.ipv6_addr_in.map(|addr| addr.into()), + } + } + fn get_port_for_wireguard_relay( &mut self, data: &WireguardEndpointData, diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 08b8a708d9..83ea64a2ac 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -302,9 +302,20 @@ message OpenvpnConstraints { uint32 port = 1; TransportProtocolConstraint protocol = 2; } + +enum IpVersion { + V4 = 0; + V6 = 1; +} + +message IpVersionConstraint { + IpVersion protocol = 1; +} + message WireguardConstraints { // NOTE: optional uint32 port = 1; + IpVersionConstraint ip_version = 2; } message CustomRelaySettings { diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs index c9042c8f49..d1104f4733 100644 --- a/mullvad-types/src/relay_constraints.rs +++ b/mullvad-types/src/relay_constraints.rs @@ -10,7 +10,7 @@ use crate::{ use jnix::{FromJava, IntoJava}; use serde::{Deserialize, Serialize}; use std::{collections::HashSet, fmt}; -use talpid_types::net::{openvpn::ProxySettings, TransportProtocol, TunnelType}; +use talpid_types::net::{openvpn::ProxySettings, IpVersion, TransportProtocol, TunnelType}; pub trait Match<T> { @@ -434,15 +434,22 @@ impl Match<OpenVpnEndpointData> for OpenVpnConstraints { /// [`Constraint`]s applicable to WireGuard relay servers. #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(default)] pub struct WireguardConstraints { pub port: Constraint<u16>, + pub ip_version: Constraint<IpVersion>, } 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) => write!(f, "port {}", port), + Constraint::Any => write!(f, "any port")?, + Constraint::Only(port) => write!(f, "port {}", port)?, + } + write!(f, " over ")?; + match self.ip_version { + Constraint::Any => write!(f, "IPv4 or IPv6"), + Constraint::Only(protocol) => write!(f, "{}", protocol), } } } diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index 29a871ee07..adea4fd512 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -156,6 +156,29 @@ impl fmt::Display for Endpoint { } } +/// IP protocol version. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum IpVersion { + V4, + V6, +} + +impl Default for IpVersion { + fn default() -> IpVersion { + IpVersion::V4 + } +} + +impl fmt::Display for IpVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match *self { + IpVersion::V4 => "IPv4".fmt(f), + IpVersion::V6 => "IPv6".fmt(f), + } + } +} + /// Representation of a transport protocol, either UDP or TCP. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] |
