summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2018-11-21 11:15:00 +0000
committerEmīls Piņķis <emils@mullvad.net>2018-11-22 14:45:46 +0000
commit9c9fab7212275833b0fcbe49b8d086acdb072a54 (patch)
tree85ea6193b6f452e093fe48edaa20f0ceac5b6b60
parent0ba8f28909dd0ee4d5ccd076c8e2c053f33850d4 (diff)
downloadmullvadvpn-9c9fab7212275833b0fcbe49b8d086acdb072a54.tar.xz
mullvadvpn-9c9fab7212275833b0fcbe49b8d086acdb072a54.zip
Add MacOS routing implementation
-rw-r--r--talpid-core/src/routing/macos.rs125
-rw-r--r--talpid-core/src/routing/mod.rs4
2 files changed, 129 insertions, 0 deletions
diff --git a/talpid-core/src/routing/macos.rs b/talpid-core/src/routing/macos.rs
new file mode 100644
index 0000000000..43081b21bb
--- /dev/null
+++ b/talpid-core/src/routing/macos.rs
@@ -0,0 +1,125 @@
+use super::{NetNode, RequiredRoutes, Route};
+
+use super::subprocess::{Exec, RunExpr};
+use std::collections::HashSet;
+use std::net::IpAddr;
+
+error_chain! {
+ errors {
+ FailedToAddRoute {
+ description("Failed to add route")
+ }
+
+ FailedToGetDefaultRoute {
+ description("Failed to get default route")
+ }
+
+ FailedToRemoveRoute {
+ description("Failed to remove route")
+ }
+ }
+}
+
+pub struct RouteManager {
+ set_routes: HashSet<Route>,
+}
+
+impl RouteManager {
+ fn add_route(&mut self, route: Route) -> Result<()> {
+ if route.prefix.prefix() == 0 {
+ if route.prefix.is_ipv4() {
+ self.add_route(Route::new("0.0.0.0/1".parse().unwrap(), route.node.clone()))?;
+ self.add_route(Route::new(
+ "128.0.0.0/1".parse().unwrap(),
+ route.node.clone(),
+ ))?;
+ } else {
+ self.add_route(Route::new("::/1".parse().unwrap(), route.node.clone()))?;
+ self.add_route(Route::new("8000::/1".parse().unwrap(), route.node.clone()))?;
+ }
+ };
+
+ let mut cmd = Exec::cmd("route")
+ .arg("-q")
+ .arg("-n")
+ .arg("add")
+ .arg(ip_vers(&route))
+ .arg(route.prefix.to_string());
+ cmd = match &route.node {
+ NetNode::Address(ref addr) => cmd.arg("-gateway").arg(addr.to_string()),
+ NetNode::Device(device) => cmd.arg("-interface").arg(&device),
+ };
+
+ cmd.to_expr()
+ .run_expr()
+ .chain_err(|| ErrorKind::FailedToAddRoute)?;
+ self.set_routes.insert(route);
+ Ok(())
+ }
+}
+
+fn ip_vers(route: &Route) -> &'static str {
+ if route.prefix.is_ipv4() {
+ "-inet"
+ } else {
+ "-inet6"
+ }
+}
+
+impl super::RoutingT for RouteManager {
+ type Error = Error;
+
+ fn new() -> Result<Self> {
+ Ok(Self {
+ set_routes: HashSet::new(),
+ })
+ }
+
+ fn add_routes(&mut self, required_routes: RequiredRoutes) -> Result<()> {
+ for route in required_routes.routes.into_iter() {
+ if let Err(e) = self.add_route(route) {
+ let _ = self.delete_routes();
+ return Err(e);
+ }
+ }
+ Ok(())
+ }
+
+ fn delete_routes(&mut self) -> Result<()> {
+ let mut end_result = Ok(());
+ for route in self.set_routes.drain() {
+ let result = duct::cmd!(
+ "route",
+ "-q",
+ "-n",
+ "delete",
+ ip_vers(&route),
+ route.prefix.to_string()
+ )
+ .run_expr()
+ .chain_err(|| ErrorKind::FailedToRemoveRoute);
+ if let Err(e) = result {
+ log::error!("failed to reset remove route: {}", e);
+ end_result = Err(e);
+ }
+ }
+ // returning the last error as to signal some kind of failure.
+ end_result
+ }
+
+
+ fn get_default_route_node(&mut self) -> Result<IpAddr> {
+ let output = duct::cmd!("route", "-n", "get", "default")
+ .stdout()
+ .chain_err(|| ErrorKind::FailedToGetDefaultRoute)?;
+ let ip_str: &str = output
+ .lines()
+ .find(|line| line.trim().starts_with("gateway: "))
+ .and_then(|line| line.trim().split_whitespace().skip(1).next())
+ .ok_or(Error::from(ErrorKind::FailedToGetDefaultRoute))?;
+
+ ip_str
+ .parse()
+ .map_err(|_| Error::from(ErrorKind::FailedToGetDefaultRoute))
+ }
+}
diff --git a/talpid-core/src/routing/mod.rs b/talpid-core/src/routing/mod.rs
index b2a4d379ad..5a8fb34e64 100644
--- a/talpid-core/src/routing/mod.rs
+++ b/talpid-core/src/routing/mod.rs
@@ -1,6 +1,10 @@
use ipnetwork::IpNetwork;
use std::net::IpAddr;
+#[cfg(target_os = "macos")]
+#[path = "macos.rs"]
+mod imp;
+
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod imp;