diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-02-13 09:22:33 -0200 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-02-13 09:22:33 -0200 |
| commit | 3571f7a58af0fd838110d06beb91f21b737deccb (patch) | |
| tree | d14fadc67c18d5c06250de60d78461a310334ce4 /talpid-core/src | |
| parent | 2aa65d23e6303536077e3e2189605554086a49e3 (diff) | |
| parent | 9139eb7c67d995910b6b9e1411d04acc93b64c08 (diff) | |
| download | mullvadvpn-3571f7a58af0fd838110d06beb91f21b737deccb.tar.xz mullvadvpn-3571f7a58af0fd838110d06beb91f21b737deccb.zip | |
Merge branch 'update-linux-offline-check'
Diffstat (limited to 'talpid-core/src')
| -rw-r--r-- | talpid-core/src/offline/linux.rs | 198 |
1 files changed, 96 insertions, 102 deletions
diff --git a/talpid-core/src/offline/linux.rs b/talpid-core/src/offline/linux.rs index cc18a48a78..8adff502ca 100644 --- a/talpid-core/src/offline/linux.rs +++ b/talpid-core/src/offline/linux.rs @@ -1,13 +1,10 @@ use crate::tunnel_state_machine::TunnelCommand; use error_chain::ChainedError; use futures::{future::Either, sync::mpsc::UnboundedSender, Future, Stream}; -use iproute2::Link; -use log::{error, trace, warn}; +use iproute2::{Address, Connection, ConnectionHandle, Link, NetlinkIpError}; +use log::{error, warn}; use netlink_socket::{Protocol, SocketAddr, TokioSocket}; -use rtnetlink::{ - LinkFlags, LinkHeader, LinkLayerType, LinkMessage, NetlinkCodec, NetlinkFramed, NetlinkMessage, - RtnlMessage, -}; +use rtnetlink::{LinkLayerType, NetlinkCodec, NetlinkFramed, NetlinkMessage}; use std::{collections::BTreeSet, thread}; error_chain! { @@ -32,6 +29,8 @@ error_chain! { const RTMGRP_NOTIFY: u32 = 1; const RTMGRP_LINK: u32 = 2; +const RTMGRP_IPV4_IFADDR: u32 = 0x10; +const RTMGRP_IPV6_IFADDR: u32 = 0x100; pub struct MonitorHandle; @@ -39,11 +38,14 @@ pub fn spawn_monitor(sender: UnboundedSender<TunnelCommand>) -> Result<MonitorHa let mut socket = TokioSocket::new(Protocol::Route).chain_err(|| ErrorKind::NetlinkConnectionError)?; socket - .bind(&SocketAddr::new(0, RTMGRP_NOTIFY | RTMGRP_LINK)) + .bind(&SocketAddr::new( + 0, + RTMGRP_NOTIFY | RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR, + )) .chain_err(|| ErrorKind::NetlinkBindError)?; let channel = NetlinkFramed::new(socket, NetlinkCodec::<NetlinkMessage>::new()); - let link_monitor = LinkMonitor::new(sender)?; + let link_monitor = LinkMonitor::new(sender); thread::spawn(|| { if let Err(error) = monitor_event_loop(channel, link_monitor) { @@ -63,51 +65,103 @@ pub fn is_offline() -> bool { }) } +/// Checks if there are no running links or that none of the running links have IP addresses +/// assigned to them. fn check_if_offline() -> Result<bool> { - Ok(list_links_providing_connectivity()?.next().is_none()) + let mut connection = NetlinkConnection::new()?; + let interfaces = connection.running_interfaces()?; + + if interfaces.is_empty() { + Ok(true) + } else { + // Check if the current IP addresses are not assigned to any one of the running interfaces + Ok(connection + .addresses()? + .into_iter() + .all(|address| !interfaces.contains(&address.index()))) + } +} + +struct NetlinkConnection { + connection: Option<Connection>, + connection_handle: ConnectionHandle, } -fn list_links_providing_connectivity() -> Result<impl Iterator<Item = Link>> { - Ok(list_links()?.into_iter().filter(link_provides_connectivity)) +impl NetlinkConnection { + /// Open a connection on the netlink socket. + pub fn new() -> Result<Self> { + let (connection, connection_handle) = + iproute2::new_connection().chain_err(|| ErrorKind::NetlinkConnectionError)?; + + Ok(NetlinkConnection { + connection: Some(connection), + connection_handle, + }) + } + + /// List all IP addresses assigned to all interfaces. + pub fn addresses(&mut self) -> Result<Vec<Address>> { + self.execute_request(self.connection_handle.address().get().execute()) + } + + /// List all links registered on the system. + fn links(&mut self) -> Result<Vec<Link>> { + self.execute_request(self.connection_handle.link().get().execute()) + } + + /// List all unique interface indices that have a running link. + pub fn running_interfaces(&mut self) -> Result<BTreeSet<u32>> { + let links = self.links()?; + + Ok(links + .into_iter() + .filter(link_provides_connectivity) + .map(|link| link.index()) + .collect()) + } + + /// Helper function to execute an asynchronous request synchronously. + fn execute_request<R>(&mut self, request: R) -> Result<R::Item> + where + R: Future<Error = NetlinkIpError>, + { + let connection = self + .connection + .take() + .ok_or(ErrorKind::NetlinkDisconnected)?; + + let (result, connection) = match connection.select2(request).wait() { + Ok(Either::A(_)) => bail!(ErrorKind::NetlinkDisconnected), + Err(Either::A((error, _))) => bail!(Error::with_chain(error, ErrorKind::NetlinkError)), + Ok(Either::B((links, connection))) => (Ok(links), connection), + Err(Either::B((error, connection))) => ( + Err(Error::with_chain( + failure::Fail::compat(error), + ErrorKind::GetLinksError, + )), + connection, + ), + }; + + self.connection = Some(connection); + result + } } -fn link_provides_connectivity(link: &impl BasicLinkInfo) -> bool { +fn link_provides_connectivity(link: &Link) -> bool { // Some tunnels have the link layer type set to None link.link_layer_type() != LinkLayerType::Loopback && link.link_layer_type() != LinkLayerType::None && link.flags().is_running() } -fn list_links() -> Result<Vec<Link>> { - let (connection, connection_handle) = - iproute2::new_connection().chain_err(|| ErrorKind::NetlinkConnectionError)?; - let links_request = connection_handle.link().get().execute(); - - match connection.select2(links_request).wait() { - Ok(Either::A(_)) => bail!(ErrorKind::NetlinkDisconnected), - Ok(Either::B((links, _))) => Ok(links), - Err(Either::A((error, _))) => Err(Error::with_chain(error, ErrorKind::NetlinkError)), - Err(Either::B((error, _))) => Err(Error::with_chain( - failure::Fail::compat(error), - ErrorKind::GetLinksError, - )), - } -} - fn monitor_event_loop( channel: NetlinkFramed<NetlinkCodec<NetlinkMessage>>, mut link_monitor: LinkMonitor, ) -> Result<()> { channel - .for_each(|(message, _address)| { - let (_header, payload) = message.into_parts(); - - match payload { - RtnlMessage::NewLink(link_message) => link_monitor.new_link(link_message), - RtnlMessage::DelLink(link_message) => link_monitor.del_link(link_message), - _ => trace!("Ignoring unknown link message"), - } - + .for_each(|(_message, _address)| { + link_monitor.update(); Ok(()) }) .wait() @@ -120,41 +174,18 @@ fn monitor_event_loop( struct LinkMonitor { is_offline: bool, - running_links: BTreeSet<u32>, sender: UnboundedSender<TunnelCommand>, } impl LinkMonitor { - pub fn new(sender: UnboundedSender<TunnelCommand>) -> Result<Self> { - let links: Vec<Link> = list_links_providing_connectivity()?.collect(); - let is_offline = links.is_empty(); - let running_links = links.into_iter().map(|link| link.index()).collect(); + pub fn new(sender: UnboundedSender<TunnelCommand>) -> Self { + let is_offline = is_offline(); - Ok(LinkMonitor { - is_offline, - running_links, - sender, - }) - } - - pub fn new_link(&mut self, link_message: LinkMessage) { - if self.is_offline && link_provides_connectivity(link_message.header()) { - self.set_is_offline(false); - } - - if let Ok(link) = Link::from_link_message(link_message) { - if link_provides_connectivity(&link) { - self.insert_link(link.index()); - } else { - self.remove_link(link.index()); - } - } + LinkMonitor { is_offline, sender } } - pub fn del_link(&mut self, link_message: LinkMessage) { - if let Ok(link) = Link::from_link_message(link_message) { - self.remove_link(link.index()); - } + pub fn update(&mut self) { + self.set_is_offline(is_offline()); } fn set_is_offline(&mut self, is_offline: bool) { @@ -165,41 +196,4 @@ impl LinkMonitor { .unbounded_send(TunnelCommand::IsOffline(is_offline)); } } - - fn insert_link(&mut self, link_index: u32) { - self.running_links.insert(link_index); - self.set_is_offline(false); - } - - fn remove_link(&mut self, link_index: u32) { - self.running_links.remove(&link_index); - if self.running_links.is_empty() { - self.set_is_offline(true); - } - } -} - -trait BasicLinkInfo { - fn flags(&self) -> LinkFlags; - fn link_layer_type(&self) -> LinkLayerType; -} - -impl BasicLinkInfo for Link { - fn flags(&self) -> LinkFlags { - self.flags() - } - - fn link_layer_type(&self) -> LinkLayerType { - self.link_layer_type() - } -} - -impl BasicLinkInfo for LinkHeader { - fn flags(&self) -> LinkFlags { - self.flags() - } - - fn link_layer_type(&self) -> LinkLayerType { - self.link_layer_type() - } } |
