summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-09-14 09:23:59 +0200
committerDavid Lönnhager <david.l@mullvad.net>2020-09-23 12:45:22 +0200
commit4c78b71821233da8cbe9de9f877ad2eb097abea4 (patch)
treec3c20591286335f1fbb571ebb66fbc05af7fb09e
parent97418e0ab023d5ada1a5a432785ea7c2149ceee8 (diff)
downloadmullvadvpn-4c78b71821233da8cbe9de9f877ad2eb097abea4.tar.xz
mullvadvpn-4c78b71821233da8cbe9de9f877ad2eb097abea4.zip
Improve route cleanup in the route manager
-rw-r--r--talpid-core/src/routing/linux.rs104
1 files changed, 85 insertions, 19 deletions
diff --git a/talpid-core/src/routing/linux.rs b/talpid-core/src/routing/linux.rs
index e27b0ad849..328ce161f2 100644
--- a/talpid-core/src/routing/linux.rs
+++ b/talpid-core/src/routing/linux.rs
@@ -20,7 +20,8 @@ use netlink_packet_route::{
route::{nlas::Nla as RouteNla, RouteHeader, RouteMessage},
rtnl::{
constants::{
- RTN_UNICAST, RTPROT_STATIC, RT_SCOPE_UNIVERSE, RT_TABLE_COMPAT, RT_TABLE_MAIN,
+ RTN_UNSPEC, RTPROT_UNSPEC, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE, RT_TABLE_COMPAT,
+ RT_TABLE_MAIN,
},
RouteFlags,
},
@@ -178,8 +179,24 @@ impl RouteManagerImpl {
Err(Error::NoFreeRoutingTableId)
}
+ async fn purge_exclusions_routes(&mut self) -> Result<()> {
+ let split_routes = self.get_routes(Some(self.split_table_id)).await?;
+ for route in split_routes {
+ if let Err(error) = self.delete_route(&route).await {
+ log::warn!(
+ "Failed to delete exclusions route: {}\n{}",
+ route,
+ error.display_chain()
+ );
+ }
+ }
+ Ok(())
+ }
+
async fn initialize_exclusions_routes(&mut self) -> Result<()> {
- let main_routes = self.get_routes().await?;
+ self.purge_exclusions_routes().await?;
+
+ let main_routes = self.get_routes(None).await?;
for mut route in main_routes {
route.table_id = self.split_table_id;
self.add_route_direct(route).await?;
@@ -331,21 +348,30 @@ impl RouteManagerImpl {
Ok(())
}
- async fn get_routes(&self) -> Result<HashSet<Route>> {
- let mut routes = self.get_routes_inner(IpVersion::V4).await?;
- routes.extend(self.get_routes_inner(IpVersion::V6).await?);
+ async fn get_routes(&self, table_id: Option<u32>) -> Result<HashSet<Route>> {
+ let mut routes = self.get_routes_inner(IpVersion::V4, table_id).await?;
+ routes.extend(self.get_routes_inner(IpVersion::V6, table_id).await?);
Ok(routes)
}
- async fn get_routes_inner(&self, version: IpVersion) -> Result<HashSet<Route>> {
+ async fn get_routes_inner(
+ &self,
+ version: IpVersion,
+ table_id: Option<u32>,
+ ) -> Result<HashSet<Route>> {
let mut routes = HashSet::new();
+ let table_id = table_id.unwrap_or(RT_TABLE_MAIN as u32);
let mut route_request = self.handle.route().get(version).execute();
+
while let Some(route) = route_request
.try_next()
.await
.map_err(Error::NetlinkError)?
{
- if let Some(route) = self.parse_route_message(route)? {
+ if let Some(route) = self.parse_route_message_inner(route)? {
+ if route.table_id != table_id {
+ continue;
+ }
routes.insert(route);
}
}
@@ -401,7 +427,13 @@ impl RouteManagerImpl {
{
let mut exclusions_route = route.clone();
exclusions_route.table_id = self.split_table_id;
- self.add_route_direct(exclusions_route).await?;
+ if let Err(error) = self.add_route_direct(exclusions_route.clone()).await {
+ log::warn!(
+ "Failed to add exclusions route: {}\n{}",
+ exclusions_route,
+ error.display_chain(),
+ );
+ }
}
if route.prefix.prefix() == 0 {
@@ -418,9 +450,11 @@ impl RouteManagerImpl {
let mut exclusions_route = route.clone();
exclusions_route.table_id = self.split_table_id;
if let Err(error) = self.delete_route(&exclusions_route).await {
- log::warn!(
- "{}",
- error.display_chain_with_msg("Failed to remove exclusions route")
+ // This may be expected when routes are deleted by the kernel
+ log::debug!(
+ "Failed to remove exclusions route: {}\n{}",
+ exclusions_route,
+ error.display_chain(),
);
}
}
@@ -584,7 +618,7 @@ impl RouteManagerImpl {
match command {
RouteManagerCommand::Shutdown(shutdown_signal) => {
log::trace!("Shutting down route manager");
- self.cleanup_routes().await;
+ self.destructor().await;
log::trace!("Route manager done");
let _ = shutdown_signal.send(());
return Err(Error::Shutdown);
@@ -653,8 +687,11 @@ impl RouteManagerImpl {
if msg.header.table != RT_TABLE_MAIN {
return Ok(None);
}
+ self.parse_route_message_inner(msg)
+ }
-
+ // Tries to coax a Route out of a RouteMessage
+ fn parse_route_message_inner(&self, msg: RouteMessage) -> Result<Option<Route>> {
let mut prefix = None;
let mut node_addr = None;
let mut device = None;
@@ -759,6 +796,23 @@ impl RouteManagerImpl {
async fn delete_route(&self, route: &Route) -> Result<()> {
let compat_table = compat_table_id(route.table_id);
+ let scope = match route.prefix {
+ IpNetwork::V4(v4_prefix) => {
+ if v4_prefix.prefix() > 0 && v4_prefix.prefix() < 32 {
+ RT_SCOPE_LINK
+ } else {
+ RT_SCOPE_UNIVERSE
+ }
+ }
+ IpNetwork::V6(v6_prefix) => {
+ if v6_prefix.prefix() > 0 && v6_prefix.prefix() < 128 {
+ RT_SCOPE_LINK
+ } else {
+ RT_SCOPE_UNIVERSE
+ }
+ }
+ };
+
let mut route_message = RouteMessage {
header: RouteHeader {
address_family: if route.prefix.is_ipv4() {
@@ -770,9 +824,9 @@ impl RouteManagerImpl {
destination_prefix_length: route.prefix.prefix(),
tos: 0u8,
table: compat_table,
- protocol: RTPROT_STATIC,
- scope: RT_SCOPE_UNIVERSE,
- kind: RTN_UNICAST,
+ protocol: RTPROT_UNSPEC,
+ scope,
+ kind: RTN_UNSPEC,
flags: RouteFlags::empty(),
},
nlas: vec![RouteNla::Destination(ip_to_bytes(route.prefix.ip()))],
@@ -796,6 +850,9 @@ impl RouteManagerImpl {
route_message.nlas.push(gateway_nla);
}
+ if let Some(metric) = route.metric {
+ route_message.nlas.push(RouteNla::Priority(metric));
+ }
self.handle
.route()
@@ -864,8 +921,7 @@ impl RouteManagerImpl {
// TODO: Request support for route priority in RouteAddIpv{4,6}Request
if let Some(metric) = route.metric {
- use netlink_packet_route::nlas::route;
- add_message.nlas.push(route::Nla::Priority(metric));
+ add_message.nlas.push(RouteNla::Priority(metric));
}
// Need to modify the request in place to set the correct flags to be able to replace any
@@ -890,11 +946,21 @@ impl RouteManagerImpl {
self.added_routes.insert(route);
Ok(())
}
+
+ async fn destructor(&mut self) {
+ if let Err(error) = self.purge_exclusions_routes().await {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to flush exclusions routes")
+ );
+ }
+ self.cleanup_routes().await;
+ }
}
impl Drop for RouteManagerImpl {
fn drop(&mut self) {
- futures::executor::block_on(self.cleanup_routes())
+ futures::executor::block_on(self.destructor());
}
}