|
|
|
@ -8,7 +8,7 @@ use std::{ |
|
|
|
ffi::OsStr, |
|
|
|
fs, |
|
|
|
io::BufRead, |
|
|
|
net::{IpAddr, Ipv4Addr, Ipv6Addr}, |
|
|
|
net::{Ipv4Addr, Ipv6Addr}, |
|
|
|
os::unix::io::RawFd, |
|
|
|
process::{Command, Output}, |
|
|
|
str::FromStr, |
|
|
|
@ -17,11 +17,10 @@ use std::{ |
|
|
|
#[derive(Clone)] |
|
|
|
pub struct Setup { |
|
|
|
routes: Vec<IpCidr>, |
|
|
|
tunnel_bypass_addr: IpAddr, |
|
|
|
allow_private: bool, |
|
|
|
tunnel_bypass_addrs: Vec<IpCidr>, |
|
|
|
tun: String, |
|
|
|
set_up: bool, |
|
|
|
delete_proxy_route: bool, |
|
|
|
delete_proxy_routes: Vec<IpCidr>, |
|
|
|
child: libc::pid_t, |
|
|
|
unmount_resolvconf: bool, |
|
|
|
restore_resolvconf_data: Option<Vec<u8>>, |
|
|
|
@ -76,35 +75,41 @@ where |
|
|
|
impl Setup { |
|
|
|
pub fn new( |
|
|
|
tun: impl Into<String>, |
|
|
|
tunnel_bypass_addr: &IpAddr, |
|
|
|
tunnel_bypass_addrs: impl IntoIterator<Item = IpCidr>, |
|
|
|
routes: impl IntoIterator<Item = IpCidr>, |
|
|
|
allow_private: bool, |
|
|
|
) -> Self { |
|
|
|
let routes_cidr = routes.into_iter().collect(); |
|
|
|
let bypass_cidrs = tunnel_bypass_addrs.into_iter().collect(); |
|
|
|
Self { |
|
|
|
tun: tun.into(), |
|
|
|
tunnel_bypass_addr: *tunnel_bypass_addr, |
|
|
|
allow_private, |
|
|
|
tunnel_bypass_addrs: bypass_cidrs, |
|
|
|
routes: routes_cidr, |
|
|
|
set_up: false, |
|
|
|
delete_proxy_route: false, |
|
|
|
delete_proxy_routes: Vec::<IpCidr>::new(), |
|
|
|
child: 0, |
|
|
|
unmount_resolvconf: false, |
|
|
|
restore_resolvconf_data: None, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn route_proxy_address(&mut self) -> Result<bool, Error> { |
|
|
|
let route_show_args = if self.tunnel_bypass_addr.is_ipv6() { |
|
|
|
fn bypass_cidr(cidr: &IpCidr) -> Result<bool, Error> { |
|
|
|
let is_ipv6 = match cidr { |
|
|
|
IpCidr::Ipv4(_) => false, |
|
|
|
IpCidr::Ipv6(_) => true, |
|
|
|
}; |
|
|
|
let route_show_args = if is_ipv6 { |
|
|
|
["ip", "-6", "route", "show"] |
|
|
|
} else { |
|
|
|
["ip", "-4", "route", "show"] |
|
|
|
}; |
|
|
|
|
|
|
|
let routes = run_iproute(route_show_args, "failed to get routing table", true)?; |
|
|
|
let routes = run_iproute( |
|
|
|
route_show_args, |
|
|
|
"failed to get routing table through the ip command", |
|
|
|
true, |
|
|
|
)?; |
|
|
|
|
|
|
|
let mut route_info = Vec::<(IpCidr, Vec<String>)>::new(); |
|
|
|
|
|
|
|
for line in routes.stdout.lines() { |
|
|
|
if line.is_err() { |
|
|
|
break; |
|
|
|
@ -117,15 +122,11 @@ impl Setup { |
|
|
|
let mut split = line.split_whitespace(); |
|
|
|
let mut dst_str = split.next().unwrap(); |
|
|
|
if dst_str == "default" { |
|
|
|
dst_str = if self.tunnel_bypass_addr.is_ipv6() { |
|
|
|
"::/0" |
|
|
|
} else { |
|
|
|
"0.0.0.0/0" |
|
|
|
} |
|
|
|
dst_str = if is_ipv6 { "::/0" } else { "0.0.0.0/0" } |
|
|
|
} |
|
|
|
|
|
|
|
let (addr_str, prefix_len_str) = match dst_str.split_once(['/']) { |
|
|
|
None => (dst_str, if self.tunnel_bypass_addr.is_ipv6() { "128" } else { "32" }), |
|
|
|
None => (dst_str, if is_ipv6 { "128" } else { "32" }), |
|
|
|
Some((addr_str, prefix_len_str)) => (addr_str, prefix_len_str), |
|
|
|
}; |
|
|
|
|
|
|
|
@ -140,19 +141,19 @@ impl Setup { |
|
|
|
// Sort routes by prefix length, the most specific route comes first.
|
|
|
|
route_info.sort_by(|entry1, entry2| entry2.0.prefix_len().cmp(&entry1.0.prefix_len())); |
|
|
|
|
|
|
|
for (cidr, route_components) in route_info { |
|
|
|
if !cidr.contains_addr(&smoltcp::wire::IpAddress::from(self.tunnel_bypass_addr)) { |
|
|
|
for (route_cidr, route_components) in route_info { |
|
|
|
if !route_cidr.contains_subnet(cidr) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// The IP address is routed through a more specific route than the default route.
|
|
|
|
// In this case, there is nothing to do.
|
|
|
|
if cidr.prefix_len() != 0 { |
|
|
|
if route_cidr.prefix_len() != 0 { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
let mut proxy_route = vec!["ip".into(), "route".into(), "add".into()]; |
|
|
|
proxy_route.push(self.tunnel_bypass_addr.to_string()); |
|
|
|
proxy_route.push(cidr.to_string()); |
|
|
|
proxy_route.extend(route_components.into_iter()); |
|
|
|
run_iproute(proxy_route, "failed to clone route for proxy", false)?; |
|
|
|
return Ok(true); |
|
|
|
@ -235,14 +236,17 @@ impl Setup { |
|
|
|
self.set_up = false; |
|
|
|
log::info!("[{}] Restoring network configuration", nix::unistd::getpid()); |
|
|
|
let _ = Command::new("ip").args(["link", "del", self.tun.as_str()]).output(); |
|
|
|
if self.delete_proxy_route { |
|
|
|
|
|
|
|
for cidr in &self.delete_proxy_routes { |
|
|
|
let _ = Command::new("ip") |
|
|
|
.args(["route", "del", self.tunnel_bypass_addr.to_string().as_str()]) |
|
|
|
.args(["route", "del", cidr.to_string().as_str()]) |
|
|
|
.output(); |
|
|
|
} |
|
|
|
|
|
|
|
if self.unmount_resolvconf { |
|
|
|
nix::mount::umount("/etc/resolv.conf")?; |
|
|
|
} |
|
|
|
|
|
|
|
if let Some(data) = &self.restore_resolvconf_data { |
|
|
|
fs::write("/etc/resolv.conf", data)?; |
|
|
|
} |
|
|
|
@ -259,8 +263,6 @@ impl Setup { |
|
|
|
)?; |
|
|
|
|
|
|
|
self.set_up = true; |
|
|
|
let _tun_name = self.tun.clone(); |
|
|
|
let _proxy_ip = self.tunnel_bypass_addr; |
|
|
|
|
|
|
|
run_iproute( |
|
|
|
["ip", "link", "set", self.tun.as_str(), "up"], |
|
|
|
@ -268,8 +270,13 @@ impl Setup { |
|
|
|
true, |
|
|
|
)?; |
|
|
|
|
|
|
|
let delete_proxy_route = self.route_proxy_address()?; |
|
|
|
self.delete_proxy_route = delete_proxy_route; |
|
|
|
let mut delete_proxy_route = Vec::<IpCidr>::new(); |
|
|
|
for cidr in &self.tunnel_bypass_addrs { |
|
|
|
if Self::bypass_cidr(cidr)? { |
|
|
|
delete_proxy_route.push(*cidr); |
|
|
|
} |
|
|
|
} |
|
|
|
self.delete_proxy_routes = delete_proxy_route; |
|
|
|
self.setup_resolv_conf()?; |
|
|
|
self.add_tunnel_routes()?; |
|
|
|
|
|
|
|
@ -321,14 +328,6 @@ impl Setup { |
|
|
|
return Err("Automatic setup requires root privileges".into()); |
|
|
|
} |
|
|
|
|
|
|
|
if self.tunnel_bypass_addr.is_loopback() && !self.allow_private { |
|
|
|
log::warn!( |
|
|
|
"The proxy address {} is a loopback address. You may need to manually \ |
|
|
|
provide --bypass-ip to specify the server IP bypassing the tunnel", |
|
|
|
self.tunnel_bypass_addr |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
let (read_from_child, write_to_parent) = nix::unistd::pipe()?; |
|
|
|
match fork::fork() { |
|
|
|
Ok(Fork::Child) => { |
|
|
|
|