diff options
| author | Emīls <emils@mullvad.net> | 2020-10-28 10:28:17 +0000 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2020-10-28 10:28:17 +0000 |
| commit | 56a58b6e658f92ff87c9ac53adf506a5915a89fb (patch) | |
| tree | 201ec9fef9c24ca000b80ee79c6a63c80e717646 | |
| parent | 116ae8d935a3a46492cce3258427d6bcbaab4796 (diff) | |
| parent | 97595ca7d69ed8df405d471187766e9d71f20680 (diff) | |
| download | mullvadvpn-56a58b6e658f92ff87c9ac53adf506a5915a89fb.tar.xz mullvadvpn-56a58b6e658f92ff87c9ac53adf506a5915a89fb.zip | |
Merge branch 'linux-ignore-loopback-routes' into master
| -rw-r--r-- | CHANGELOG.md | 4 | ||||
| -rw-r--r-- | talpid-core/src/routing/linux.rs | 53 |
2 files changed, 46 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ccb8f64fa..fd897c8bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,8 +46,12 @@ Line wrap the file at 100 chars. Th more smoothly. - Run app in landscape mode on TVs. +#### Linux +- Make route monitor ignore loopback routes. + ### Fixed - Fix missing map animation after selecting a new location in the desktop app. +- Fix crash on older kernels which report a default route through the loopback interface. #### Android - Fix connect action button sometimes showing itself as "Cancel" instead of "Secure my connection" diff --git a/talpid-core/src/routing/linux.rs b/talpid-core/src/routing/linux.rs index 9b7d016d1a..278ec8afbf 100644 --- a/talpid-core/src/routing/linux.rs +++ b/talpid-core/src/routing/linux.rs @@ -16,6 +16,7 @@ use futures::{channel::mpsc::UnboundedReceiver, future::FutureExt, StreamExt, Tr use netlink_packet_route::{ + constants::ARPHRD_LOOPBACK, link::{nlas::Nla as LinkNla, LinkMessage}, route::{nlas::Nla as RouteNla, RouteHeader, RouteMessage}, rtnl::{ @@ -81,7 +82,7 @@ struct RequiredDefaultRoute { pub struct RouteManagerImpl { handle: Handle, messages: UnboundedReceiver<(NetlinkMessage<RtnlMessage>, SocketAddr)>, - iface_map: BTreeMap<u32, String>, + iface_map: BTreeMap<u32, NetworkInterface>, // currently added routes added_routes: HashSet<Route>, @@ -428,22 +429,25 @@ impl RouteManagerImpl { Ok(routes) } - async fn initialize_link_map(handle: &rtnetlink::Handle) -> Result<BTreeMap<u32, String>> { + async fn initialize_link_map( + handle: &rtnetlink::Handle, + ) -> Result<BTreeMap<u32, NetworkInterface>> { let mut link_map = BTreeMap::new(); let mut link_request = handle.link().get().execute(); while let Some(link) = link_request.try_next().await.map_err(Error::NetlinkError)? { - if let Some((idx, link_name)) = Self::map_iface_name_to_idx(link) { - link_map.insert(idx, link_name); + if let Some((idx, device)) = Self::map_interface(link) { + link_map.insert(idx, device); } } Ok(link_map) } + fn find_iface_idx(&self, iface_name: &str) -> Option<u32> { self.iface_map .iter() - .find(|(_idx, name)| name.as_str() == iface_name) + .find(|(_idx, iface)| iface.name.as_str() == iface_name) .map(|(idx, _name)| *idx) } @@ -680,12 +684,12 @@ impl RouteManagerImpl { async fn process_netlink_message(&mut self, msg: NetlinkMessage<RtnlMessage>) -> Result<()> { match msg.payload { NetlinkPayload::InnerMessage(RtnlMessage::NewLink(new_link)) => { - if let Some((idx, name)) = Self::map_iface_name_to_idx(new_link) { + if let Some((idx, name)) = Self::map_interface(new_link) { self.iface_map.insert(idx, name); } } NetlinkPayload::InnerMessage(RtnlMessage::DelLink(old_link)) => { - if let Some((idx, _)) = Self::map_iface_name_to_idx(old_link) { + if let Some((idx, _)) = Self::map_interface(old_link) { self.iface_map.remove(&idx); } } @@ -731,7 +735,13 @@ impl RouteManagerImpl { match nla { RouteNla::Oif(device_idx) => { match self.iface_map.get(&device_idx) { - Some(device_name) => device = Some(device_name.to_string()), + Some(route_device) => { + if route_device.is_loopback() { + log::debug!("Ignoring route with interface '{}' because it's a loopback interface", route_device.name); + return Ok(None); + } + device = Some(route_device); + } None => { return Err(Error::UnknownDeviceIndex(*device_idx)); } @@ -767,6 +777,7 @@ impl RouteManagerImpl { } } + // when a gateway is specified but prefix is none, then this is a default route if prefix.is_none() && gateway.is_some() { prefix = match af_spec as i32 { @@ -783,7 +794,7 @@ impl RouteManagerImpl { let node = Node { ip: node_addr.or(gateway), - device, + device: device.map(|dev| dev.name.clone()), }; Ok(Some(Route { @@ -794,13 +805,21 @@ impl RouteManagerImpl { })) } - fn map_iface_name_to_idx(msg: LinkMessage) -> Option<(u32, String)> { + fn map_interface(msg: LinkMessage) -> Option<(u32, NetworkInterface)> { let index = msg.header.index; + let link_layer_type = msg.header.link_layer_type; for nla in msg.nlas { if let LinkNla::IfName(name) = nla { - return Some((index, name)); + return Some(( + index, + NetworkInterface { + name, + link_layer_type, + }, + )); } } + None } @@ -1014,6 +1033,18 @@ fn compat_table_id(id: u32) -> u8 { } } +#[derive(Debug)] +struct NetworkInterface { + name: String, + link_layer_type: u16, +} + +impl NetworkInterface { + fn is_loopback(&self) -> bool { + self.link_layer_type == ARPHRD_LOOPBACK + } +} + #[cfg(test)] mod test { use super::*; |
