diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-04-15 22:00:32 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2020-06-02 10:05:02 +0200 |
| commit | 7731393aae9f476b4e64593b4357a17bab9c4f68 (patch) | |
| tree | b3b0e02c4a16dcecab6bca7b733fb9d08a051b7e | |
| parent | a612c583a3a0524ecac09c78f740d4eadad2960a (diff) | |
| download | mullvadvpn-7731393aae9f476b4e64593b4357a17bab9c4f68.tar.xz mullvadvpn-7731393aae9f476b4e64593b4357a17bab9c4f68.zip | |
Set up routes for excluded applications using the route manager
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 1 | ||||
| -rw-r--r-- | talpid-core/src/split.rs | 130 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 24 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/disconnected_state.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/mod.rs | 1 |
6 files changed, 53 insertions, 117 deletions
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs index c9e35f9c2f..4d0a13b9e8 100644 --- a/talpid-core/src/firewall/linux.rs +++ b/talpid-core/src/firewall/linux.rs @@ -305,7 +305,6 @@ impl<'a> PolicyBatch<'a> { let mut rule = Rule::new(&self.nat_chain); rule.add_expr(&nft_expr!(ct mark)); rule.add_expr(&nft_expr!(cmp == split::MARK)); - // TODO: match oif interface rule.add_expr(&nft_expr!(masquerade)); add_verdict(&mut rule, &Verdict::Accept); self.batch.add(&rule, nftnl::MsgType::Add); diff --git a/talpid-core/src/split.rs b/talpid-core/src/split.rs index 7ae0f365c5..bf0ec8e7b0 100644 --- a/talpid-core/src/split.rs +++ b/talpid-core/src/split.rs @@ -1,12 +1,14 @@ #![cfg(target_os = "linux")] +use crate::routing::{NetNode, Node, RequiredRoute, RouteManager}; +use ipnetwork::IpNetwork; use regex::Regex; use std::{ + collections::HashSet, fs, io::{self, BufRead, BufReader, BufWriter, Read, Seek, Write}, - net::{AddrParseError, IpAddr}, + net::{IpAddr, Ipv4Addr}, path::Path, process::Command, - str::FromStr, }; use talpid_types::SPLIT_TUNNEL_CGROUP_NAME; @@ -26,18 +28,6 @@ const RT_TABLES_PATH: &str = "/etc/iproute2/rt_tables"; #[derive(err_derive::Error, Debug)] #[error(no_from)] pub enum Error { - /// Unable to list routing table entries. - #[error(display = "Failed to enumerate routes")] - EnumerateRoutes(#[error(source)] io::Error), - - /// Unable to find the interface/ip pair used by the physical interface. - #[error(display = "No default route found")] - NoDefaultRoute, - - /// Failed to parse string containing an IP address. May be invalid. - #[error(display = "Failed to parse IP address")] - ParseIpError(#[error(source)] AddrParseError), - /// Failed to run the process. #[error(display = "Unable to execute process")] ExecFailed(#[error(source)] io::Error), @@ -74,41 +64,13 @@ pub enum Error { #[error(display = "Unable to obtain PIDs from cgroup.procs")] ListCGroupPids(#[error(source)] io::Error), + /// Unable to add route to the exclusions table. + #[error(display = "Failed to add routing table entry")] + SetupRouting(#[error(source)] crate::routing::Error), + /// Unable to add setup DNS routing. #[error(display = "Failed to add routing table DNS rules")] - SetDns(#[error(source)] io::Error), -} - -struct DefaultRoute { - interface: String, - address: IpAddr, -} - -/// Obtain the IP/interface of the physical interface -fn get_default_route() -> Result<DefaultRoute, Error> { - // FIXME: use netlink - let mut cmd = Command::new("ip"); - cmd.args(&["-4", "route", "list", "table", "main"]); - log::trace!("running cmd - {:?}", &cmd); - let out = cmd.output().map_err(Error::EnumerateRoutes)?; - let out_str = String::from_utf8_lossy(&out.stdout); - - // Find "default" row - let expression = Regex::new(r"^default via ([0-9.]+) dev (\w+)").unwrap(); - - for line in out_str.lines() { - if let Some(captures) = expression.captures(&line) { - let ip_str = captures.get(1).unwrap().as_str(); - let interface = captures.get(2).unwrap().as_str().to_string(); - - return Ok(DefaultRoute { - interface, - address: IpAddr::from_str(ip_str).map_err(Error::ParseIpError)?, - }); - } - } - - Err(Error::NoDefaultRoute) + SetDns(#[error(source)] crate::routing::Error), } /// Manage routing for split tunneling cgroup. @@ -185,28 +147,8 @@ impl SplitTunnel { writeln!(file, "{} {}", self.table_id, ROUTING_TABLE_NAME).map_err(Error::RoutingTableSetup) } - /// Reset the split-tunneling routing table to its default state - fn reset_table() -> Result<(), Error> { - let _ = exec_ip(&["-4", "route", "flush", "table", ROUTING_TABLE_NAME]); - - // Force routing through the physical interface - let default_route = get_default_route()?; - exec_ip(&[ - "-4", - "route", - "add", - "default", - "via", - &default_route.address.to_string(), - "dev", - &default_route.interface, - "table", - ROUTING_TABLE_NAME, - ]) - } - /// Route PID-associated packets through the physical interface. - pub fn enable_routing(&self) -> Result<(), Error> { + pub fn enable_routing(&self, route_manager: &mut RouteManager) -> Result<(), Error> { // TODO: IPv6 // Create the rule if it does not exist @@ -231,11 +173,20 @@ impl SplitTunnel { ])?; } - Self::reset_table() + // Add default route for the exclusions table + let zero_network = + ipnetwork::IpNetwork::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0).unwrap(); + let mut required_routes = HashSet::new(); + required_routes.insert( + RequiredRoute::new(zero_network, NetNode::DefaultNode).table(self.table_id as u8), + ); + route_manager + .add_routes(required_routes) + .map_err(Error::SetupRouting) } /// Stop routing PID-associated packets through the physical interface. - pub fn disable_routing(&self) -> Result<(), Error> { + pub fn disable_routing(&self) { // TODO: IPv6 if let Err(e) = exec_ip(&[ @@ -251,35 +202,28 @@ impl SplitTunnel { ]) { log::warn!("Failed to delete routing policy: {}", e); } - - Ok(()) } /// Route DNS requests through the tunnel interface. - pub fn route_dns(&self, tunnel_alias: &str, dns_servers: &[IpAddr]) -> Result<(), Error> { + pub fn route_dns( + &self, + route_manager: &mut RouteManager, + tunnel_alias: &str, + dns_servers: &[IpAddr], + ) -> Result<(), Error> { + let mut dns_routes = HashSet::new(); + for server in dns_servers { - if let IpAddr::V4(addr) = server { - let addr = addr.to_string(); - exec_ip(&[ - "-4", - "route", - "replace", - &addr, - "dev", - tunnel_alias, - "table", - ROUTING_TABLE_NAME, - ])?; - } + dns_routes.insert( + RequiredRoute::new( + IpNetwork::from(*server), + Node::device(tunnel_alias.to_string()), + ) + .table(self.table_id as u8), + ); } - Ok(()) - } - - /// Reset DNS rules. - pub fn flush_dns(&self) -> Result<(), Error> { - // For now, simply flush it - Self::reset_table() + route_manager.add_routes(dns_routes).map_err(Error::SetDns) } } diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index 3c99d5b098..ef5362d635 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -75,30 +75,28 @@ impl ConnectedState { dns_ips.push(ipv6_gateway.into()); }; + shared_values + .dns_monitor + .set(&self.metadata.interface, &dns_ips) + .map_err(BoxedError::new)?; + #[cfg(target_os = "linux")] shared_values .split_tunnel - .route_dns(&self.metadata.interface, &dns_ips) + .route_dns( + &mut shared_values.route_manager, + &self.metadata.interface, + &dns_ips, + ) .map_err(BoxedError::new)?; - shared_values - .dns_monitor - .set(&self.metadata.interface, &dns_ips) - .map_err(BoxedError::new) + Ok(()) } fn reset_dns(shared_values: &mut SharedTunnelStateValues) { if let Err(error) = shared_values.dns_monitor.reset() { log::error!("{}", error.display_chain_with_msg("Unable to reset DNS")); } - - #[cfg(target_os = "linux")] - if let Err(error) = shared_values.split_tunnel.flush_dns() { - log::error!( - "{}", - error.display_chain_with_msg("Unable to update split-tunnel route") - ); - } } fn reset_routes(shared_values: &mut SharedTunnelStateValues) { diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 2c895fb707..3a34f7fb0f 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -6,7 +6,6 @@ use super::{ use crate::{ firewall::FirewallPolicy, routing::RouteManager, - split, tunnel::{ self, tun_provider::TunProvider, CloseHandle, TunnelEvent, TunnelMetadata, TunnelMonitor, }, @@ -360,7 +359,10 @@ impl TunnelState for ConnectingState { ErrorState::enter(shared_values, ErrorStateCause::StartTunnelError) } else { #[cfg(target_os = "linux")] - if let Err(error) = shared_values.split_tunnel.enable_routing() { + if let Err(error) = shared_values + .split_tunnel + .enable_routing(&mut shared_values.route_manager) + { error!( "{}", error.display_chain_with_msg("Failed to set up split tunneling") diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index 4cb0a97e20..ac3b33aed5 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -40,13 +40,7 @@ impl TunnelState for DisconnectedState { _: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { #[cfg(target_os = "linux")] - if let Err(error) = shared_values.split_tunnel.disable_routing() { - log::error!( - "{}", - error.display_chain_with_msg("Failed to update routing") - ); - } - + shared_values.split_tunnel.disable_routing(); Self::set_firewall_policy(shared_values); #[cfg(target_os = "android")] shared_values.tun_provider.close_tun(); diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index bf4a17bad4..0393be3418 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -22,7 +22,6 @@ use crate::{ mpsc::Sender, offline, routing::RouteManager, - split, tunnel::tun_provider::TunProvider, }; |
