Skip to content

Commit 5e22b9c

Browse files
committed
stuff: expose route manager to daemon
1 parent ba5a991 commit 5e22b9c

File tree

6 files changed

+85
-43
lines changed

6 files changed

+85
-43
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mullvad-daemon/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ talpid-future = { path = "../talpid-future" }
4242
talpid-platform-metadata = { path = "../talpid-platform-metadata" }
4343
talpid-time = { path = "../talpid-time" }
4444
talpid-types = { path = "../talpid-types" }
45+
talpid-routing = { path = "../talpid-routing" }
4546

4647
leak-checker = { path = "../leak-checker" }
4748

mullvad-daemon/src/leak_checker/mod.rs

+60-29
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ use anyhow::anyhow;
22
use futures::{select, FutureExt};
33
use leak_checker::traceroute::TracerouteOpt;
44
pub use leak_checker::LeakInfo;
5+
use mullvad_types::TUNNEL_FWMARK;
56
use std::net::IpAddr;
67
use std::time::Duration;
8+
use talpid_routing::RouteManagerHandle;
79
use talpid_types::tunnel::TunnelStateTransition;
810
use tokio::sync::mpsc;
911

@@ -15,6 +17,7 @@ pub struct LeakChecker {
1517
/// [LeakChecker] internal task state.
1618
struct Task {
1719
events_rx: mpsc::UnboundedReceiver<TaskEvent>,
20+
route_manager: RouteManagerHandle,
1821
callbacks: Vec<Box<dyn LeakCheckerCallback>>,
1922
}
2023

@@ -36,11 +39,12 @@ pub trait LeakCheckerCallback: Send + 'static {
3639
}
3740

3841
impl LeakChecker {
39-
pub fn new() -> Self {
42+
pub fn new(route_manager: RouteManagerHandle) -> Self {
4043
let (task_event_tx, events_rx) = mpsc::unbounded_channel();
4144

4245
let task = Task {
4346
events_rx,
47+
route_manager,
4448
callbacks: vec![],
4549
};
4650

@@ -56,10 +60,12 @@ impl LeakChecker {
5660
self.send(TaskEvent::NewTunnelState(tunnel_state))
5761
}
5862

63+
/// Call `callback` if a leak is detected.
5964
pub fn add_leak_callback(&mut self, callback: impl LeakCheckerCallback) {
6065
self.send(TaskEvent::AddCallback(Box::new(callback)))
6166
}
6267

68+
/// Send a [TaskEvent] to the running [Task];
6369
fn send(&mut self, event: TaskEvent) {
6470
if self.task_event_tx.send(event).is_err() {
6571
panic!("LeakChecker unexpectedly closed");
@@ -92,25 +98,15 @@ impl Task {
9298
};
9399

94100
let ping_destination = tunnel.endpoint.address.ip();
101+
let route_manager = self.route_manager.clone();
102+
let leak_test = async {
103+
// Give the connection a little time to settle before starting the test.
104+
// TODO: is this necessary? is there some better way?
105+
// TODO: ether remove this or add some concrete motivation.
106+
tokio::time::sleep(Duration::from_millis(500)).await;
95107

96-
// TODO (linux):
97-
// Use get_destination_route(ip, Some(fwmark)) to figure out default interface.
98-
// where ip is some unused example public ip, or maybe the relay ip
99-
100-
// TODO (android):
101-
// Maybe connectivity monitor?
102-
// It should be possible somehow. `ifconfig` can print interfaces.
103-
// needs further investigation
104-
105-
// TODO (macos):
106-
// get_default_route in route manager
107-
108-
// TODO (windows):
109-
// Use default route monitor thingy. It should contain interfaces.
110-
// Can maybe use callback to subscribe for updates
111-
// get_best_route
112-
113-
let interface = "wlan0"; // TODO
108+
check_for_leaks(&route_manager, ping_destination).await
109+
};
114110

115111
// Make sure the tunnel state doesn't change while we're doing the leak test.
116112
// If that happens, then our results might be invalid.
@@ -134,15 +130,6 @@ impl Task {
134130
}
135131
};
136132

137-
let leak_test = async {
138-
// Give the connection a little time to settle before starting the test.
139-
// TODO: is this necessary? is there some better way?
140-
// TODO: ether remove this or add some concrete motivation.
141-
tokio::time::sleep(Duration::from_millis(500)).await;
142-
143-
check_for_leaks(interface, ping_destination).await
144-
};
145-
146133
let leak_result = select! {
147134
// If tunnel state changes, restart the test.
148135
_ = another_tunnel_state.fuse() => continue 'leak_test,
@@ -173,7 +160,51 @@ impl Task {
173160
}
174161
}
175162

176-
async fn check_for_leaks(interface: &str, destination: IpAddr) -> anyhow::Result<Option<LeakInfo>> {
163+
async fn check_for_leaks(
164+
route_manager: &RouteManagerHandle,
165+
destination: IpAddr,
166+
) -> anyhow::Result<Option<LeakInfo>> {
167+
// TODO (linux):
168+
// Use get_destination_route(ip, Some(fwmark)) to figure out default interface.
169+
// where ip is some unused example public ip, or maybe the relay ip
170+
#[cfg(target_os = "linux")]
171+
let interface = {
172+
let Ok(Some(route)) = route_manager
173+
.get_destination_route(destination, Some(TUNNEL_FWMARK))
174+
.await
175+
else {
176+
todo!("no route to relay?");
177+
};
178+
179+
route
180+
.get_node()
181+
.get_device()
182+
.expect("no device for default route??")
183+
.to_string()
184+
};
185+
186+
// TODO (android):
187+
// Maybe connectivity monitor?
188+
// It should be possible somehow. `ifconfig` can print interfaces.
189+
// needs further investigation
190+
#[cfg(target_os = "android")]
191+
let interface = todo!("get default interface");
192+
193+
// TODO (macos):
194+
// get_default_route in route manager
195+
#[cfg(target_os = "macos")]
196+
let interface = todo!("get default interface");
197+
198+
// TODO (windows):
199+
// Use default route monitor thingy. It should contain interfaces.
200+
// Can maybe use callback to subscribe for updates
201+
// get_best_route
202+
#[cfg(target_os = "macos")]
203+
let interface = todo!("get default interface");
204+
205+
log::debug!("attempting to leak traffic on interface {interface:?} to {destination}");
206+
207+
// TODO: use UDP on windows
177208
leak_checker::traceroute::try_run_leak_test(&TracerouteOpt {
178209
interface: interface.to_string(),
179210
destination,

mullvad-daemon/src/lib.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ use talpid_core::{
8383
split_tunnel,
8484
tunnel_state_machine::{self, TunnelCommand, TunnelStateMachineHandle},
8585
};
86+
use talpid_routing::RouteManagerHandle;
8687
#[cfg(target_os = "android")]
8788
use talpid_types::android::AndroidContext;
8889
#[cfg(target_os = "windows")]
@@ -182,6 +183,10 @@ pub enum Error {
182183
#[error("Tunnel state machine error")]
183184
TunnelError(#[source] tunnel_state_machine::Error),
184185

186+
/// Errors from [talpid_routing::RouteManagerHandle].
187+
#[error("Route manager error")]
188+
RouteManager(#[source] talpid_routing::Error),
189+
185190
/// Custom list already exists
186191
#[error("Custom list error: {0}")]
187192
CustomListError(#[source] mullvad_types::custom_list::Error),
@@ -769,6 +774,15 @@ impl Daemon {
769774
let _ = settings_changed_event_sender.send(InternalDaemonEvent::SettingsChanged);
770775
});
771776

777+
let route_manager = RouteManagerHandle::spawn(
778+
#[cfg(target_os = "linux")]
779+
mullvad_types::TUNNEL_FWMARK,
780+
#[cfg(target_os = "linux")]
781+
mullvad_types::TUNNEL_TABLE_ID,
782+
)
783+
.await
784+
.map_err(Error::RouteManager)?;
785+
772786
let (offline_state_tx, offline_state_rx) = mpsc::unbounded();
773787
#[cfg(target_os = "windows")]
774788
let (volume_update_tx, volume_update_rx) = mpsc::unbounded();
@@ -792,6 +806,7 @@ impl Daemon {
792806
resource_dir.clone(),
793807
internal_event_tx.to_specialized_sender(),
794808
offline_state_tx,
809+
route_manager.clone(),
795810
#[cfg(target_os = "windows")]
796811
volume_update_rx,
797812
#[cfg(target_os = "android")]
@@ -844,7 +859,7 @@ impl Daemon {
844859
);
845860

846861
let leak_checker = {
847-
let mut leak_checker = LeakChecker::new();
862+
let mut leak_checker = LeakChecker::new(route_manager);
848863
let internal_event_tx = internal_event_tx.clone();
849864
leak_checker.add_leak_callback(move |info| {
850865
internal_event_tx

talpid-core/src/tunnel_state_machine/mod.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub async fn spawn(
130130
resource_dir: PathBuf,
131131
state_change_listener: impl Sender<TunnelStateTransition> + Send + 'static,
132132
offline_state_listener: mpsc::UnboundedSender<Connectivity>,
133+
route_manager: RouteManagerHandle,
133134
#[cfg(target_os = "windows")] volume_update_rx: mpsc::UnboundedReceiver<()>,
134135
#[cfg(target_os = "android")] android_context: AndroidContext,
135136
#[cfg(target_os = "android")] connectivity_listener: ConnectivityListener,
@@ -157,6 +158,7 @@ pub async fn spawn(
157158
log_dir,
158159
resource_dir,
159160
commands_rx: command_rx,
161+
route_manager,
160162
#[cfg(target_os = "windows")]
161163
volume_update_rx,
162164
#[cfg(target_os = "android")]
@@ -254,6 +256,7 @@ struct TunnelStateMachineInitArgs<G: TunnelParametersGenerator> {
254256
log_dir: Option<PathBuf>,
255257
resource_dir: PathBuf,
256258
commands_rx: mpsc::UnboundedReceiver<TunnelCommand>,
259+
route_manager: RouteManagerHandle,
257260
#[cfg(target_os = "windows")]
258261
volume_update_rx: mpsc::UnboundedReceiver<()>,
259262
#[cfg(target_os = "android")]
@@ -276,15 +279,6 @@ impl TunnelStateMachine {
276279
#[cfg(target_os = "macos")]
277280
let filtering_resolver = crate::resolver::start_resolver().await?;
278281

279-
let route_manager = RouteManagerHandle::spawn(
280-
#[cfg(target_os = "linux")]
281-
args.linux_ids.fwmark,
282-
#[cfg(target_os = "linux")]
283-
args.linux_ids.table_id,
284-
)
285-
.await
286-
.map_err(Error::InitRouteManagerError)?;
287-
288282
#[cfg(windows)]
289283
let split_tunnel = split_tunnel::SplitTunnel::new(
290284
runtime.clone(),
@@ -322,7 +316,7 @@ impl TunnelStateMachine {
322316
#[cfg(target_os = "linux")]
323317
runtime.clone(),
324318
#[cfg(target_os = "linux")]
325-
route_manager.clone(),
319+
args.route_manager.clone(),
326320
)
327321
.map_err(Error::InitDnsMonitorError)?;
328322

@@ -341,7 +335,7 @@ impl TunnelStateMachine {
341335
let offline_monitor = offline::spawn_monitor(
342336
offline_tx,
343337
#[cfg(not(target_os = "android"))]
344-
route_manager.clone(),
338+
args.route_manager.clone(),
345339
#[cfg(target_os = "linux")]
346340
Some(args.linux_ids.fwmark),
347341
#[cfg(target_os = "android")]
@@ -381,7 +375,7 @@ impl TunnelStateMachine {
381375
runtime,
382376
firewall,
383377
dns_monitor,
384-
route_manager,
378+
route_manager: args.route_manager,
385379
_offline_monitor: offline_monitor,
386380
allow_lan: args.settings.allow_lan,
387381
#[cfg(not(target_os = "android"))]

talpid-routing/src/unix/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ mod imp;
3434

3535
pub use imp::Error as PlatformError;
3636

37-
/// Errors that can be encountered whilst initializing route manager
37+
/// Errors that can be encountered whilst interacting with a [RouteManagerHandle].
3838
#[derive(thiserror::Error, Debug)]
3939
pub enum Error {
4040
/// Route manager thread may have panicked

0 commit comments

Comments
 (0)