mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-04-21 14:29:10 +00:00
Allow multiple bypass IP addresses/CIDRs in routing setup
See issue #73.
This commit is contained in:
parent
9b27dd2df2
commit
e08a0f683d
10 changed files with 112 additions and 123 deletions
13
Dockerfile
13
Dockerfile
|
@ -12,18 +12,9 @@ RUN cargo build --release --target x86_64-unknown-linux-gnu
|
||||||
## Final image
|
## Final image
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM ubuntu:latest
|
FROM ubuntu:latest
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV TUN=tun0
|
RUN apt update && apt install -y iproute2 && apt clean all
|
||||||
ENV PROXY=
|
|
||||||
ENV DNS=virtual
|
|
||||||
ENV MODE=auto
|
|
||||||
ENV BYPASS_IP=
|
|
||||||
ENV VERBOSITY=info
|
|
||||||
|
|
||||||
RUN apt update && apt install -y iproute2 curl && apt clean all
|
|
||||||
|
|
||||||
COPY --from=builder /worker/target/x86_64-unknown-linux-gnu/release/tun2proxy /usr/bin/tun2proxy
|
COPY --from=builder /worker/target/x86_64-unknown-linux-gnu/release/tun2proxy /usr/bin/tun2proxy
|
||||||
COPY --from=builder /worker/docker/entrypoint.sh /app
|
|
||||||
|
|
||||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
ENTRYPOINT ["/usr/bin/tun2proxy", "--setup", "auto"]
|
||||||
|
|
18
README.md
18
README.md
|
@ -99,7 +99,7 @@ Options:
|
||||||
--dns-addr <IP> DNS resolver address [default: 8.8.8.8]
|
--dns-addr <IP> DNS resolver address [default: 8.8.8.8]
|
||||||
-6, --ipv6-enabled IPv6 enabled
|
-6, --ipv6-enabled IPv6 enabled
|
||||||
-s, --setup <method> Routing and system setup [default: none] [possible values: none, auto]
|
-s, --setup <method> Routing and system setup [default: none] [possible values: none, auto]
|
||||||
-b, --bypass <IP> Public proxy IP used in routing setup which should bypassing the tunnel
|
-b, --bypass <IP|CIDR> IPs and CIDRs used in routing setup which should bypass the tunnel
|
||||||
-v, --verbosity <level> Verbosity level [default: info] [possible values: off, error, warn, info, debug, trace]
|
-v, --verbosity <level> Verbosity level [default: info] [possible values: off, error, warn, info, debug, trace]
|
||||||
-h, --help Print help
|
-h, --help Print help
|
||||||
-V, --version Print version
|
-V, --version Print version
|
||||||
|
@ -119,31 +119,17 @@ Next, start a container from the tun2proxy image:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d \
|
docker run -d \
|
||||||
-e PROXY=proto://[username[:password]@]host:port \
|
|
||||||
-v /dev/net/tun:/dev/net/tun \
|
-v /dev/net/tun:/dev/net/tun \
|
||||||
--sysctl net.ipv6.conf.default.disable_ipv6=0 \
|
--sysctl net.ipv6.conf.default.disable_ipv6=0 \
|
||||||
--cap-add NET_ADMIN \
|
--cap-add NET_ADMIN \
|
||||||
--name tun2proxy \
|
--name tun2proxy \
|
||||||
tun2proxy
|
tun2proxy --proxy proto://[username[:password]@]host:port
|
||||||
```
|
```
|
||||||
|
|
||||||
container env list
|
|
||||||
|
|
||||||
| container env | Default | program option | mean |
|
|
||||||
| ------------- | ------- | ----------------------- | ------------------------------------------------------------ |
|
|
||||||
| TUN | tun0 | -t, --tun <name> | Name of the tun interface [default: tun0] |
|
|
||||||
| PROXY | None | -p, --proxy <URL> | Proxy URL in the form proto://[username[:password]@]host:port |
|
|
||||||
| DNS | virtual | -d, --dns <strategy> | DNS handling strategy [default: virtual] [possible values: virtual, over-tcp, direct] |
|
|
||||||
| MODE | auto | -s, --setup <method> | Routing and system setup [default: none] [possible values: none, auto] |
|
|
||||||
| BYPASS_IP | None | -b, --bypass <IP> | Public proxy IP used in routing setup which should bypassing the tunnel |
|
|
||||||
| VERBOSITY | info | -v, --verbosity <level> | Verbosity level [default: info] [possible values: off, error, warn, info, debug, trace] |
|
|
||||||
| | | | |
|
|
||||||
|
|
||||||
You can then provide the running container's network to another worker container by sharing the network namespace (like kubernetes sidecar):
|
You can then provide the running container's network to another worker container by sharing the network namespace (like kubernetes sidecar):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it \
|
docker run -it \
|
||||||
-d \
|
|
||||||
--network "container:tun2proxy" \
|
--network "container:tun2proxy" \
|
||||||
ubuntu:latest
|
ubuntu:latest
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
|
|
||||||
run() {
|
|
||||||
if [ -n "$TUN" ]; then
|
|
||||||
TUN="--tun $TUN"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$PROXY" ]; then
|
|
||||||
PROXY="--proxy $PROXY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$DNS" ]; then
|
|
||||||
DNS="--dns $DNS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$BYPASS_IP" ]; then
|
|
||||||
BYPASS_IP="--bypass $BYPASS_IP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$VERBOSITY" ]; then
|
|
||||||
VERBOSITY="-v $VERBOSITY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$MODE" ]; then
|
|
||||||
MODE="--setup $MODE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Bootstrap ready!! Exec Command: tun2proxy $TUN $PROXY $DNS $VERBOSITY $MODE $BYPASS_IP $@"
|
|
||||||
|
|
||||||
exec tun2proxy $TUN $PROXY $DNS $VERBOSITY $MODE $BYPASS_IP $@
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
run $@ || echo "Runing ERROR!!"
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
socks::SocksProxyManager,
|
socks::SocksProxyManager,
|
||||||
tun2proxy::{ConnectionManager, TunToProxy},
|
tun2proxy::{ConnectionManager, TunToProxy},
|
||||||
};
|
};
|
||||||
|
use smoltcp::wire::IpCidr;
|
||||||
use socks5_impl::protocol::UserKey;
|
use socks5_impl::protocol::UserKey;
|
||||||
use std::{
|
use std::{
|
||||||
net::{SocketAddr, ToSocketAddrs},
|
net::{SocketAddr, ToSocketAddrs},
|
||||||
|
@ -17,6 +18,7 @@ mod http;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
mod socks;
|
mod socks;
|
||||||
mod tun2proxy;
|
mod tun2proxy;
|
||||||
|
pub mod util;
|
||||||
mod virtdevice;
|
mod virtdevice;
|
||||||
mod virtdns;
|
mod virtdns;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
@ -104,8 +106,8 @@ pub struct Options {
|
||||||
dns_over_tcp: bool,
|
dns_over_tcp: bool,
|
||||||
dns_addr: Option<std::net::IpAddr>,
|
dns_addr: Option<std::net::IpAddr>,
|
||||||
ipv6_enabled: bool,
|
ipv6_enabled: bool,
|
||||||
bypass: Option<std::net::IpAddr>,
|
|
||||||
pub setup: bool,
|
pub setup: bool,
|
||||||
|
bypass: Vec<IpCidr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
|
@ -140,8 +142,10 @@ impl Options {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_bypass(mut self, ip: Option<std::net::IpAddr>) -> Self {
|
pub fn with_bypass_ips<'a>(mut self, bypass_ips: impl IntoIterator<Item = &'a IpCidr>) -> Self {
|
||||||
self.bypass = ip;
|
for bypass_ip in bypass_ips {
|
||||||
|
self.bypass.push(*bypass_ip);
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
src/main.rs
38
src/main.rs
|
@ -1,5 +1,7 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use smoltcp::wire::IpCidr;
|
||||||
use std::{net::IpAddr, process::ExitCode};
|
use std::{net::IpAddr, process::ExitCode};
|
||||||
|
use tun2proxy::util::str_to_cidr;
|
||||||
use tun2proxy::{error::Error, main_entry, NetworkInterface, Options, Proxy};
|
use tun2proxy::{error::Error, main_entry, NetworkInterface, Options, Proxy};
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -41,9 +43,9 @@ struct Args {
|
||||||
#[arg(short, long, value_name = "method", value_enum, default_value = if cfg!(target_os = "linux") { "none" } else { "auto" })]
|
#[arg(short, long, value_name = "method", value_enum, default_value = if cfg!(target_os = "linux") { "none" } else { "auto" })]
|
||||||
setup: Option<ArgSetup>,
|
setup: Option<ArgSetup>,
|
||||||
|
|
||||||
/// Public proxy IP used in routing setup which should bypassing the tunnel
|
/// IPs used in routing setup which should bypass the tunnel
|
||||||
#[arg(short, long, value_name = "IP")]
|
#[arg(short, long, value_name = "IP|CIDR")]
|
||||||
bypass: Option<IpAddr>,
|
bypass: Vec<String>,
|
||||||
|
|
||||||
/// Verbosity level
|
/// Verbosity level
|
||||||
#[arg(short, long, value_name = "level", value_enum, default_value = "info")]
|
#[arg(short, long, value_name = "level", value_enum, default_value = "info")]
|
||||||
|
@ -53,7 +55,7 @@ struct Args {
|
||||||
/// DNS query handling strategy
|
/// DNS query handling strategy
|
||||||
/// - Virtual: Intercept DNS queries and resolve them locally with a fake IP address
|
/// - Virtual: Intercept DNS queries and resolve them locally with a fake IP address
|
||||||
/// - OverTcp: Use TCP to send DNS queries to the DNS server
|
/// - OverTcp: Use TCP to send DNS queries to the DNS server
|
||||||
/// - Direct: Looks as general UDP traffic but change the destination to the DNS server
|
/// - Direct: Do not handle DNS by relying on DNS server bypassing
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||||
enum ArgDns {
|
enum ArgDns {
|
||||||
Virtual,
|
Virtual,
|
||||||
|
@ -117,20 +119,28 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let bypass_tun_ip = match args.bypass {
|
|
||||||
Some(addr) => addr,
|
|
||||||
None => args.proxy.addr.ip(),
|
|
||||||
};
|
|
||||||
options = options.with_bypass(Some(bypass_tun_ip));
|
|
||||||
|
|
||||||
options.setup = args.setup.map(|s| s == ArgSetup::Auto).unwrap_or(false);
|
options.setup = args.setup.map(|s| s == ArgSetup::Auto).unwrap_or(false);
|
||||||
|
|
||||||
let block = || -> Result<(), Error> {
|
let block = || -> Result<(), Error> {
|
||||||
|
let mut bypass_ips = Vec::<IpCidr>::new();
|
||||||
|
for cidr_str in args.bypass {
|
||||||
|
bypass_ips.push(str_to_cidr(&cidr_str)?);
|
||||||
|
}
|
||||||
|
if bypass_ips.is_empty() {
|
||||||
|
let prefix_len = if args.proxy.addr.ip().is_ipv6() { 128 } else { 32 };
|
||||||
|
bypass_ips.push(IpCidr::new(args.proxy.addr.ip().into(), prefix_len))
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options.with_bypass_ips(&bypass_ips);
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
if options.setup {
|
{
|
||||||
let mut setup = Setup::new(&args.tun, &bypass_tun_ip, get_default_cidrs(), args.bypass.is_some());
|
let mut setup: Setup;
|
||||||
setup.configure()?;
|
if options.setup {
|
||||||
setup.drop_privileges()?;
|
setup = Setup::new(&args.tun, bypass_ips, get_default_cidrs());
|
||||||
|
setup.configure()?;
|
||||||
|
setup.drop_privileges()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main_entry(&interface, &args.proxy, options)?;
|
main_entry(&interface, &args.proxy, options)?;
|
||||||
|
|
73
src/setup.rs
73
src/setup.rs
|
@ -8,7 +8,7 @@ use std::{
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
fs,
|
fs,
|
||||||
io::BufRead,
|
io::BufRead,
|
||||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
os::unix::io::RawFd,
|
os::unix::io::RawFd,
|
||||||
process::{Command, Output},
|
process::{Command, Output},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
@ -17,11 +17,10 @@ use std::{
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Setup {
|
pub struct Setup {
|
||||||
routes: Vec<IpCidr>,
|
routes: Vec<IpCidr>,
|
||||||
tunnel_bypass_addr: IpAddr,
|
tunnel_bypass_addrs: Vec<IpCidr>,
|
||||||
allow_private: bool,
|
|
||||||
tun: String,
|
tun: String,
|
||||||
set_up: bool,
|
set_up: bool,
|
||||||
delete_proxy_route: bool,
|
delete_proxy_routes: Vec<IpCidr>,
|
||||||
child: libc::pid_t,
|
child: libc::pid_t,
|
||||||
unmount_resolvconf: bool,
|
unmount_resolvconf: bool,
|
||||||
restore_resolvconf_data: Option<Vec<u8>>,
|
restore_resolvconf_data: Option<Vec<u8>>,
|
||||||
|
@ -76,35 +75,41 @@ where
|
||||||
impl Setup {
|
impl Setup {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tun: impl Into<String>,
|
tun: impl Into<String>,
|
||||||
tunnel_bypass_addr: &IpAddr,
|
tunnel_bypass_addrs: impl IntoIterator<Item = IpCidr>,
|
||||||
routes: impl IntoIterator<Item = IpCidr>,
|
routes: impl IntoIterator<Item = IpCidr>,
|
||||||
allow_private: bool,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let routes_cidr = routes.into_iter().collect();
|
let routes_cidr = routes.into_iter().collect();
|
||||||
|
let bypass_cidrs = tunnel_bypass_addrs.into_iter().collect();
|
||||||
Self {
|
Self {
|
||||||
tun: tun.into(),
|
tun: tun.into(),
|
||||||
tunnel_bypass_addr: *tunnel_bypass_addr,
|
tunnel_bypass_addrs: bypass_cidrs,
|
||||||
allow_private,
|
|
||||||
routes: routes_cidr,
|
routes: routes_cidr,
|
||||||
set_up: false,
|
set_up: false,
|
||||||
delete_proxy_route: false,
|
delete_proxy_routes: Vec::<IpCidr>::new(),
|
||||||
child: 0,
|
child: 0,
|
||||||
unmount_resolvconf: false,
|
unmount_resolvconf: false,
|
||||||
restore_resolvconf_data: None,
|
restore_resolvconf_data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn route_proxy_address(&mut self) -> Result<bool, Error> {
|
fn bypass_cidr(cidr: &IpCidr) -> Result<bool, Error> {
|
||||||
let route_show_args = if self.tunnel_bypass_addr.is_ipv6() {
|
let is_ipv6 = match cidr {
|
||||||
|
IpCidr::Ipv4(_) => false,
|
||||||
|
IpCidr::Ipv6(_) => true,
|
||||||
|
};
|
||||||
|
let route_show_args = if is_ipv6 {
|
||||||
["ip", "-6", "route", "show"]
|
["ip", "-6", "route", "show"]
|
||||||
} else {
|
} else {
|
||||||
["ip", "-4", "route", "show"]
|
["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();
|
let mut route_info = Vec::<(IpCidr, Vec<String>)>::new();
|
||||||
|
|
||||||
for line in routes.stdout.lines() {
|
for line in routes.stdout.lines() {
|
||||||
if line.is_err() {
|
if line.is_err() {
|
||||||
break;
|
break;
|
||||||
|
@ -117,15 +122,11 @@ impl Setup {
|
||||||
let mut split = line.split_whitespace();
|
let mut split = line.split_whitespace();
|
||||||
let mut dst_str = split.next().unwrap();
|
let mut dst_str = split.next().unwrap();
|
||||||
if dst_str == "default" {
|
if dst_str == "default" {
|
||||||
dst_str = if self.tunnel_bypass_addr.is_ipv6() {
|
dst_str = if is_ipv6 { "::/0" } else { "0.0.0.0/0" }
|
||||||
"::/0"
|
|
||||||
} else {
|
|
||||||
"0.0.0.0/0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (addr_str, prefix_len_str) = match dst_str.split_once(['/']) {
|
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),
|
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.
|
// 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()));
|
route_info.sort_by(|entry1, entry2| entry2.0.prefix_len().cmp(&entry1.0.prefix_len()));
|
||||||
|
|
||||||
for (cidr, route_components) in route_info {
|
for (route_cidr, route_components) in route_info {
|
||||||
if !cidr.contains_addr(&smoltcp::wire::IpAddress::from(self.tunnel_bypass_addr)) {
|
if !route_cidr.contains_subnet(cidr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The IP address is routed through a more specific route than the default route.
|
// The IP address is routed through a more specific route than the default route.
|
||||||
// In this case, there is nothing to do.
|
// In this case, there is nothing to do.
|
||||||
if cidr.prefix_len() != 0 {
|
if route_cidr.prefix_len() != 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut proxy_route = vec!["ip".into(), "route".into(), "add".into()];
|
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());
|
proxy_route.extend(route_components.into_iter());
|
||||||
run_iproute(proxy_route, "failed to clone route for proxy", false)?;
|
run_iproute(proxy_route, "failed to clone route for proxy", false)?;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
@ -235,14 +236,17 @@ impl Setup {
|
||||||
self.set_up = false;
|
self.set_up = false;
|
||||||
log::info!("[{}] Restoring network configuration", nix::unistd::getpid());
|
log::info!("[{}] Restoring network configuration", nix::unistd::getpid());
|
||||||
let _ = Command::new("ip").args(["link", "del", self.tun.as_str()]).output();
|
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")
|
let _ = Command::new("ip")
|
||||||
.args(["route", "del", self.tunnel_bypass_addr.to_string().as_str()])
|
.args(["route", "del", cidr.to_string().as_str()])
|
||||||
.output();
|
.output();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.unmount_resolvconf {
|
if self.unmount_resolvconf {
|
||||||
nix::mount::umount("/etc/resolv.conf")?;
|
nix::mount::umount("/etc/resolv.conf")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(data) = &self.restore_resolvconf_data {
|
if let Some(data) = &self.restore_resolvconf_data {
|
||||||
fs::write("/etc/resolv.conf", data)?;
|
fs::write("/etc/resolv.conf", data)?;
|
||||||
}
|
}
|
||||||
|
@ -259,8 +263,6 @@ impl Setup {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.set_up = true;
|
self.set_up = true;
|
||||||
let _tun_name = self.tun.clone();
|
|
||||||
let _proxy_ip = self.tunnel_bypass_addr;
|
|
||||||
|
|
||||||
run_iproute(
|
run_iproute(
|
||||||
["ip", "link", "set", self.tun.as_str(), "up"],
|
["ip", "link", "set", self.tun.as_str(), "up"],
|
||||||
|
@ -268,8 +270,13 @@ impl Setup {
|
||||||
true,
|
true,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let delete_proxy_route = self.route_proxy_address()?;
|
let mut delete_proxy_route = Vec::<IpCidr>::new();
|
||||||
self.delete_proxy_route = delete_proxy_route;
|
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.setup_resolv_conf()?;
|
||||||
self.add_tunnel_routes()?;
|
self.add_tunnel_routes()?;
|
||||||
|
|
||||||
|
@ -321,14 +328,6 @@ impl Setup {
|
||||||
return Err("Automatic setup requires root privileges".into());
|
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()?;
|
let (read_from_child, write_to_parent) = nix::unistd::pipe()?;
|
||||||
match fork::fork() {
|
match fork::fork() {
|
||||||
Ok(Fork::Child) => {
|
Ok(Fork::Child) => {
|
||||||
|
|
|
@ -259,7 +259,7 @@ impl<'a> TunToProxy<'a> {
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
if options.setup {
|
if options.setup {
|
||||||
tun.setup_config(options.bypass, options.dns_addr)?;
|
tun.setup_config(&options.bypass, options.dns_addr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let poll = Poll::new()?;
|
let poll = Poll::new()?;
|
||||||
|
|
22
src/util.rs
Normal file
22
src/util.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::error::Error;
|
||||||
|
use smoltcp::wire::IpCidr;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub fn str_to_cidr(s: &str) -> Result<IpCidr, Error> {
|
||||||
|
// IpCidr's FromString implementation requires the netmask to be specified.
|
||||||
|
// Try to parse as IP address without netmask before falling back.
|
||||||
|
match IpAddr::from_str(s) {
|
||||||
|
Err(_) => (),
|
||||||
|
Ok(cidr) => {
|
||||||
|
let prefix_len = if cidr.is_ipv4() { 32 } else { 128 };
|
||||||
|
return Ok(IpCidr::new(cidr.into(), prefix_len));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let cidr = IpCidr::from_str(s);
|
||||||
|
match cidr {
|
||||||
|
Err(()) => Err("Invalid CIDR: ".into()),
|
||||||
|
Ok(cidr) => Ok(cidr),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use mio::{event, windows::NamedPipe, Interest, Registry, Token};
|
use mio::{event, windows::NamedPipe, Interest, Registry, Token};
|
||||||
|
use smoltcp::wire::IpCidr;
|
||||||
use smoltcp::{
|
use smoltcp::{
|
||||||
phy::{self, Device, DeviceCapabilities, Medium},
|
phy::{self, Device, DeviceCapabilities, Medium},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
|
@ -225,7 +226,11 @@ impl WinTunInterface {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_config(&mut self, bypass_ip: Option<IpAddr>, dns_addr: Option<IpAddr>) -> Result<(), io::Error> {
|
pub fn setup_config<'a>(
|
||||||
|
&mut self,
|
||||||
|
bypass_ips: impl IntoIterator<Item = &'a IpCidr>,
|
||||||
|
dns_addr: Option<IpAddr>,
|
||||||
|
) -> Result<(), io::Error> {
|
||||||
let adapter = self.wintun_session.get_adapter();
|
let adapter = self.wintun_session.get_adapter();
|
||||||
|
|
||||||
// Setup the adapter's address/mask/gateway
|
// Setup the adapter's address/mask/gateway
|
||||||
|
@ -261,7 +266,7 @@ impl WinTunInterface {
|
||||||
|
|
||||||
// 3. route the bypass ip to the old gateway
|
// 3. route the bypass ip to the old gateway
|
||||||
// command: `route add bypass_ip old_gateway metric 1`
|
// command: `route add bypass_ip old_gateway metric 1`
|
||||||
if let Some(bypass_ip) = bypass_ip {
|
for bypass_ip in bypass_ips {
|
||||||
let args = &["add", &bypass_ip.to_string(), &old_gateway.to_string(), "metric", "1"];
|
let args = &["add", &bypass_ip.to_string(), &old_gateway.to_string(), "metric", "1"];
|
||||||
run_command("route", args)?;
|
run_command("route", args)?;
|
||||||
log::info!("route {:?}", args);
|
log::info!("route {:?}", args);
|
||||||
|
|
|
@ -11,8 +11,10 @@ mod tests {
|
||||||
use nix::sys::signal;
|
use nix::sys::signal;
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
use smoltcp::wire::IpCidr;
|
||||||
|
|
||||||
use tun2proxy::setup::{get_default_cidrs, Setup};
|
use tun2proxy::setup::{get_default_cidrs, Setup};
|
||||||
|
use tun2proxy::util::str_to_cidr;
|
||||||
use tun2proxy::{main_entry, NetworkInterface, Options, Proxy, ProxyType};
|
use tun2proxy::{main_entry, NetworkInterface, Options, Proxy, ProxyType};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -66,12 +68,17 @@ mod tests {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bypass_ip = match env::var("BYPASS_IP") {
|
let mut bypass_ips = Vec::<IpCidr>::new();
|
||||||
Err(_) => test.proxy.addr.ip(),
|
|
||||||
Ok(ip_str) => IpAddr::from_str(ip_str.as_str()).unwrap(),
|
match env::var("BYPASS_IP") {
|
||||||
|
Err(_) => {
|
||||||
|
let prefix_len = if test.proxy.addr.ip().is_ipv6() { 128 } else { 32 };
|
||||||
|
bypass_ips.push(IpCidr::new(test.proxy.addr.ip().into(), prefix_len));
|
||||||
|
}
|
||||||
|
Ok(ip_str) => bypass_ips.push(str_to_cidr(&ip_str).expect("Invalid bypass IP")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut setup = Setup::new(TUN_TEST_DEVICE, &bypass_ip, get_default_cidrs(), false);
|
let mut setup = Setup::new(TUN_TEST_DEVICE, bypass_ips, get_default_cidrs());
|
||||||
setup.configure().unwrap();
|
setup.configure().unwrap();
|
||||||
|
|
||||||
match fork::fork() {
|
match fork::fork() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue