summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls <emils@mullvad.net>2020-11-06 16:23:29 +0000
committerEmīls <emils@mullvad.net>2020-11-11 10:16:23 +0000
commit142cdbe7b1f09ac505d3b06d5d3b0a8839241e6d (patch)
treea0dbf878bfb8079f5b35b3d6768778eaf4912865
parente21e8e98cd82c0ba005754a71182867b317a8f6a (diff)
downloadmullvadvpn-142cdbe7b1f09ac505d3b06d5d3b0a8839241e6d.tar.xz
mullvadvpn-142cdbe7b1f09ac505d3b06d5d3b0a8839241e6d.zip
Divide default route if necessary when building exclsuion table
-rw-r--r--talpid-core/src/routing/linux.rs42
-rw-r--r--talpid-core/src/routing/mod.rs13
2 files changed, 52 insertions, 3 deletions
diff --git a/talpid-core/src/routing/linux.rs b/talpid-core/src/routing/linux.rs
index 8667026294..9cf1126558 100644
--- a/talpid-core/src/routing/linux.rs
+++ b/talpid-core/src/routing/linux.rs
@@ -73,6 +73,18 @@ pub enum Error {
Shutdown,
}
+impl Error {
+ /// Returns true only if it's a netlink error with a code ENETUNREACH
+ fn is_network_unreachable(&self) -> bool {
+ match self {
+ Error::NetlinkError(rtnetlink::Error::NetlinkError(err)) => {
+ err.code == -libc::ENETUNREACH
+ }
+ _ => false,
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct RequiredDefaultRoute {
table_id: u32,
@@ -203,10 +215,33 @@ impl RouteManagerImpl {
let mut main_routes = self.get_routes(None).await?.into_iter().collect::<Vec<_>>();
main_routes.sort_by(|a, b| a.prefix.prefix().cmp(&b.prefix.prefix()));
+ main_routes.sort_by(|a, b| a.prefix.is_ipv4().cmp(&b.prefix.is_ipv4()));
for mut route in main_routes {
route.table_id = self.split_table_id;
- self.add_route_direct(route).await?;
+ if let Err(err) = self.add_route_direct(route.clone()).await {
+ // If a rotue can't be added because parts of it's next-hop are unreachable, then
+ // a gateway route should be added first. Seemingly there's an ARP table per
+ // routing table, and a route that specifies both a gateway and an output interface
+ // depends on a route that instructs how to reach the gateway.
+ if err.is_network_unreachable() {
+ match (route.device_only_route(), route.node.get_address()) {
+ (Some(mut gateway_route), Some(address)) => {
+ gateway_route.prefix =
+ IpNetwork::new(address, if address.is_ipv4() { 32 } else { 128 })
+ .unwrap();
+ let add_gateway_route = self.add_route_direct(gateway_route).await;
+ let add_route_result = self.add_route_direct(route).await;
+ if let Err(err) = add_gateway_route.and_then(|_| add_route_result) {
+ log::error!("Failed to add route to split-routing table: {}", err);
+ };
+ continue;
+ }
+ _ => (),
+ };
+ }
+ log::error!("Failed to add route to split-routing table: {}", err);
+ }
}
Ok(())
}
@@ -869,12 +904,13 @@ impl RouteManagerImpl {
device: device.map(|dev| dev.name.clone()),
};
- Ok(Some(Route {
+ let result = Ok(Some(Route {
node,
prefix,
metric,
table_id,
- }))
+ }));
+ result
}
fn map_interface(msg: LinkMessage) -> Option<(u32, NetworkInterface)> {
diff --git a/talpid-core/src/routing/mod.rs b/talpid-core/src/routing/mod.rs
index 15bf1b04ae..6ab4240fd1 100644
--- a/talpid-core/src/routing/mod.rs
+++ b/talpid-core/src/routing/mod.rs
@@ -40,6 +40,19 @@ impl Route {
}
}
+ /// Returns route that only contains the device node if a device node exists.
+ #[cfg(target_os = "linux")]
+ fn device_only_route(&self) -> Option<Self> {
+ if let Some(device) = self.node.get_device() {
+ Some(Self {
+ node: Node::device(device.to_string()),
+ ..self.clone()
+ })
+ } else {
+ None
+ }
+ }
+
#[cfg(target_os = "linux")]
fn table(mut self, new_id: u32) -> Self {
self.table_id = new_id;