summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-02-13 09:22:33 -0200
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2019-02-13 09:22:33 -0200
commit3571f7a58af0fd838110d06beb91f21b737deccb (patch)
treed14fadc67c18d5c06250de60d78461a310334ce4 /talpid-core/src
parent2aa65d23e6303536077e3e2189605554086a49e3 (diff)
parent9139eb7c67d995910b6b9e1411d04acc93b64c08 (diff)
downloadmullvadvpn-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.rs198
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()
- }
}