Add a routes field to the WireGuard connection config

This allows the config to decouple allowed IPs from routing.
This commit is contained in:
David Lönnhager committed 2026-09-21 10:04:54 +02:00
1 parent 326bb16e3c
commit 44bf15459e
8 files changed
+32 -6

No files matched your search

+1
View File
@@ -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,
+1
View File
@@ -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),
},
@@ -73,6 +73,8 @@ impl TryFrom<proto::WireguardConfig> 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),
})
+3
View File
@@ -72,6 +72,9 @@ pub struct ConnectionConfig {
/// Gateway used by the tunnel (a private address).
pub ipv4_gateway: Ipv4Addr,
pub ipv6_gateway: Option<Ipv6Addr>,
/// Networks to route into the tunnel. If not set, this is the union of peers' allowed IPs.
#[serde(default)]
pub routes: Option<Vec<IpNetwork>>,
#[cfg(target_os = "linux")]
pub fwmark: Option<u32>,
}
+22 -6
View File
@@ -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<IpNetwork>,
}
/// 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<IpNetwork> = 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<Item = ipnetwork::IpNetwork> + '_ {
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<Item = IpNetwork> + '_ {
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));
+1
View File
@@ -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<Interface> = LazyLock::new(|| Interface {
+1
View File
@@ -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,
+1
View File
@@ -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,
}