diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index cb4dca2b2d..36d2f51bd6 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -451,6 +451,7 @@ impl Relay { exit_peer: None, ipv4_gateway, ipv6_gateway, + routes: None, // NOTE: Ignored in gRPC #[cfg(target_os = "linux")] fwmark: None, diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs index 0ca531189f..7a4938ea38 100644 --- a/mullvad-daemon/src/tunnel.rs +++ b/mullvad-daemon/src/tunnel.rs @@ -177,6 +177,7 @@ impl InnerParametersGenerator { exit_peer: endpoint.exit_peer, ipv4_gateway: endpoint.ipv4_gateway, ipv6_gateway: Some(endpoint.ipv6_gateway), + routes: None, #[cfg(target_os = "linux")] fwmark: Some(mullvad_types::TUNNEL_FWMARK), }, diff --git a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs index 5b4ce004ec..1c2be5cfa4 100644 --- a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs +++ b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs @@ -73,6 +73,8 @@ impl TryFrom for wireguard::ConnectionConfig { exit_peer: None, ipv4_gateway, ipv6_gateway, + // NOTE: Not visible to protobuf clients, and custom tunnels cannot set this. + routes: None, #[cfg(target_os = "linux")] fwmark: Some(mullvad_types::TUNNEL_FWMARK), }) diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index 5ef23b5100..53c593153d 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -72,6 +72,9 @@ pub struct ConnectionConfig { /// Gateway used by the tunnel (a private address). pub ipv4_gateway: Ipv4Addr, pub ipv6_gateway: Option, + /// Networks to route into the tunnel. If not set, this is the union of peers' allowed IPs. + #[serde(default)] + pub routes: Option>, #[cfg(target_os = "linux")] pub fwmark: Option, } diff --git a/talpid-wireguard/src/config.rs b/talpid-wireguard/src/config.rs index 30496c27ff..7a2f4cfaa0 100644 --- a/talpid-wireguard/src/config.rs +++ b/talpid-wireguard/src/config.rs @@ -1,3 +1,4 @@ +use ipnetwork::IpNetwork; use std::net::{Ipv4Addr, Ipv6Addr}; use talpid_types::net::{GenericTunnelOptions, obfuscation::Obfuscators, wireguard}; @@ -35,6 +36,9 @@ pub struct Config { pub quantum_resistant: bool, /// Enable DAITA pub daita: bool, + /// Networks routed into the tunnel device. Typically matches the union of all peers' allowed + /// IPs. + pub routes: Vec, } /// Configuration errors @@ -105,6 +109,7 @@ impl Config { obfuscation_mtu, quantum_resistant: wg_options.quantum_resistant, daita: wg_options.daita, + routes: Vec::new(), }; for peer in config.peers_mut() { @@ -115,6 +120,20 @@ impl Config { } } + let routes: Vec = match &connection.routes { + Some(routes) => routes + .iter() + .copied() + .filter(|route| route.is_ipv4() || generic_options.enable_ipv6) + .collect(), + None => config + .peers() + .flat_map(|peer| peer.allowed_ips.iter()) + .copied() + .collect(), + }; + config.routes = routes; + Ok(config) } @@ -164,11 +183,9 @@ impl Config { .chain(std::iter::once(&mut self.entry_peer)) } - /// Return routes for all allowed IPs. - pub fn get_tunnel_destinations(&self) -> impl Iterator + '_ { - self.peers() - .flat_map(|peer| peer.allowed_ips.iter()) - .cloned() + /// Return the networks routed into the tunnel device. + pub fn get_tunnel_destinations(&self) -> impl Iterator + '_ { + self.routes.iter().copied() } } @@ -176,7 +193,6 @@ impl Config { /// Used to block traffic to other destinations while connecting on Android. #[cfg(target_os = "android")] pub(crate) fn patch_allowed_ips(mut config: Config) -> Config { - use ipnetwork::IpNetwork; use std::net::IpAddr; let gateway_net_v4 = IpNetwork::from(IpAddr::from(config.ipv4_gateway)); diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs index ed23478bdd..f20e0013b9 100644 --- a/talpid-wireguard/src/wireguard_nt/mod.rs +++ b/talpid-wireguard/src/wireguard_nt/mod.rs @@ -1236,6 +1236,7 @@ mod tests { obfuscation_mtu: 0, quantum_resistant: false, daita: false, + routes: vec!["1.3.3.0/24".parse().unwrap()], }); static WG_STRUCT_CONFIG: LazyLock = LazyLock::new(|| Interface { diff --git a/test/test-manager/src/tests/dns.rs b/test/test-manager/src/tests/dns.rs index 2762aa543c..2bbb93293b 100644 --- a/test/test-manager/src/tests/dns.rs +++ b/test/test-manager/src/tests/dns.rs @@ -665,6 +665,7 @@ async fn connect_local_wg_relay(mullvad_client: &mut MullvadProxyClient) -> Resu }, ipv4_gateway: CUSTOM_TUN_GATEWAY, exit_peer: None, + routes: None, #[cfg(target_os = "linux")] fwmark: None, ipv6_gateway: None, diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index 29255c8148..1261019db2 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -662,6 +662,7 @@ pub fn unreachable_wireguard_tunnel() -> talpid_types::net::wireguard::Connectio exit_peer: None, ipv4_gateway: Ipv4Addr::new(10, 64, 10, 1), ipv6_gateway: None, + routes: None, #[cfg(target_os = "linux")] fwmark: None, }