diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-10-22 18:57:09 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2020-10-26 13:47:00 +0100 |
| commit | 59d9aed318512aefa504bce6a048fb70592f5b06 (patch) | |
| tree | 9384153cb0f544c193d317907242648433d27cb2 | |
| parent | c3398d6f18613866923e648828002927e038bdcb (diff) | |
| download | mullvadvpn-59d9aed318512aefa504bce6a048fb70592f5b06.tar.xz mullvadvpn-59d9aed318512aefa504bce6a048fb70592f5b06.zip | |
Correctly set new DNS routes in exclusions table
| -rw-r--r-- | talpid-core/src/routing/linux.rs | 74 |
1 files changed, 49 insertions, 25 deletions
diff --git a/talpid-core/src/routing/linux.rs b/talpid-core/src/routing/linux.rs index dbe35f2c96..9b7d016d1a 100644 --- a/talpid-core/src/routing/linux.rs +++ b/talpid-core/src/routing/linux.rs @@ -94,6 +94,8 @@ pub struct RouteManagerImpl { split_table_id: u32, split_ignored_interface: Option<String>, + + dns_routes: HashSet<Route>, } impl RouteManagerImpl { @@ -129,6 +131,8 @@ impl RouteManagerImpl { split_table_id, split_ignored_interface: None, + + dns_routes: HashSet::new(), }; monitor.initialize_exclusions_routes().await?; @@ -265,26 +269,47 @@ impl RouteManagerImpl { } } + async fn clear_exclusions_dns(&mut self) -> Result<()> { + for route in self.dns_routes.clone() { + self.delete_route_if_exists(&route).await?; + self.dns_routes.remove(&route); + self.added_routes.remove(&route); + } + self.dns_routes.clear(); + + Ok(()) + } + /// Route DNS requests through the tunnel interface. - #[cfg(target_os = "linux")] async fn route_exclusions_dns( &mut self, tunnel_alias: &str, dns_servers: &[IpAddr], ) -> Result<()> { + self.clear_exclusions_dns().await?; + let mut dns_routes = HashSet::new(); for server in dns_servers { dns_routes.insert( - RequiredRoute::new( - IpNetwork::from(*server), + Route::new( Node::device(tunnel_alias.to_string()), + IpNetwork::from(*server), ) .table(self.split_table_id), ); } - self.add_required_routes(dns_routes).await + if let Some(capacity_needed) = dns_routes.len().checked_sub(self.dns_routes.capacity()) { + self.dns_routes.reserve(capacity_needed); + } + + for route in dns_routes { + self.add_route(route.clone()).await?; + self.dns_routes.insert(route); + } + + Ok(()) } async fn add_required_default_routes( @@ -478,9 +503,8 @@ impl RouteManagerImpl { self.default_routes.remove(&route); self.update_default_routes().await?; } - if self.added_routes.contains(&route) { - self.added_routes.remove(&route); - } + self.added_routes.remove(&route); + self.dns_routes.remove(&route); Ok(()) } @@ -583,32 +607,17 @@ impl RouteManagerImpl { let route = Route::new(best_node, required_route.destination).table(required_route.table_id); - if let Err(e) = self.delete_route(&route).await { - if let Error::NetlinkError(err) = &e { - if let rtnetlink::Error::NetlinkError(msg) = err { - // -3 means that the route doesn't exist anymore anyway - if msg.code == -3 { - continue; - } - } - } + if let Err(e) = self.delete_route_if_exists(&route).await { log::error!("Failed to remove route - {} - {}", route, e); } } self.required_default_routes.clear(); for route in self.added_routes.drain().collect::<Vec<_>>().iter() { - if let Err(e) = self.delete_route(&route).await { - if let Error::NetlinkError(err) = &e { - if let rtnetlink::Error::NetlinkError(msg) = err { - // -3 means that the route doesn't exist anymore anyway - if msg.code == -3 { - continue; - } - } - } + if let Err(e) = self.delete_route_if_exists(&route).await { log::error!("Failed to remove route - {} - {}", route, e); } + self.dns_routes.remove(&route); } } @@ -810,6 +819,21 @@ impl RouteManagerImpl { } } + async fn delete_route_if_exists(&self, route: &Route) -> Result<()> { + if let Err(error) = self.delete_route(route).await { + if let Error::NetlinkError(netlink_error) = &error { + if let rtnetlink::Error::NetlinkError(msg) = &netlink_error { + if msg.code == -libc::ESRCH { + return Ok(()); + } + } + } + Err(error) + } else { + Ok(()) + } + } + async fn delete_route(&self, route: &Route) -> Result<()> { let compat_table = compat_table_id(route.table_id); let scope = match route.prefix { |
