diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2022-10-12 13:32:59 +0200 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2022-11-07 11:54:33 +0100 |
| commit | ac846d3154e584e429927f2ecbec2b906bf50a68 (patch) | |
| tree | 808548e9da6e65d37a49c62e85b91728f9704d98 | |
| parent | 3b8e28cd91aaa59e236a4c4875d9cb43e823042f (diff) | |
| download | mullvadvpn-ac846d3154e584e429927f2ecbec2b906bf50a68.tar.xz mullvadvpn-ac846d3154e584e429927f2ecbec2b906bf50a68.zip | |
Split up talpid-core
| -rw-r--r-- | Cargo.lock | 146 | ||||
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rw-r--r-- | mullvad-api/Cargo.toml | 3 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/bridge.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-daemon/src/early_boot_firewall.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 4 | ||||
| -rw-r--r-- | mullvad-daemon/src/tunnel.rs | 2 | ||||
| -rw-r--r-- | mullvad-jni/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-jni/src/talpid_vpn_service.rs | 2 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/custom_tunnel.rs | 2 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/relay_constraints.rs | 2 | ||||
| -rw-r--r-- | mullvad-paths/Cargo.toml | 2 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/lib.rs | 6 | ||||
| -rw-r--r-- | mullvad-setup/src/main.rs | 11 | ||||
| -rw-r--r-- | mullvad-types/src/lib.rs | 6 | ||||
| -rw-r--r-- | mullvad-types/src/relay_list.rs | 8 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 12 | ||||
| -rw-r--r-- | talpid-core/build.rs | 42 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/mod.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/network_manager.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/systemd_resolved.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/dns/mod.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/dns/windows/mod.rs | 15 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 47 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 11 | ||||
| -rw-r--r-- | talpid-core/src/firewall/windows.rs | 85 | ||||
| -rw-r--r-- | talpid-core/src/lib.rs | 23 | ||||
| -rw-r--r-- | talpid-core/src/linux/mod.rs | 12 | ||||
| -rw-r--r-- | talpid-core/src/logging/mod.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/logging/windows.rs | 78 | ||||
| -rw-r--r-- | talpid-core/src/offline/linux.rs | 19 | ||||
| -rw-r--r-- | talpid-core/src/offline/macos.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/offline/mod.rs | 16 | ||||
| -rw-r--r-- | talpid-core/src/offline/windows.rs | 15 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/driver.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/mod.rs | 15 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/volume_monitor.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 118 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/tun_provider/unix.rs | 69 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 36 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/mod.rs | 41 | ||||
| -rw-r--r-- | talpid-core/src/window.rs (renamed from talpid-core/src/windows/window.rs) | 2 | ||||
| -rw-r--r-- | talpid-dbus/src/network_manager.rs | 6 | ||||
| -rw-r--r-- | talpid-openvpn/Cargo.toml | 62 | ||||
| -rw-r--r-- | talpid-openvpn/build.rs | 9 | ||||
| -rw-r--r-- | talpid-openvpn/src/lib.rs (renamed from talpid-core/src/tunnel/openvpn/mod.rs) | 59 | ||||
| -rw-r--r-- | talpid-openvpn/src/mktemp.rs (renamed from talpid-core/src/mktemp.rs) | 0 | ||||
| -rw-r--r-- | talpid-openvpn/src/process/mod.rs (renamed from talpid-core/src/process/mod.rs) | 0 | ||||
| -rw-r--r-- | talpid-openvpn/src/process/openvpn.rs (renamed from talpid-core/src/process/openvpn.rs) | 19 | ||||
| -rw-r--r-- | talpid-openvpn/src/process/stoppable_process.rs (renamed from talpid-core/src/process/stoppable_process.rs) | 0 | ||||
| -rw-r--r-- | talpid-openvpn/src/proxy/mod.rs (renamed from talpid-core/src/proxy/mod.rs) | 0 | ||||
| -rw-r--r-- | talpid-openvpn/src/proxy/noop.rs (renamed from talpid-core/src/proxy/noop.rs) | 0 | ||||
| -rw-r--r-- | talpid-openvpn/src/proxy/shadowsocks.rs (renamed from talpid-core/src/proxy/shadowsocks.rs) | 2 | ||||
| -rw-r--r-- | talpid-openvpn/src/wintun.rs (renamed from talpid-core/src/tunnel/openvpn/wintun.rs) | 17 | ||||
| -rw-r--r-- | talpid-routing/Cargo.toml | 47 | ||||
| -rw-r--r-- | talpid-routing/src/android.rs (renamed from talpid-core/src/routing/android.rs) | 3 | ||||
| -rw-r--r-- | talpid-routing/src/lib.rs (renamed from talpid-core/src/routing/mod.rs) | 13 | ||||
| -rw-r--r-- | talpid-routing/src/linux.rs (renamed from talpid-core/src/routing/linux.rs) | 81 | ||||
| -rw-r--r-- | talpid-routing/src/macos.rs (renamed from talpid-core/src/routing/macos.rs) | 5 | ||||
| -rw-r--r-- | talpid-routing/src/unix.rs (renamed from talpid-core/src/routing/unix.rs) | 31 | ||||
| -rw-r--r-- | talpid-routing/src/windows/default_route_monitor.rs (renamed from talpid-core/src/routing/windows/default_route_monitor.rs) | 6 | ||||
| -rw-r--r-- | talpid-routing/src/windows/get_best_default_route.rs (renamed from talpid-core/src/routing/windows/get_best_default_route.rs) | 4 | ||||
| -rw-r--r-- | talpid-routing/src/windows/mod.rs (renamed from talpid-core/src/routing/windows/mod.rs) | 48 | ||||
| -rw-r--r-- | talpid-routing/src/windows/route_manager.rs (renamed from talpid-core/src/routing/windows/route_manager.rs) | 12 | ||||
| -rw-r--r-- | talpid-tunnel/Cargo.toml | 42 | ||||
| -rw-r--r-- | talpid-tunnel/src/lib.rs | 62 | ||||
| -rw-r--r-- | talpid-tunnel/src/tun_provider/android/ipnetwork_sub.rs (renamed from talpid-core/src/tunnel/tun_provider/android/ipnetwork_sub.rs) | 0 | ||||
| -rw-r--r-- | talpid-tunnel/src/tun_provider/android/mod.rs (renamed from talpid-core/src/tunnel/tun_provider/android/mod.rs) | 14 | ||||
| -rw-r--r-- | talpid-tunnel/src/tun_provider/mod.rs (renamed from talpid-core/src/tunnel/tun_provider/mod.rs) | 0 | ||||
| -rw-r--r-- | talpid-tunnel/src/tun_provider/stub.rs (renamed from talpid-core/src/tunnel/tun_provider/stub.rs) | 0 | ||||
| -rw-r--r-- | talpid-tunnel/src/tun_provider/unix.rs (renamed from talpid-core/src/network_interface.rs) | 108 | ||||
| -rw-r--r-- | talpid-tunnel/src/windows.rs (renamed from talpid-core/src/tunnel/windows.rs) | 2 | ||||
| -rw-r--r-- | talpid-types/src/net/openvpn.rs | 2 | ||||
| -rw-r--r-- | talpid-types/src/net/wireguard.rs | 2 | ||||
| -rw-r--r-- | talpid-windows-net/Cargo.toml | 24 | ||||
| -rw-r--r-- | talpid-windows-net/src/lib.rs | 10 | ||||
| -rw-r--r-- | talpid-windows-net/src/net.rs (renamed from talpid-core/src/windows/mod.rs) | 125 | ||||
| -rw-r--r-- | talpid-wireguard/Cargo.toml | 78 | ||||
| -rw-r--r-- | talpid-wireguard/build.rs | 36 | ||||
| -rw-r--r-- | talpid-wireguard/src/config.rs (renamed from talpid-core/src/tunnel/wireguard/config.rs) | 8 | ||||
| -rw-r--r-- | talpid-wireguard/src/connectivity_check.rs (renamed from talpid-core/src/tunnel/wireguard/connectivity_check.rs) | 4 | ||||
| -rw-r--r-- | talpid-wireguard/src/lib.rs (renamed from talpid-core/src/tunnel/wireguard/mod.rs) | 46 | ||||
| -rw-r--r-- | talpid-wireguard/src/logging.rs (renamed from talpid-core/src/tunnel/wireguard/logging.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/ping_monitor/android.rs (renamed from talpid-core/src/ping_monitor/android.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/ping_monitor/icmp.rs (renamed from talpid-core/src/ping_monitor/icmp.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/ping_monitor/mod.rs (renamed from talpid-core/src/ping_monitor/mod.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/stats.rs (renamed from talpid-core/src/tunnel/wireguard/stats.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_go.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_go.rs) | 26 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_kernel/mod.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_kernel/mod.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_kernel/netlink_tunnel.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_kernel/nl_message.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_kernel/nl_message.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_kernel/nm_tunnel.rs) | 33 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_kernel/parsers.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_kernel/parsers.rs) | 0 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_kernel/wg_message.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_kernel/wg_message.rs) | 2 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_nt.rs (renamed from talpid-core/src/tunnel/wireguard/wireguard_nt.rs) | 52 |
96 files changed, 1276 insertions, 719 deletions
diff --git a/Cargo.lock b/Cargo.lock index a437183a59..2238ff5948 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1587,7 +1587,6 @@ dependencies = [ "talpid-types", "tokio", "tokio-rustls", - "tokio-stream", "uuid", ] @@ -1626,7 +1625,6 @@ dependencies = [ "ctrlc", "dirs-next", "duct", - "either", "err-derive", "fern", "futures", @@ -1690,6 +1688,7 @@ dependencies = [ "nix 0.23.1", "rand 0.8.5", "talpid-core", + "talpid-tunnel", "talpid-types", "tokio", ] @@ -3121,10 +3120,7 @@ dependencies = [ "log", "memoffset", "mnl", - "netlink-packet-core", "netlink-packet-route", - "netlink-packet-utils", - "netlink-proto", "netlink-sys", "nftnl", "nix 0.23.1", @@ -3146,23 +3142,25 @@ dependencies = [ "subslice", "system-configuration", "talpid-dbus", + "talpid-openvpn", "talpid-platform-metadata", + "talpid-routing", "talpid-time", + "talpid-tunnel", "talpid-tunnel-config-client", "talpid-types", + "talpid-windows-net", + "talpid-wireguard", "tempfile", "tokio", - "tokio-stream", "tonic", "tonic-build", "triggered", "trust-dns-server", "tun", - "tunnel-obfuscation", "uuid", "which", "widestring 1.0.2", - "winapi", "windows", "windows-service", "windows-sys 0.42.0", @@ -3183,6 +3181,43 @@ dependencies = [ ] [[package]] +name = "talpid-openvpn" +version = "0.0.0" +dependencies = [ + "async-trait", + "atty", + "bitflags", + "byteorder", + "cfg-if", + "duct", + "err-derive", + "futures", + "lazy_static", + "log", + "os_pipe", + "parity-tokio-ipc", + "parking_lot 0.11.2", + "prost", + "shadowsocks-service", + "shell-escape", + "socket2", + "talpid-routing", + "talpid-tunnel", + "talpid-types", + "talpid-windows-net", + "tokio", + "tonic", + "tonic-build", + "triggered", + "uuid", + "which", + "widestring 0.5.1", + "winapi", + "windows-sys 0.42.0", + "winreg", +] + +[[package]] name = "talpid-openvpn-plugin" version = "0.0.0" dependencies = [ @@ -3212,6 +3247,30 @@ dependencies = [ ] [[package]] +name = "talpid-routing" +version = "0.0.0" +dependencies = [ + "err-derive", + "futures", + "ipnetwork", + "jnix", + "lazy_static", + "libc", + "log", + "netlink-packet-route", + "netlink-sys", + "rtnetlink", + "socket2", + "talpid-types", + "talpid-windows-net", + "tokio", + "tokio-stream", + "widestring 1.0.2", + "winapi", + "windows-sys 0.42.0", +] + +[[package]] name = "talpid-time" version = "0.0.0" dependencies = [ @@ -3220,6 +3279,26 @@ dependencies = [ ] [[package]] +name = "talpid-tunnel" +version = "0.0.0" +dependencies = [ + "cfg-if", + "duct", + "err-derive", + "futures", + "ipnetwork", + "jnix", + "log", + "nix 0.23.1", + "talpid-routing", + "talpid-types", + "talpid-windows-net", + "tokio", + "tun", + "windows-sys 0.42.0", +] + +[[package]] name = "talpid-tunnel-config-client" version = "0.0.0" dependencies = [ @@ -3249,6 +3328,57 @@ dependencies = [ ] [[package]] +name = "talpid-windows-net" +version = "0.0.0" +dependencies = [ + "err-derive", + "futures", + "libc", + "socket2", + "winapi", + "windows-sys 0.42.0", +] + +[[package]] +name = "talpid-wireguard" +version = "0.0.0" +dependencies = [ + "bitflags", + "byteorder", + "chrono", + "duct", + "err-derive", + "futures", + "hex", + "internet-checksum", + "ipnetwork", + "lazy_static", + "libc", + "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-packet-utils", + "netlink-proto", + "nix 0.23.1", + "parking_lot 0.11.2", + "rand 0.8.5", + "rtnetlink", + "socket2", + "talpid-dbus", + "talpid-routing", + "talpid-tunnel", + "talpid-tunnel-config-client", + "talpid-types", + "talpid-windows-net", + "tokio", + "tokio-stream", + "tunnel-obfuscation", + "widestring 0.5.1", + "windows-sys 0.42.0", + "zeroize", +] + +[[package]] name = "tempfile" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 91fbf682aa..1868ba9dfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,14 @@ members = [ "talpid-openvpn-plugin", "talpid-core", "talpid-dbus", + "talpid-openvpn", "talpid-platform-metadata", + "talpid-routing", "talpid-time", + "talpid-tunnel", "talpid-tunnel-config-client", + "talpid-windows-net", + "talpid-wireguard", "mullvad-management-interface", "tunnel-obfuscation", ] diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index 2a7f8ef51d..e02ac22e11 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -34,6 +34,3 @@ talpid-time = { path = "../talpid-time" } shadowsocks = { version = "1.14.2", default-features = false, features = ["stream-cipher"] } uuid = { version = "0.8", features = ["v4"] } - -[target.'cfg(target_os="macos")'.dependencies] -tokio-stream = { version = "0.1", features = ["io-util"] } diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs index 9d1c5172fa..e283dc96ed 100644 --- a/mullvad-cli/src/cmds/bridge.rs +++ b/mullvad-cli/src/cmds/bridge.rs @@ -383,6 +383,8 @@ impl Bridge { peer: SocketAddr::new(remote_ip, remote_port), password, cipher, + #[cfg(target_os = "linux")] + fwmark: None, }; let packed_proxy = openvpn::ProxySettings::Shadowsocks(proxy); if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 5069a38f2a..0348051bfe 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -12,7 +12,6 @@ cfg-if = "1.0" chrono = { version = "0.4.19", features = ["serde"] } clap = { version = "3.0", features = ["cargo"] } err-derive = "0.3.1" -either = "1" fern = { version = "0.6", features = ["colored"] } futures = "0.3" ipnetwork = "0.16" diff --git a/mullvad-daemon/src/early_boot_firewall.rs b/mullvad-daemon/src/early_boot_firewall.rs index 7e93f47b27..a5df67be0b 100644 --- a/mullvad-daemon/src/early_boot_firewall.rs +++ b/mullvad-daemon/src/early_boot_firewall.rs @@ -14,7 +14,7 @@ pub enum Error { } pub async fn initialize_firewall() -> Result<(), Error> { - let mut firewall = Firewall::new()?; + let mut firewall = Firewall::new(mullvad_types::TUNNEL_FWMARK)?; let allow_lan = get_allow_lan().await.unwrap_or_else(|err| { log::info!( "Not allowing LAN traffic due to failing to read settings: {}", diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 4f22c7244c..fce026ff1f 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -692,6 +692,10 @@ where exclusion_gid, #[cfg(target_os = "android")] android_context, + #[cfg(target_os = "linux")] + mullvad_types::TUNNEL_FWMARK, + #[cfg(target_os = "linux")] + mullvad_types::TUNNEL_TABLE_ID, ) .await .map_err(Error::TunnelError)?; diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs index 7f7fb0c8b3..81e0063166 100644 --- a/mullvad-daemon/src/tunnel.rs +++ b/mullvad-daemon/src/tunnel.rs @@ -216,6 +216,8 @@ impl InnerParametersGenerator { exit_peer: endpoint.exit_peer, ipv4_gateway: endpoint.ipv4_gateway, ipv6_gateway: Some(endpoint.ipv6_gateway), + #[cfg(target_os = "linux")] + fwmark: Some(mullvad_types::TUNNEL_FWMARK), }, options: self.tunnel_options.wireguard.options.clone(), generic_options: self.tunnel_options.generic.clone(), diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml index da60b07ea8..dcf3546a95 100644 --- a/mullvad-jni/Cargo.toml +++ b/mullvad-jni/Cargo.toml @@ -28,4 +28,5 @@ mullvad-problem-report = { path = "../mullvad-problem-report" } mullvad-types = { path = "../mullvad-types" } mullvad-api = { path = "../mullvad-api" } talpid-core = { path = "../talpid-core" } +talpid-tunnel = { path = "../talpid-tunnel" } talpid-types = { path = "../talpid-types" } diff --git a/mullvad-jni/src/talpid_vpn_service.rs b/mullvad-jni/src/talpid_vpn_service.rs index d2b1f00075..554313b8e4 100644 --- a/mullvad-jni/src/talpid_vpn_service.rs +++ b/mullvad-jni/src/talpid_vpn_service.rs @@ -18,7 +18,7 @@ use std::{ os::unix::io::RawFd, time::{Duration, Instant}, }; -use talpid_core::tunnel::TunConfig; +use talpid_tunnel::tun_provider::TunConfig; use talpid_types::ErrorExt; #[derive(Debug, err_derive::Error)] diff --git a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs index 950f8f920c..146799b473 100644 --- a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs +++ b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs @@ -94,6 +94,8 @@ impl TryFrom<proto::ConnectionConfig> for mullvad_types::ConnectionConfig { exit_peer: None, ipv4_gateway, ipv6_gateway, + #[cfg(target_os = "linux")] + fwmark: Some(mullvad_types::TUNNEL_FWMARK), }, )) } diff --git a/mullvad-management-interface/src/types/conversions/relay_constraints.rs b/mullvad-management-interface/src/types/conversions/relay_constraints.rs index d76006bf7f..a402a058ed 100644 --- a/mullvad-management-interface/src/types/conversions/relay_constraints.rs +++ b/mullvad-management-interface/src/types/conversions/relay_constraints.rs @@ -520,6 +520,8 @@ impl TryFrom<proto::BridgeSettings> for mullvad_types::relay_constraints::Bridge })?; let proxy_settings = talpid_net::openvpn::ProxySettings::Shadowsocks( talpid_net::openvpn::ShadowsocksProxySettings { + #[cfg(target_os = "linux")] + fwmark: Some(mullvad_types::TUNNEL_FWMARK), peer, password: proxy_settings.password, cipher: proxy_settings.cipher, diff --git a/mullvad-paths/Cargo.toml b/mullvad-paths/Cargo.toml index 04edf4e365..57690210bb 100644 --- a/mullvad-paths/Cargo.toml +++ b/mullvad-paths/Cargo.toml @@ -11,5 +11,5 @@ publish = false err-derive = "0.3.1" log = "0.4" -[target.'cfg(any(windows, target_os = "macos"))'.dependencies] +[target.'cfg(windows)'.dependencies] dirs-next = "2.0" diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs index b398d5268e..e880b101d1 100644 --- a/mullvad-relay-selector/src/lib.rs +++ b/mullvad-relay-selector/src/lib.rs @@ -1138,7 +1138,11 @@ impl RelaySelector { shadowsocks_endpoint.port, shadowsocks_endpoint.protocol ); - shadowsocks_endpoint.to_proxy_settings(relay.ipv4_addr_in.into()) + shadowsocks_endpoint.to_proxy_settings( + relay.ipv4_addr_in.into(), + #[cfg(target_os = "linux")] + mullvad_types::TUNNEL_FWMARK, + ) }) } diff --git a/mullvad-setup/src/main.rs b/mullvad-setup/src/main.rs index 2f210ebb76..c5ef3f3000 100644 --- a/mullvad-setup/src/main.rs +++ b/mullvad-setup/src/main.rs @@ -154,10 +154,13 @@ async fn reset_firewall() -> Result<(), Error> { return Err(Error::DaemonIsRunning); } - Firewall::new() - .map_err(Error::FirewallError)? - .reset_policy() - .map_err(Error::FirewallError) + Firewall::new( + #[cfg(target_os = "linux")] + mullvad_types::TUNNEL_FWMARK, + ) + .map_err(Error::FirewallError)? + .reset_policy() + .map_err(Error::FirewallError) } async fn remove_device() -> Result<(), Error> { diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs index 6d636aceb5..d157f55467 100644 --- a/mullvad-types/src/lib.rs +++ b/mullvad-types/src/lib.rs @@ -14,3 +14,9 @@ pub mod wireguard; mod custom_tunnel; pub use crate::custom_tunnel::*; + +// b"mole" is [ 0x6d, 0x6f 0x6c, 0x65 ] +#[cfg(target_os = "linux")] +pub const TUNNEL_TABLE_ID: u32 = 0x6d6f6c65; +#[cfg(target_os = "linux")] +pub const TUNNEL_FWMARK: u32 = 0x6d6f6c65; diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs index 643bcbd798..9e9b77be9d 100644 --- a/mullvad-types/src/relay_list.rs +++ b/mullvad-types/src/relay_list.rs @@ -164,11 +164,17 @@ pub struct ShadowsocksEndpointData { } impl ShadowsocksEndpointData { - pub fn to_proxy_settings(&self, addr: IpAddr) -> ProxySettings { + pub fn to_proxy_settings( + &self, + addr: IpAddr, + #[cfg(target_os = "linux")] fwmark: u32, + ) -> ProxySettings { ProxySettings::Shadowsocks(ShadowsocksProxySettings { peer: SocketAddr::new(addr, self.port), password: self.password.clone(), cipher: self.cipher.clone(), + #[cfg(target_os = "linux")] + fwmark: Some(fwmark), }) } } diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index fbd231a6a8..162bfd8ea9 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -25,16 +25,17 @@ os_pipe = "0.9" parking_lot = "0.11" regex = "1.1.0" shell-escape = "0.1" +talpid-routing = { path = "../talpid-routing" } talpid-types = { path = "../talpid-types" } talpid-time = { path = "../talpid-time" } talpid-tunnel-config-client = { path = "../talpid-tunnel-config-client" } +talpid-tunnel = { path = "../talpid-tunnel" } +talpid-wireguard = { path = "../talpid-wireguard" } uuid = { version = "0.8", features = ["v4"] } zeroize = "1" chrono = "0.4.21" tokio = { version = "1.8", features = ["process", "rt-multi-thread", "fs"] } -tokio-stream = { version = "0.1", features = ["io-util"] } rand = "0.8.5" -tunnel-obfuscation = { path = "../tunnel-obfuscation" } shadowsocks-service = { version = "1.14.3", default-features = false, features = ["local", "stream-cipher"] } [target.'cfg(not(target_os="android"))'.dependencies] @@ -42,6 +43,7 @@ byteorder = "1" internet-checksum = "0.2" socket2 = { version = "0.4.2", features = ["all"] } parity-tokio-ipc = "0.9" +talpid-openvpn = { path = "../talpid-openvpn" } triggered = "0.1.1" tonic = "0.8" prost = "0.11" @@ -58,10 +60,7 @@ jnix = { version = "0.5", features = ["derive"] } inotify = "0.10" resolv-conf = "0.7" rtnetlink = "0.11" -netlink-packet-core = "0.4.2" -netlink-packet-utils = "0.5.1" netlink-packet-route = "0.13" -netlink-proto = "0.10" netlink-sys = "0.8.3" nftnl = { version = "0.6.2", features = ["nftnl-1-1-0"] } mnl = { version = "0.2.2", features = ["mnl-1-0-4"] } @@ -81,10 +80,10 @@ subslice = "0.2" [target.'cfg(windows)'.dependencies] widestring = "1.0" winreg = { version = "0.7", features = ["transactions"] } -winapi = { version = "0.3.6", features = ["ws2def"] } talpid-platform-metadata = { path = "../talpid-platform-metadata" } memoffset = "0.6" windows-service = "0.5.0" +talpid-windows-net = { path = "../talpid-windows-net" } [target.'cfg(windows)'.dependencies.windows] version = "0.42.0" @@ -108,7 +107,6 @@ features = [ "Win32_Globalization", "Win32_Security", "Win32_Storage_FileSystem", - "Win32_System_Com", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Ioctl", "Win32_System_IO", diff --git a/talpid-core/build.rs b/talpid-core/build.rs index 70a1fb6d53..c9bced6bfb 100644 --- a/talpid-core/build.rs +++ b/talpid-core/build.rs @@ -1,8 +1,5 @@ -use std::{env, path::PathBuf}; - #[cfg(windows)] mod win { - use super::manifest_dir; use std::{env, path::PathBuf}; pub static WINFW_BUILD_DIR: &'static str = "..\\windows\\winfw\\bin"; @@ -39,6 +36,12 @@ mod win { println!("cargo:rustc-link-search={}", lib_dir.display()); println!("cargo:rustc-link-lib=dylib={}", lib_name); } + + pub fn manifest_dir() -> PathBuf { + env::var("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .expect("CARGO_MANIFEST_DIR env var not set") + } } #[cfg(windows)] @@ -56,38 +59,7 @@ fn main() { #[cfg(not(windows))] fn main() { - generate_grpc_code(); - - let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); - - declare_libs_dir("../dist-assets/binaries"); - declare_libs_dir("../build/lib"); - - let link_type = match target_os.as_str() { - "android" => "", - "linux" | "macos" => "=static", - // We would like to avoid panicing on windows even if we can not link correctly - // because we would like to be able to run check and clippy. - // This does not allow for correct linking or building. - "windows" => "", - _ => panic!("Unsupported platform: {}", target_os), - }; - - println!("cargo:rustc-link-lib{}=wg", link_type); -} - -fn manifest_dir() -> PathBuf { - env::var("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .expect("CARGO_MANIFEST_DIR env var not set") -} - -#[cfg(not(windows))] -fn declare_libs_dir(base: &str) { - let target_triplet = env::var("TARGET").expect("TARGET is not set"); - let lib_dir = manifest_dir().join(base).join(target_triplet); - println!("cargo:rerun-if-changed={}", lib_dir.display()); - println!("cargo:rustc-link-search={}", lib_dir.display()); + generate_grpc_code() } fn generate_grpc_code() { diff --git a/talpid-core/src/dns/linux/mod.rs b/talpid-core/src/dns/linux/mod.rs index 3eb11df51a..b7e2ad3f69 100644 --- a/talpid-core/src/dns/linux/mod.rs +++ b/talpid-core/src/dns/linux/mod.rs @@ -7,8 +7,8 @@ use self::{ network_manager::NetworkManager, resolvconf::Resolvconf, static_resolv_conf::StaticResolvConf, systemd_resolved::SystemdResolved, }; -use crate::routing::RouteManagerHandle; use std::{env, fmt, net::IpAddr}; +use talpid_routing::RouteManagerHandle; pub type Result<T> = std::result::Result<T, Error>; diff --git a/talpid-core/src/dns/linux/network_manager.rs b/talpid-core/src/dns/linux/network_manager.rs index d44c273cc1..14e3fe7178 100644 --- a/talpid-core/src/dns/linux/network_manager.rs +++ b/talpid-core/src/dns/linux/network_manager.rs @@ -13,9 +13,7 @@ pub struct NetworkManager { impl NetworkManager { pub fn new() -> Result<Self> { let connection = DBus::new()?; - connection.ensure_resolv_conf_is_managed()?; - connection.ensure_network_manager_exists()?; - connection.nm_version_dns_works()?; + connection.ensure_can_be_used_to_manage_dns()?; let manager = NetworkManager { connection, device: None, diff --git a/talpid-core/src/dns/linux/systemd_resolved.rs b/talpid-core/src/dns/linux/systemd_resolved.rs index 321b4181ed..7094a5f28b 100644 --- a/talpid-core/src/dns/linux/systemd_resolved.rs +++ b/talpid-core/src/dns/linux/systemd_resolved.rs @@ -1,9 +1,7 @@ -use crate::{ - linux::{iface_index, IfaceIndexLookupError}, - routing::RouteManagerHandle, -}; +use crate::linux::{iface_index, IfaceIndexLookupError}; use std::net::IpAddr; use talpid_dbus::systemd_resolved::{AsyncHandle, SystemdResolved as DbusInterface}; +use talpid_routing::RouteManagerHandle; use talpid_types::ErrorExt; pub(crate) use talpid_dbus::systemd_resolved::Error as SystemdDbusError; diff --git a/talpid-core/src/dns/mod.rs b/talpid-core/src/dns/mod.rs index 900b32e902..ca06251f55 100644 --- a/talpid-core/src/dns/mod.rs +++ b/talpid-core/src/dns/mod.rs @@ -1,6 +1,6 @@ -#[cfg(target_os = "linux")] -use crate::routing::RouteManagerHandle; use std::net::IpAddr; +#[cfg(target_os = "linux")] +use talpid_routing::RouteManagerHandle; #[cfg(target_os = "macos")] use { diff --git a/talpid-core/src/dns/windows/mod.rs b/talpid-core/src/dns/windows/mod.rs index d173290be3..9b780b1f54 100644 --- a/talpid-core/src/dns/windows/mod.rs +++ b/talpid-core/src/dns/windows/mod.rs @@ -1,7 +1,7 @@ -use crate::windows::{guid_from_luid, luid_from_alias, string_from_guid}; use std::{io, net::IpAddr}; use talpid_types::ErrorExt; -use windows_sys::core::GUID; +use talpid_windows_net::{guid_from_luid, luid_from_alias}; +use windows_sys::{core::GUID, Win32::System::Com::StringFromGUID2}; use winreg::{ enums::{HKEY_LOCAL_MACHINE, KEY_SET_VALUE}, transaction::Transaction, @@ -144,3 +144,14 @@ fn config_interface<'a>( fn flush_dns_cache() -> Result<(), Error> { dnsapi::flush_resolver_cache().map_err(Error::FlushResolverCacheError) } + +/// Obtain a string representation for a GUID object. +fn string_from_guid(guid: &GUID) -> String { + let mut buffer = [0u16; 40]; + let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) } + as usize; + // cannot fail because `buffer` is large enough + assert!(length > 0); + let length = length - 1; + String::from_utf16(&buffer[0..length]).unwrap() +} diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs index b873d71b62..14900afb51 100644 --- a/talpid-core/src/firewall/linux.rs +++ b/talpid-core/src/firewall/linux.rs @@ -11,7 +11,7 @@ use nftnl::{ use std::{ env, ffi::{CStr, CString}, - io, + fs, io, net::{IpAddr, Ipv4Addr}, }; use talpid_types::net::{AllowedTunnelTraffic, Endpoint, TransportProtocol}; @@ -19,6 +19,7 @@ use talpid_types::net::{AllowedTunnelTraffic, Endpoint, TransportProtocol}; /// Priority for rules that tag split tunneling packets. Equals NF_IP_PRI_MANGLE. const MANGLE_CHAIN_PRIORITY: i32 = libc::NF_IP_PRI_MANGLE; const PREROUTING_CHAIN_PRIORITY: i32 = libc::NF_IP_PRI_CONNTRACK + 1; +const PROC_SYS_NET_IPV4_CONF_SRC_VALID_MARK: &str = "/proc/sys/net/ipv4/conf/all/src_valid_mark"; pub type Result<T> = std::result::Result<T, Error>; @@ -96,7 +97,9 @@ enum End { } /// The Linux implementation for the firewall and DNS. -pub struct Firewall(()); +pub struct Firewall { + fwmark: u32, +} struct FirewallTables { main: Table, @@ -105,12 +108,12 @@ struct FirewallTables { } impl Firewall { - pub fn from_args(_args: FirewallArguments) -> Result<Self> { - Ok(Firewall(())) + pub fn from_args(args: FirewallArguments) -> Result<Self> { + Firewall::new(args.fwmark) } - pub fn new() -> Result<Self> { - Ok(Firewall(())) + pub fn new(fwmark: u32) -> Result<Self> { + Ok(Firewall { fwmark }) } pub fn apply_policy(&mut self, policy: FirewallPolicy) -> Result<()> { @@ -119,7 +122,7 @@ impl Firewall { mangle_v4: Table::new(&*MANGLE_TABLE_NAME_V4, ProtoFamily::Ipv4), mangle_v6: Table::new(&*MANGLE_TABLE_NAME_V6, ProtoFamily::Ipv6), }; - let batch = PolicyBatch::new(&tables).finalize(&policy)?; + let batch = PolicyBatch::new(&tables).finalize(&policy, self.fwmark)?; Self::send_and_process(&batch)?; Self::apply_kernel_config(&policy); self.verify_tables(&[&TABLE_NAME, &MANGLE_TABLE_NAME_V4, &MANGLE_TABLE_NAME_V6]) @@ -152,7 +155,7 @@ impl Firewall { } if let FirewallPolicy::Connecting { .. } = policy { - if let Err(err) = crate::linux::set_src_valid_mark_sysctl() { + if let Err(err) = set_src_valid_mark_sysctl() { log::error!("Failed to apply src_valid_mark: {}", err); } } @@ -314,17 +317,17 @@ impl<'a> PolicyBatch<'a> { /// Finalize the nftnl message batch by adding every firewall rule needed to satisfy the given /// policy. - pub fn finalize(mut self, policy: &FirewallPolicy) -> Result<FinalizedBatch> { + pub fn finalize(mut self, policy: &FirewallPolicy, fwmark: u32) -> Result<FinalizedBatch> { self.add_loopback_rules()?; - self.add_split_tunneling_rules(policy)?; + self.add_split_tunneling_rules(policy, fwmark)?; self.add_dhcp_client_rules(); self.add_ndp_rules(); - self.add_policy_specific_rules(policy)?; + self.add_policy_specific_rules(policy, fwmark)?; Ok(self.batch.finalize()) } - fn add_split_tunneling_rules(&mut self, policy: &FirewallPolicy) -> Result<()> { + fn add_split_tunneling_rules(&mut self, policy: &FirewallPolicy, fwmark: u32) -> Result<()> { // Send select DNS requests in the tunnel if let FirewallPolicy::Connected { tunnel, @@ -365,7 +368,7 @@ impl<'a> PolicyBatch<'a> { rule.add_expr(&nft_expr!(cmp == split_tunnel::NET_CLS_CLASSID)); rule.add_expr(&nft_expr!(immediate data split_tunnel::MARK)); rule.add_expr(&nft_expr!(ct mark set)); - rule.add_expr(&nft_expr!(immediate data crate::linux::TUNNEL_FW_MARK)); + rule.add_expr(&nft_expr!(immediate data fwmark)); rule.add_expr(&nft_expr!(meta mark set)); self.batch.add(&rule, nftnl::MsgType::Add); } @@ -416,7 +419,7 @@ impl<'a> PolicyBatch<'a> { check_not_iface(&mut prerouting_rule, Direction::In, &tunnel.interface)?; prerouting_rule.add_expr(&nft_expr!(ct mark)); prerouting_rule.add_expr(&nft_expr!(cmp == split_tunnel::MARK)); - prerouting_rule.add_expr(&nft_expr!(immediate data crate::linux::TUNNEL_FW_MARK)); + prerouting_rule.add_expr(&nft_expr!(immediate data fwmark)); prerouting_rule.add_expr(&nft_expr!(meta mark set)); if *ADD_COUNTERS { prerouting_rule.add_expr(&nft_expr!(counter)); @@ -551,7 +554,7 @@ impl<'a> PolicyBatch<'a> { } } - fn add_policy_specific_rules(&mut self, policy: &FirewallPolicy) -> Result<()> { + fn add_policy_specific_rules(&mut self, policy: &FirewallPolicy, fwmark: u32) -> Result<()> { let allow_lan = match policy { FirewallPolicy::Connecting { peer_endpoint, @@ -560,7 +563,7 @@ impl<'a> PolicyBatch<'a> { allowed_endpoint, allowed_tunnel_traffic, } => { - self.add_allow_tunnel_endpoint_rules(peer_endpoint); + self.add_allow_tunnel_endpoint_rules(peer_endpoint, fwmark); self.add_allow_endpoint_rules(&allowed_endpoint.endpoint); // Important to block DNS after allow relay rule (so the relay can operate @@ -589,7 +592,7 @@ impl<'a> PolicyBatch<'a> { allow_lan, dns_servers, } => { - self.add_allow_tunnel_endpoint_rules(peer_endpoint); + self.add_allow_tunnel_endpoint_rules(peer_endpoint, fwmark); self.add_allow_dns_rules(tunnel, dns_servers, TransportProtocol::Udp)?; self.add_allow_dns_rules(tunnel, dns_servers, TransportProtocol::Tcp)?; // Important to block DNS *before* we allow the tunnel and allow LAN. So DNS @@ -632,10 +635,10 @@ impl<'a> PolicyBatch<'a> { Ok(()) } - fn add_allow_tunnel_endpoint_rules(&mut self, endpoint: &Endpoint) { + fn add_allow_tunnel_endpoint_rules(&mut self, endpoint: &Endpoint, fwmark: u32) { let mut prerouting_rule = Rule::new(&self.prerouting_chain); check_endpoint(&mut prerouting_rule, End::Src, endpoint); - prerouting_rule.add_expr(&nft_expr!(immediate data crate::linux::TUNNEL_FW_MARK)); + prerouting_rule.add_expr(&nft_expr!(immediate data fwmark)); prerouting_rule.add_expr(&nft_expr!(meta mark set)); if *ADD_COUNTERS { @@ -658,7 +661,7 @@ impl<'a> PolicyBatch<'a> { let mut out_rule = Rule::new(&self.out_chain); check_endpoint(&mut out_rule, End::Dst, endpoint); out_rule.add_expr(&nft_expr!(meta mark)); - out_rule.add_expr(&nft_expr!(cmp == crate::linux::TUNNEL_FW_MARK)); + out_rule.add_expr(&nft_expr!(cmp == fwmark)); add_verdict(&mut out_rule, &Verdict::Accept); self.batch.add(&out_rule, nftnl::MsgType::Add); @@ -1059,3 +1062,7 @@ fn add_verdict(rule: &mut Rule<'_>, verdict: &expr::Verdict) { } rule.add_expr(verdict); } + +fn set_src_valid_mark_sysctl() -> io::Result<()> { + fs::write(PROC_SYS_NET_IPV4_CONF_SRC_VALID_MARK, b"1") +} diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs index 5aea81cc90..dcd0c2bfb8 100644 --- a/talpid-core/src/firewall/mod.rs +++ b/talpid-core/src/firewall/mod.rs @@ -233,6 +233,10 @@ pub struct FirewallArguments { pub initial_state: InitialFirewallState, /// This argument is required for the blocked state to configure the firewall correctly. pub allow_lan: bool, + /// Specifies the firewall mark used to identify traffic that is allowed to be excluded from + /// the tunnel and _leaked_ during blocked states. + #[cfg(target_os = "linux")] + pub fwmark: u32, } /// State to enter during firewall init. @@ -252,9 +256,12 @@ impl Firewall { } /// Createsa new firewall instance. - pub fn new() -> Result<Self, Error> { + pub fn new(#[cfg(target_os = "linux")] fwmark: u32) -> Result<Self, Error> { Ok(Firewall { - inner: imp::Firewall::new()?, + inner: imp::Firewall::new( + #[cfg(target_os = "linux")] + fwmark, + )?, }) } diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs index 23be309a63..e2854e0197 100644 --- a/talpid-core/src/firewall/windows.rs +++ b/talpid-core/src/firewall/windows.rs @@ -1,6 +1,6 @@ -use crate::{logging::windows::log_sink, tunnel::TunnelMetadata}; +use crate::tunnel::TunnelMetadata; -use std::{net::IpAddr, path::Path, ptr}; +use std::{ffi::CStr, io, net::IpAddr, path::Path, ptr}; use self::winfw::*; use super::{FirewallArguments, FirewallPolicy, InitialFirewallState}; @@ -9,6 +9,7 @@ use talpid_types::{ tunnel::FirewallPolicyError, }; use widestring::WideCString; +use windows_sys::Win32::Globalization::{MultiByteToWideChar, CP_ACP}; /// Errors that can happen when configuring the Windows firewall. #[derive(err_derive::Error, Debug)] @@ -294,13 +295,91 @@ fn widestring_ip(ip: IpAddr) -> WideCString { WideCString::from_str_truncate(ip.to_string()) } +/// Logging callback implementation. +pub extern "system" fn log_sink( + level: log::Level, + msg: *const libc::c_char, + context: *mut libc::c_void, +) { + if msg.is_null() { + log::error!("Log message from FFI boundary is NULL"); + } else { + let rust_log_level = log::Level::from(level); + let target = if context.is_null() { + "UNKNOWN".into() + } else { + unsafe { CStr::from_ptr(context as *const _).to_string_lossy() } + }; + + let mb_string = unsafe { CStr::from_ptr(msg) }; + + let managed_msg = match multibyte_to_wide(mb_string, CP_ACP) { + Ok(wide_str) => String::from_utf16_lossy(&wide_str), + // Best effort: + Err(_) => mb_string.to_string_lossy().into_owned(), + }; + + log::logger().log( + &log::Record::builder() + .level(rust_log_level) + .target(&target) + .args(format_args!("{}", managed_msg)) + .build(), + ); + } +} + +fn multibyte_to_wide(mb_string: &CStr, codepage: u32) -> Result<Vec<u16>, io::Error> { + if unsafe { *mb_string.as_ptr() } == 0 { + return Ok(vec![]); + } + + let wc_size = unsafe { + MultiByteToWideChar( + codepage, + 0, + mb_string.as_ptr() as *const u8, + -1, + ptr::null_mut(), + 0, + ) + }; + + if wc_size == 0 { + return Err(io::Error::last_os_error()); + } + + let mut wc_buffer = Vec::with_capacity(wc_size as usize); + + let chars_written = unsafe { + MultiByteToWideChar( + codepage, + 0, + mb_string.as_ptr() as *const u8, + -1, + wc_buffer.as_mut_ptr(), + wc_size, + ) + }; + + if chars_written == 0 { + return Err(io::Error::last_os_error()); + } + + unsafe { wc_buffer.set_len((chars_written - 1) as usize) }; + + Ok(wc_buffer) +} + #[allow(non_snake_case)] mod winfw { use super::{widestring_ip, AllowedEndpoint, AllowedTunnelTraffic, Error, WideCString}; - use crate::logging::windows::LogSink; use libc; use talpid_types::net::TransportProtocol; + type LogSink = + extern "system" fn(level: log::Level, msg: *const libc::c_char, context: *mut libc::c_void); + pub struct WinFwAllowedEndpointContainer { _clients: Box<[WideCString]>, clients_ptrs: Box<[*const u16]>, diff --git a/talpid-core/src/lib.rs b/talpid-core/src/lib.rs index 46bb4c1169..cc908bbfbe 100644 --- a/talpid-core/src/lib.rs +++ b/talpid-core/src/lib.rs @@ -9,24 +9,15 @@ #[macro_use] mod ffi; -/// Windows API wrappers and utilities +/// Window API wrappers and utilities #[cfg(target_os = "windows")] -pub mod windows; - -#[cfg(any(target_os = "linux", target_os = "macos"))] -/// Working with IP interface devices -pub mod network_interface; -/// Abstraction over operating system routing table. -pub mod routing; +pub mod window; mod offline; /// Split tunneling pub mod split_tunnel; -/// Working with processes. -pub mod process; - /// Abstracts over different VPN tunnel technologies pub mod tunnel; @@ -48,20 +39,10 @@ pub mod tunnel_state_machine; /// Future utilities pub mod future_retry; -#[cfg(not(target_os = "android"))] -/// Internal code for managing bundled proxy software. -mod proxy; - -#[cfg(not(target_os = "android"))] -mod mktemp; - /// Misc utilities for the Linux platform. #[cfg(target_os = "linux")] mod linux; -/// A pair of functions to monitor and establish connectivity with ICMP -pub mod ping_monitor; - /// A resolver that's controlled by the tunnel state machine #[cfg(target_os = "macos")] pub mod resolver; diff --git a/talpid-core/src/linux/mod.rs b/talpid-core/src/linux/mod.rs index 6c070c1985..05655bf4be 100644 --- a/talpid-core/src/linux/mod.rs +++ b/talpid-core/src/linux/mod.rs @@ -1,10 +1,8 @@ use std::{ ffi::{self, CString}, - fs, io, + io, }; -const PROC_SYS_NET_IPV4_CONF_SRC_VALID_MARK: &str = "/proc/sys/net/ipv4/conf/all/src_valid_mark"; - /// Converts an interface name into the corresponding index. pub fn iface_index(name: &str) -> Result<libc::c_uint, IfaceIndexLookupError> { let c_name = CString::new(name) @@ -27,11 +25,3 @@ pub enum IfaceIndexLookupError { #[error(display = "Failed to get index for interface {}", _0)] InterfaceLookupError(String, #[error(source)] io::Error), } - -// b"mole" is [ 0x6d, 0x6f 0x6c, 0x65 ] -pub const TUNNEL_FW_MARK: u32 = 0x6d6f6c65; -pub const TUNNEL_TABLE_ID: u32 = 0x6d6f6c65; - -pub fn set_src_valid_mark_sysctl() -> io::Result<()> { - fs::write(PROC_SYS_NET_IPV4_CONF_SRC_VALID_MARK, b"1") -} diff --git a/talpid-core/src/logging/mod.rs b/talpid-core/src/logging/mod.rs index 41791fe668..3251a39799 100644 --- a/talpid-core/src/logging/mod.rs +++ b/talpid-core/src/logging/mod.rs @@ -1,9 +1,5 @@ use std::{fs, io, path::Path}; -/// Types/implementations for logging through a callback. -#[cfg(windows)] -pub mod windows; - /// Unable to create new log file #[derive(err_derive::Error, Debug)] #[error(display = "Unable to create new log file")] diff --git a/talpid-core/src/logging/windows.rs b/talpid-core/src/logging/windows.rs deleted file mode 100644 index 2558660a5b..0000000000 --- a/talpid-core/src/logging/windows.rs +++ /dev/null @@ -1,78 +0,0 @@ -use libc::{c_char, c_void}; -use std::{ffi::CStr, io, ptr}; -use windows_sys::Win32::Globalization::{MultiByteToWideChar, CP_ACP}; - -/// Logging callback type. -pub type LogSink = extern "system" fn(level: log::Level, msg: *const c_char, context: *mut c_void); - -/// Logging callback implementation. -pub extern "system" fn log_sink(level: log::Level, msg: *const c_char, context: *mut c_void) { - if msg.is_null() { - log::error!("Log message from FFI boundary is NULL"); - } else { - let rust_log_level = log::Level::from(level); - let target = if context.is_null() { - "UNKNOWN".into() - } else { - unsafe { CStr::from_ptr(context as *const _).to_string_lossy() } - }; - - let mb_string = unsafe { CStr::from_ptr(msg) }; - - let managed_msg = match multibyte_to_wide(mb_string, CP_ACP) { - Ok(wide_str) => String::from_utf16_lossy(&wide_str), - // Best effort: - Err(_) => mb_string.to_string_lossy().into_owned(), - }; - - log::logger().log( - &log::Record::builder() - .level(rust_log_level) - .target(&target) - .args(format_args!("{}", managed_msg)) - .build(), - ); - } -} - -fn multibyte_to_wide(mb_string: &CStr, codepage: u32) -> Result<Vec<u16>, io::Error> { - if unsafe { *mb_string.as_ptr() } == 0 { - return Ok(vec![]); - } - - let wc_size = unsafe { - MultiByteToWideChar( - codepage, - 0, - mb_string.as_ptr() as *const u8, - -1, - ptr::null_mut(), - 0, - ) - }; - - if wc_size == 0 { - return Err(io::Error::last_os_error()); - } - - let mut wc_buffer = Vec::with_capacity(wc_size as usize); - - let chars_written = unsafe { - MultiByteToWideChar( - codepage, - 0, - mb_string.as_ptr() as *const u8, - -1, - wc_buffer.as_mut_ptr(), - wc_size, - ) - }; - - if chars_written == 0 { - return Err(io::Error::last_os_error()); - } - - unsafe { wc_buffer.set_len((chars_written - 1) as usize) }; - - Ok(wc_buffer) -} diff --git a/talpid-core/src/offline/linux.rs b/talpid-core/src/offline/linux.rs index 913202f08c..09db30e359 100644 --- a/talpid-core/src/offline/linux.rs +++ b/talpid-core/src/offline/linux.rs @@ -1,9 +1,9 @@ -use crate::routing::{self, RouteManagerHandle}; use futures::{channel::mpsc::UnboundedSender, StreamExt}; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, sync::Arc, }; +use talpid_routing::{self, RouteManagerHandle}; use talpid_types::ErrorExt; pub type Result<T> = std::result::Result<T, Error>; @@ -12,11 +12,12 @@ pub type Result<T> = std::result::Result<T, Error>; #[error(no_from)] pub enum Error { #[error(display = "The route manager returned an error")] - RouteManagerError(#[error(source)] routing::Error), + RouteManagerError(#[error(source)] talpid_routing::Error), } pub struct MonitorHandle { route_manager: RouteManagerHandle, + fwmark: Option<u32>, _notify_tx: Arc<UnboundedSender<bool>>, } @@ -26,7 +27,7 @@ const PUBLIC_INTERNET_ADDRESS_V6: IpAddr = impl MonitorHandle { pub async fn host_is_offline(&self) -> bool { - match public_ip_unreachable(&self.route_manager).await { + match public_ip_unreachable(&self.route_manager, self.fwmark).await { Ok(is_offline) => is_offline, Err(err) => { log::error!( @@ -42,8 +43,9 @@ impl MonitorHandle { pub async fn spawn_monitor( notify_tx: UnboundedSender<bool>, route_manager: RouteManagerHandle, + fwmark: Option<u32>, ) -> Result<MonitorHandle> { - let mut is_offline = public_ip_unreachable(&route_manager).await?; + let mut is_offline = public_ip_unreachable(&route_manager, fwmark).await?; let mut listener = route_manager .change_listener() @@ -54,6 +56,7 @@ pub async fn spawn_monitor( let sender = Arc::downgrade(¬ify_tx); let monitor_handle = MonitorHandle { route_manager: route_manager.clone(), + fwmark, _notify_tx: notify_tx, }; @@ -61,7 +64,7 @@ pub async fn spawn_monitor( while let Some(_event) = listener.next().await { match sender.upgrade() { Some(sender) => { - let new_offline_state = public_ip_unreachable(&route_manager) + let new_offline_state = public_ip_unreachable(&route_manager, fwmark) .await .unwrap_or_else(|err| { log::error!( @@ -83,14 +86,14 @@ pub async fn spawn_monitor( Ok(monitor_handle) } -async fn public_ip_unreachable(handle: &RouteManagerHandle) -> Result<bool> { +async fn public_ip_unreachable(handle: &RouteManagerHandle, fwmark: Option<u32>) -> Result<bool> { Ok(handle - .get_destination_route(PUBLIC_INTERNET_ADDRESS_V4, true) + .get_destination_route(PUBLIC_INTERNET_ADDRESS_V4, fwmark) .await .map_err(Error::RouteManagerError)? .is_none() && handle - .get_destination_route(PUBLIC_INTERNET_ADDRESS_V6, true) + .get_destination_route(PUBLIC_INTERNET_ADDRESS_V6, fwmark) .await .unwrap_or(None) .is_none()) diff --git a/talpid-core/src/offline/macos.rs b/talpid-core/src/offline/macos.rs index 20e2145e4f..baae3c40f1 100644 --- a/talpid-core/src/offline/macos.rs +++ b/talpid-core/src/offline/macos.rs @@ -27,7 +27,7 @@ use talpid_types::ErrorExt; #[derive(err_derive::Error, Debug)] pub enum Error { #[error(display = "Failed to initialize route monitor")] - StartMonitorError(#[error(source)] crate::routing::PlatformError), + StartMonitorError(#[error(source)] talpid_routing::PlatformError), } pub struct MonitorHandle { @@ -43,7 +43,7 @@ impl MonitorHandle { } async fn exists_non_tunnel_default_route() -> bool { - match crate::routing::get_default_routes().await { + match talpid_routing::get_default_routes().await { Ok((Some(node), _)) | Ok((None, Some(node))) => { let route_exists = node .get_device() @@ -85,7 +85,7 @@ pub async fn spawn_monitor(notify_tx: UnboundedSender<bool>) -> Result<MonitorHa fn watch_route_monitor( mut context: OfflineStateContext, ) -> Result<impl Future<Output = ()>, Error> { - let mut monitor = crate::routing::listen_for_default_route_changes()?; + let mut monitor = talpid_routing::listen_for_default_route_changes()?; Ok(async move { while let Some(_route_change) = monitor.next().await { diff --git a/talpid-core/src/offline/mod.rs b/talpid-core/src/offline/mod.rs index 3c5448762b..a227b5e102 100644 --- a/talpid-core/src/offline/mod.rs +++ b/talpid-core/src/offline/mod.rs @@ -1,8 +1,8 @@ -#[cfg(any(target_os = "linux", target_os = "windows"))] -use crate::routing::RouteManagerHandle; #[cfg(target_os = "windows")] -use crate::windows::window::PowerManagementListener; +use crate::window::PowerManagementListener; use futures::channel::mpsc::UnboundedSender; +#[cfg(any(target_os = "linux", target_os = "windows"))] +use talpid_routing::RouteManagerHandle; #[cfg(target_os = "android")] use talpid_types::android::AndroidContext; @@ -44,22 +44,22 @@ impl MonitorHandle { pub async fn spawn_monitor( sender: UnboundedSender<bool>, - #[cfg(target_os = "linux")] route_manager: RouteManagerHandle, + #[cfg(any(target_os = "linux", target_os = "windows"))] route_manager: RouteManagerHandle, + #[cfg(target_os = "linux")] fwmark: Option<u32>, #[cfg(target_os = "android")] android_context: AndroidContext, - #[cfg(target_os = "windows")] route_manager: RouteManagerHandle, #[cfg(target_os = "windows")] power_mgmt_rx: PowerManagementListener, ) -> Result<MonitorHandle, Error> { let monitor = if !*FORCE_DISABLE_OFFLINE_MONITOR { Some( imp::spawn_monitor( sender, - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "windows", target_os = "linux"))] route_manager, + #[cfg(target_os = "linux")] + fwmark, #[cfg(target_os = "android")] android_context, #[cfg(target_os = "windows")] - route_manager, - #[cfg(target_os = "windows")] power_mgmt_rx, ) .await?, diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs index 9dc341d03a..0709caa4ea 100644 --- a/talpid-core/src/offline/windows.rs +++ b/talpid-core/src/offline/windows.rs @@ -1,10 +1,6 @@ -use crate::{ - routing::{get_best_default_route, CallbackHandle, EventType, RouteManagerHandle}, - windows::{ - window::{PowerManagementEvent, PowerManagementListener}, - AddressFamily, - }, -}; +use talpid_routing::{get_best_default_route, CallbackHandle, EventType, RouteManagerHandle}; + +use crate::window::{PowerManagementEvent, PowerManagementListener}; use futures::channel::mpsc::UnboundedSender; use parking_lot::Mutex; use std::{ @@ -13,13 +9,14 @@ use std::{ time::Duration, }; use talpid_types::ErrorExt; +use talpid_windows_net::AddressFamily; #[derive(err_derive::Error, Debug)] pub enum Error { #[error(display = "Unable to create listener thread")] ThreadCreationError(#[error(source)] io::Error), #[error(display = "Failed to start connectivity monitor")] - ConnectivityMonitorError(#[error(source)] crate::routing::Error), + ConnectivityMonitorError(#[error(source)] talpid_routing::Error), } pub struct BroadcastListener { @@ -125,7 +122,7 @@ impl BroadcastListener { family: AddressFamily, state_lock: &Arc<Mutex<SystemState>>, ) { - use crate::routing::EventType::*; + use talpid_routing::EventType::*; if matches!(event_type, UpdatedDetails(_)) { // ignore changes that don't affect the route diff --git a/talpid-core/src/split_tunnel/windows/driver.rs b/talpid-core/src/split_tunnel/windows/driver.rs index b03d8b72af..2eb9aecd30 100644 --- a/talpid-core/src/split_tunnel/windows/driver.rs +++ b/talpid-core/src/split_tunnel/windows/driver.rs @@ -2,7 +2,6 @@ use super::windows::{ get_device_path, get_process_creation_time, get_process_device_path, open_process, Event, Overlapped, ProcessAccess, ProcessSnapshot, }; -use crate::windows::as_uninit_byte_slice; use bitflags::bitflags; use memoffset::offset_of; use std::{ @@ -994,3 +993,8 @@ fn write_string_to_buffer(buffer: &mut [MaybeUninit<u8>], byte_offset: usize, st buffer[byte_offset + i] = MaybeUninit::new(byte); } } + +/// Casts a struct to a slice of possibly uninitialized bytes. +pub fn as_uninit_byte_slice<T: Copy + Sized>(value: &T) -> &[mem::MaybeUninit<u8>] { + unsafe { std::slice::from_raw_parts(value as *const _ as *const _, mem::size_of::<T>()) } +} diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs index 49028319a0..f10184ae74 100644 --- a/talpid-core/src/split_tunnel/windows/mod.rs +++ b/talpid-core/src/split_tunnel/windows/mod.rs @@ -5,14 +5,9 @@ mod volume_monitor; mod windows; use crate::{ - routing::{self, get_best_default_route, CallbackHandle, EventType, RouteManagerHandle}, tunnel::TunnelMetadata, tunnel_state_machine::TunnelCommand, - windows::{ - get_ip_address_for_interface, - window::{PowerManagementEvent, PowerManagementListener}, - AddressFamily, - }, + window::{PowerManagementEvent, PowerManagementListener}, }; use futures::channel::{mpsc, oneshot}; use std::{ @@ -28,7 +23,9 @@ use std::{ }, time::Duration, }; +use talpid_routing::{get_best_default_route, CallbackHandle, EventType, RouteManagerHandle}; use talpid_types::{tunnel::ErrorStateCause, ErrorExt}; +use talpid_windows_net::{get_ip_address_for_interface, AddressFamily}; use windows_sys::Win32::Foundation::ERROR_OPERATION_ABORTED; const DRIVER_EVENT_BUFFER_SIZE: usize = 2048; @@ -72,11 +69,11 @@ pub enum Error { /// Failed to obtain default route #[error(display = "Failed to obtain the default route")] - ObtainDefaultRoute(#[error(source)] routing::Error), + ObtainDefaultRoute(#[error(source)] talpid_routing::Error), /// Failed to obtain an IP address given a network interface LUID #[error(display = "Failed to obtain IP address for interface LUID")] - LuidToIp(#[error(source)] crate::windows::Error), + LuidToIp(#[error(source)] talpid_windows_net::Error), /// Failed to set up callback for monitoring default route changes #[error(display = "Failed to register default route change callback")] @@ -854,7 +851,7 @@ fn split_tunnel_default_route_change_handler<'a>( address_family: AddressFamily, ctx_mutex: &Arc<Mutex<SplitTunnelDefaultRouteChangeHandlerContext>>, ) { - use crate::routing::EventType::*; + use talpid_routing::EventType::*; // Update the "internet interface" IP when best default route changes let mut ctx = ctx_mutex.lock().expect("ST route handler mutex poisoned"); diff --git a/talpid-core/src/split_tunnel/windows/volume_monitor.rs b/talpid-core/src/split_tunnel/windows/volume_monitor.rs index 0758463399..34ba36089e 100644 --- a/talpid-core/src/split_tunnel/windows/volume_monitor.rs +++ b/talpid-core/src/split_tunnel/windows/volume_monitor.rs @@ -1,7 +1,7 @@ //! Used to monitor volume mounts and dismounts, and reapply the split //! tunnel config if any of the excluded paths are affected by them. use super::path_monitor::PathMonitorHandle; -use crate::windows::window::{create_hidden_window, WindowCloseHandle}; +use crate::window::{create_hidden_window, WindowCloseHandle}; use futures::{channel::mpsc, StreamExt}; use std::{ ffi::OsString, diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 4425c3c69d..a8d5792d5b 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -1,30 +1,18 @@ -use self::tun_provider::TunProvider; -use crate::{logging, routing::RouteManagerHandle}; -use futures::{channel::oneshot, future::BoxFuture}; -use std::{ - net::{IpAddr, Ipv4Addr, Ipv6Addr}, - path::{Path, PathBuf}, - sync::{Arc, Mutex}, -}; +use crate::logging; #[cfg(not(target_os = "android"))] -use talpid_types::net::openvpn as openvpn_types; -use talpid_types::net::{wireguard as wireguard_types, AllowedTunnelTraffic, TunnelParameters}; - -#[cfg(target_os = "android")] -pub use self::tun_provider::TunConfig; - -#[cfg(target_os = "windows")] -mod windows; - -/// A module for all OpenVPN related tunnel management. +use futures::channel::oneshot; +use std::path; +#[cfg(not(target_os = "android"))] +use talpid_openvpn; +#[cfg(any(target_os = "linux", target_os = "windows"))] +use talpid_routing::RouteManagerHandle; +pub use talpid_tunnel::{TunnelArgs, TunnelEvent, TunnelMetadata}; #[cfg(not(target_os = "android"))] -pub mod openvpn; +use talpid_types::net::openvpn as openvpn_types; +use talpid_types::net::{wireguard as wireguard_types, TunnelParameters}; /// A module for all WireGuard related tunnel management. -pub mod wireguard; - -/// A module for low level platform specific tunnel device management. -pub(crate) mod tun_provider; +use talpid_wireguard; const OPENVPN_LOG_FILENAME: &str = "openvpn.log"; const WIREGUARD_LOG_FILENAME: &str = "wireguard.log"; @@ -42,7 +30,7 @@ pub enum Error { /// Failure in Windows syscall. #[cfg(windows)] #[error(display = "Failure in Windows syscall")] - WinnetError(#[error(source)] crate::routing::Error), + WinnetError(#[error(source)] talpid_routing::Error), /// Running on an operating system which is not supported yet. #[error(display = "Tunnel type not supported on this operating system")] @@ -54,75 +42,27 @@ pub enum Error { /// Failure to build Wireguard configuration. #[error(display = "Failed to configure Wireguard with the given parameters")] - WireguardConfigError(#[error(source)] self::wireguard::config::Error), + WireguardConfigError(#[error(source)] talpid_wireguard::config::Error), /// There was an error listening for events from the OpenVPN tunnel #[cfg(not(target_os = "android"))] #[error(display = "Failed while listening for events from the OpenVPN tunnel")] - OpenVpnTunnelMonitoringError(#[error(source)] openvpn::Error), + OpenVpnTunnelMonitoringError(#[error(source)] talpid_openvpn::Error), /// There was an error listening for events from the Wireguard tunnel #[error(display = "Failed while listening for events from the Wireguard tunnel")] - WireguardTunnelMonitoringError(#[error(source)] wireguard::Error), + WireguardTunnelMonitoringError(#[error(source)] talpid_wireguard::Error), /// Could not detect and assign the correct mtu #[error(display = "Could not detect and assign a correct MTU for the Wireguard tunnel")] AssignMtuError, } -/// Possible events from the VPN tunnel and the child process managing it. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum TunnelEvent { - /// Sent when the tunnel fails to connect due to an authentication error. - AuthFailed(Option<String>), - /// Sent when the tunnel interface has been created, before routes are set up. - InterfaceUp(TunnelMetadata, AllowedTunnelTraffic), - /// Sent when the tunnel comes up and is ready for traffic. - Up(TunnelMetadata), - /// Sent when the tunnel goes down. - Down, -} - -/// Information about a VPN tunnel. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct TunnelMetadata { - /// The name of the device which the tunnel is running on. - pub interface: String, - /// The local IPs on the tunnel interface. - pub ips: Vec<IpAddr>, - /// The IP to the default gateway on the tunnel interface. - pub ipv4_gateway: Ipv4Addr, - /// The IP to the IPv6 default gateway on the tunnel interface. - pub ipv6_gateway: Option<Ipv6Addr>, -} - /// Abstraction for monitoring a generic VPN tunnel. pub struct TunnelMonitor { monitor: InternalTunnelMonitor, } -/// Arguments for creating a tunnel. -pub struct TunnelArgs<'a, L> -where - // L: (Fn(TunnelEvent) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>) - L: (Fn(TunnelEvent) -> BoxFuture<'static, ()>) + Send + Clone + Sync + 'static, -{ - /// Toktio runtime handle. - pub runtime: tokio::runtime::Handle, - /// Resource directory path. - pub resource_dir: &'a Path, - /// Callback function called when an event happens. - pub on_event: L, - /// Receiver oneshot channel for closing the tunnel. - pub tunnel_close_rx: oneshot::Receiver<()>, - /// Mutex to tunnel provider. - pub tun_provider: Arc<Mutex<TunProvider>>, - /// Connection retry attempts. - pub retry_attempt: u32, - /// Route manager handle. - pub route_manager: RouteManagerHandle, -} - // TODO(emilsp) move most of the openvpn tunnel details to OpenVpnTunnelMonitor impl TunnelMonitor { /// Creates a new `TunnelMonitor` that connects to the given remote and notifies `on_event` @@ -130,7 +70,7 @@ impl TunnelMonitor { #[cfg_attr(any(target_os = "android", windows), allow(unused_variables))] pub fn start<L>( tunnel_parameters: &mut TunnelParameters, - log_dir: &Option<PathBuf>, + log_dir: &Option<path::PathBuf>, args: TunnelArgs<'_, L>, ) -> Result<Self> where @@ -165,7 +105,7 @@ impl TunnelMonitor { /// Returns a path to an executable that communicates with relay servers. #[cfg(windows)] - pub fn get_relay_client(resource_dir: &Path, params: &TunnelParameters) -> PathBuf { + pub fn get_relay_client(resource_dir: &path::Path, params: &TunnelParameters) -> path::PathBuf { let resource_dir = resource_dir.to_path_buf(); let process_string = match params { TunnelParameters::OpenVpn(params) => { @@ -187,7 +127,7 @@ impl TunnelMonitor { fn start_wireguard_tunnel<L>( params: &mut wireguard_types::TunnelParameters, - log: Option<PathBuf>, + log: Option<path::PathBuf>, args: TunnelArgs<'_, L>, ) -> Result<Self> where @@ -200,8 +140,8 @@ impl TunnelMonitor { #[cfg(any(target_os = "linux", target_os = "windows"))] args.runtime .block_on(Self::assign_mtu(&args.route_manager, params)); - let config = wireguard::config::Config::from_parameters(params)?; - let monitor = wireguard::WireguardMonitor::start( + let config = talpid_wireguard::config::Config::from_parameters(params)?; + let monitor = talpid_wireguard::WireguardMonitor::start( config, if params.options.use_pq_safe_psk { Some( @@ -280,8 +220,8 @@ impl TunnelMonitor { #[cfg(not(target_os = "android"))] async fn start_openvpn_tunnel<L>( config: &openvpn_types::TunnelParameters, - log: Option<PathBuf>, - resource_dir: &Path, + log: Option<path::PathBuf>, + resource_dir: &path::Path, on_event: L, tunnel_close_rx: oneshot::Receiver<()>, #[cfg(target_os = "linux")] route_manager: RouteManagerHandle, @@ -292,7 +232,7 @@ impl TunnelMonitor { + Sync + 'static, { - let monitor = openvpn::OpenVpnMonitor::start( + let monitor = talpid_openvpn::OpenVpnMonitor::start( on_event, config, log, @@ -323,8 +263,8 @@ impl TunnelMonitor { #[cfg(not(target_os = "windows"))] fn prepare_tunnel_log_file( parameters: &TunnelParameters, - log_dir: &Option<PathBuf>, - ) -> Result<Option<PathBuf>> { + log_dir: &Option<path::PathBuf>, + ) -> Result<Option<path::PathBuf>> { if let Some(ref log_dir) = log_dir { match parameters { TunnelParameters::OpenVpn(_) => { @@ -342,8 +282,8 @@ impl TunnelMonitor { #[cfg(target_os = "windows")] fn prepare_tunnel_log_file( parameters: &TunnelParameters, - log_dir: &Option<PathBuf>, - ) -> Result<Option<PathBuf>> { + log_dir: &Option<path::PathBuf>, + ) -> Result<Option<path::PathBuf>> { if let Some(ref log_dir) = log_dir { let filename = match parameters { TunnelParameters::OpenVpn(_) => OPENVPN_LOG_FILENAME, @@ -365,8 +305,8 @@ impl TunnelMonitor { enum InternalTunnelMonitor { #[cfg(not(target_os = "android"))] - OpenVpn(openvpn::OpenVpnMonitor), - Wireguard(wireguard::WireguardMonitor), + OpenVpn(talpid_openvpn::OpenVpnMonitor), + Wireguard(talpid_wireguard::WireguardMonitor), } impl InternalTunnelMonitor { diff --git a/talpid-core/src/tunnel/tun_provider/unix.rs b/talpid-core/src/tunnel/tun_provider/unix.rs deleted file mode 100644 index 5c48a3c663..0000000000 --- a/talpid-core/src/tunnel/tun_provider/unix.rs +++ /dev/null @@ -1,69 +0,0 @@ -use super::TunConfig; -use crate::network_interface::{self, NetworkInterface, TunnelDevice}; -use std::{net::IpAddr, ops::Deref}; - -/// Errors that can occur while setting up a tunnel device. -#[derive(Debug, err_derive::Error)] -#[error(no_from)] -pub enum Error { - /// Failure to create a tunnel device. - #[error(display = "Failed to create a tunnel device")] - CreateTunnelDevice(#[cause] network_interface::Error), - - /// Failure to set a tunnel device IP address. - #[error(display = "Failed to set tunnel IP address: {}", _0)] - SetIpAddr(IpAddr, #[cause] network_interface::Error), - - /// Failure to set the tunnel device as up. - #[error(display = "Failed to set the tunnel device as up")] - SetUp(#[cause] network_interface::Error), -} - -/// Factory of tunnel devices on Unix systems. -pub struct UnixTunProvider; - -impl Default for UnixTunProvider { - fn default() -> Self { - Self::new() - } -} - -impl UnixTunProvider { - pub fn new() -> Self { - UnixTunProvider - } - - pub fn get_tun(&mut self, config: TunConfig) -> Result<UnixTun, Error> { - let mut tunnel_device = TunnelDevice::new().map_err(Error::CreateTunnelDevice)?; - - for ip in config.addresses.iter() { - tunnel_device - .set_ip(*ip) - .map_err(|cause| Error::SetIpAddr(*ip, cause))?; - } - - tunnel_device.set_up(true).map_err(Error::SetUp)?; - - Ok(UnixTun(tunnel_device)) - } -} - -/// Generic tunnel device. -/// -/// Contains the file descriptor representing the device. -pub struct UnixTun(TunnelDevice); - -impl UnixTun { - /// Retrieve the tunnel interface name. - pub fn interface_name(&self) -> &str { - self.get_name() - } -} - -impl Deref for UnixTun { - type Target = TunnelDevice; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 964963d46e..cdf9029b81 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -5,10 +5,7 @@ use super::{ }; use crate::{ firewall::FirewallPolicy, - routing::RouteManager, - tunnel::{ - self, tun_provider::TunProvider, TunnelArgs, TunnelEvent, TunnelMetadata, TunnelMonitor, - }, + tunnel::{self, TunnelMonitor}, }; use cfg_if::cfg_if; use futures::{ @@ -22,17 +19,16 @@ use std::{ thread, time::{Duration, Instant}, }; +use talpid_routing::RouteManager; +use talpid_tunnel::{tun_provider::TunProvider, TunnelArgs, TunnelEvent, TunnelMetadata}; use talpid_types::{ net::{AllowedTunnelTraffic, TunnelParameters}, tunnel::{ErrorStateCause, FirewallPolicyError}, ErrorExt, }; -#[cfg(windows)] -use crate::routing; - #[cfg(target_os = "android")] -use crate::tunnel::tun_provider; +use talpid_tunnel::tun_provider; use super::connected_state::TunnelEventsReceiver; @@ -175,16 +171,16 @@ impl ConnectingState { tunnel::Error::EnableIpv6Error => ErrorStateCause::Ipv6Unavailable, #[cfg(target_os = "android")] tunnel::Error::WireguardTunnelMonitoringError( - tunnel::wireguard::Error::TunnelError( - tunnel::wireguard::TunnelError::SetupTunnelDeviceError( + talpid_wireguard::Error::TunnelError( + talpid_wireguard::TunnelError::SetupTunnelDeviceError( tun_provider::Error::PermissionDenied, ), ), ) => ErrorStateCause::VpnPermissionDenied, #[cfg(target_os = "android")] tunnel::Error::WireguardTunnelMonitoringError( - tunnel::wireguard::Error::TunnelError( - tunnel::wireguard::TunnelError::SetupTunnelDeviceError( + talpid_wireguard::Error::TunnelError( + talpid_wireguard::TunnelError::SetupTunnelDeviceError( tun_provider::Error::InvalidDnsServers(addresses), ), ), @@ -227,7 +223,7 @@ impl ConnectingState { Ok(_) => None, Err(error) => match error { tunnel::Error::WireguardTunnelMonitoringError( - tunnel::wireguard::Error::TimeoutError, + talpid_wireguard::Error::TimeoutError, ) => { log::debug!("WireGuard tunnel timed out"); None @@ -481,9 +477,7 @@ impl ConnectingState { #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] fn should_retry(error: &tunnel::Error, retry_attempt: u32) -> bool { - #[cfg(windows)] - use tunnel::openvpn; - use tunnel::wireguard::{Error, TunnelError}; + use talpid_wireguard::{Error, TunnelError}; match error { tunnel::Error::WireguardTunnelMonitoringError(Error::CreateObfuscatorError(_)) => true, @@ -513,18 +507,18 @@ fn should_retry(error: &tunnel::Error, retry_attempt: u32) -> bool { )) if retry_attempt < MAX_ADAPTER_FAIL_RETRIES => true, #[cfg(windows)] - tunnel::Error::OpenVpnTunnelMonitoringError(openvpn::Error::WintunCreateAdapterError( - _, - )) if retry_attempt < MAX_ADAPTER_FAIL_RETRIES => true, + tunnel::Error::OpenVpnTunnelMonitoringError( + talpid_openvpn::Error::WintunCreateAdapterError(_), + ) if retry_attempt < MAX_ADAPTER_FAIL_RETRIES => true, _ => false, } } #[cfg(windows)] -fn is_recoverable_routing_error(error: &crate::routing::Error) -> bool { +fn is_recoverable_routing_error(error: &talpid_routing::Error) -> bool { match error { - routing::Error::AddRoutesFailed(_) => true, + talpid_routing::Error::AddRoutesFailed(_) => true, _ => false, } } diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index 5d13b7d1f2..b802dd6c54 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -18,11 +18,11 @@ use crate::{ firewall::{Firewall, FirewallArguments, InitialFirewallState}, mpsc::Sender, offline, - routing::RouteManager, - tunnel::{tun_provider::TunProvider, TunnelEvent}, }; #[cfg(windows)] use std::ffi::OsString; +use talpid_routing::RouteManager; +use talpid_tunnel::{tun_provider::TunProvider, TunnelEvent}; use futures::{ channel::{mpsc, oneshot}, @@ -71,7 +71,7 @@ pub enum Error { /// Failed to initialize the route manager. #[error(display = "Failed to initialize the route manager")] - InitRouteManagerError(#[error(source)] crate::routing::Error), + InitRouteManagerError(#[error(source)] talpid_routing::Error), /// Failed to initialize filtering resolver #[cfg(target_os = "macos")] @@ -116,6 +116,8 @@ pub async fn spawn( #[cfg(target_os = "windows")] volume_update_rx: mpsc::UnboundedReceiver<()>, #[cfg(target_os = "macos")] exclusion_gid: u32, #[cfg(target_os = "android")] android_context: AndroidContext, + #[cfg(target_os = "linux")] fwmark: u32, + #[cfg(target_os = "linux")] table_id: u32, ) -> Result<TunnelStateMachineHandle, Error> { let (command_tx, command_rx) = mpsc::unbounded(); let command_tx = Arc::new(command_tx); @@ -127,6 +129,12 @@ pub async fn spawn( initial_settings.allow_lan, #[cfg(target_os = "android")] initial_settings.dns_servers.clone(), + #[cfg(target_os = "android")] + crate::firewall::ALLOWED_LAN_NETS + .iter() + .chain(crate::firewall::ALLOWED_LAN_MULTICAST_NETS.iter()) + .cloned() + .collect(), ); let (shutdown_tx, shutdown_rx) = oneshot::channel(); @@ -148,6 +156,10 @@ pub async fn spawn( exclusion_gid, #[cfg(target_os = "android")] android_context, + #[cfg(target_os = "linux")] + fwmark, + #[cfg(target_os = "linux")] + table_id, }; let state_machine = TunnelStateMachine::new(init_args).await?; @@ -237,6 +249,10 @@ struct TunnelStateMachineInitArgs<G: TunnelParametersGenerator> { exclusion_gid: u32, #[cfg(target_os = "android")] android_context: AndroidContext, + #[cfg(target_os = "linux")] + fwmark: u32, + #[cfg(target_os = "linux")] + table_id: u32, } impl TunnelStateMachine { @@ -256,11 +272,17 @@ impl TunnelStateMachine { let filtering_resolver = crate::resolver::start_resolver().await?; #[cfg(target_os = "windows")] - let power_mgmt_rx = crate::windows::window::PowerManagementListener::new(); + let power_mgmt_rx = crate::window::PowerManagementListener::new(); - let route_manager = RouteManager::new(HashSet::new()) - .await - .map_err(Error::InitRouteManagerError)?; + let route_manager = RouteManager::new( + HashSet::new(), + #[cfg(target_os = "linux")] + args.fwmark, + #[cfg(target_os = "linux")] + args.table_id, + ) + .await + .map_err(Error::InitRouteManagerError)?; #[cfg(windows)] let split_tunnel = split_tunnel::SplitTunnel::new( @@ -283,9 +305,12 @@ impl TunnelStateMachine { InitialFirewallState::None }, allow_lan: args.settings.allow_lan, + #[cfg(target_os = "linux")] + fwmark: args.fwmark, }; let firewall = Firewall::from_args(fw_args).map_err(Error::InitFirewallError)?; + let dns_monitor = DnsMonitor::new( #[cfg(target_os = "linux")] runtime.clone(), @@ -316,6 +341,8 @@ impl TunnelStateMachine { route_manager .handle() .map_err(Error::InitRouteManagerError)?, + #[cfg(target_os = "linux")] + Some(args.fwmark), #[cfg(target_os = "android")] android_context, #[cfg(target_os = "windows")] diff --git a/talpid-core/src/windows/window.rs b/talpid-core/src/window.rs index dd009f42ef..d3ba502dde 100644 --- a/talpid-core/src/windows/window.rs +++ b/talpid-core/src/window.rs @@ -1,4 +1,4 @@ -//! Utilities for windows. +//! Utilities for working with windows on Windows. use std::{os::windows::io::AsRawHandle, ptr, sync::Arc, thread}; use tokio::sync::broadcast; diff --git a/talpid-dbus/src/network_manager.rs b/talpid-dbus/src/network_manager.rs index a04870d615..40cdf0a656 100644 --- a/talpid-dbus/src/network_manager.rs +++ b/talpid-dbus/src/network_manager.rs @@ -393,6 +393,12 @@ impl NetworkManager { } } + pub fn ensure_can_be_used_to_manage_dns(&self) -> Result<()> { + self.ensure_resolv_conf_is_managed()?; + self.ensure_network_manager_exists()?; + self.nm_version_dns_works()?; + Ok(()) + } pub fn ensure_resolv_conf_is_managed(&self) -> Result<()> { // check if NM is set to manage resolv.conf let management_mode: String = self diff --git a/talpid-openvpn/Cargo.toml b/talpid-openvpn/Cargo.toml new file mode 100644 index 0000000000..1e701506a6 --- /dev/null +++ b/talpid-openvpn/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "talpid-openvpn" +version = "0.0.0" +authors = ["Mullvad VPN"] +description = "Library for creating OpenVPN tunnels" +license = "GPL-3.0" +edition = "2021" +publish = false + + +[dependencies] +bitflags = "1.2" +async-trait = "0.1" +atty = "0.2" +cfg-if = "1.0" +duct = "0.13" +err-derive = "0.3.1" +futures = "0.3.15" +lazy_static = "1.0" +log = "0.4" +os_pipe = "0.9" +parking_lot = "0.11" +shell-escape = "0.1" +talpid-routing = { path = "../talpid-routing" } +talpid-tunnel = { path = "../talpid-tunnel" } +talpid-types = { path = "../talpid-types" } +uuid = { version = "0.8", features = ["v4"] } +tokio = { version = "1.8", features = ["process", "rt-multi-thread", "fs"] } +shadowsocks-service = { version = "1.14.3", default-features = false, features = ["local", "stream-cipher"] } + +[target.'cfg(not(target_os="android"))'.dependencies] +byteorder = "1" +socket2 = { version = "0.4.2", features = ["all"] } +parity-tokio-ipc = "0.9" +triggered = "0.1.1" +tonic = "0.8" +prost = "0.11" + +[target.'cfg(target_os = "linux")'.dependencies] +which = { version = "4.0", default-features = false } + +[target.'cfg(windows)'.dependencies] +widestring = "0.5" +winreg = { version = "0.7", features = ["transactions"] } +winapi = { version = "0.3.6", features = ["ws2def"] } +talpid-windows-net = { path = "../talpid-windows-net" } + +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.42.0" +features = [ + "Win32_Foundation", + "Win32_System_LibraryLoader", + "Win32_System_Registry", + "Win32_NetworkManagement_Ndis", +] + +[build-dependencies] +tonic-build = { version = "0.8", default-features = false, features = ["transport", "prost"] } + + +[dev-dependencies] +tokio = { version = "1", features = [ "test-util" ] } diff --git a/talpid-openvpn/build.rs b/talpid-openvpn/build.rs new file mode 100644 index 0000000000..2029098f5e --- /dev/null +++ b/talpid-openvpn/build.rs @@ -0,0 +1,9 @@ +fn main() { + generate_grpc_code(); +} + +fn generate_grpc_code() { + const PROTO_FILE: &str = "../talpid-openvpn-plugin/proto/openvpn_plugin.proto"; + tonic_build::compile_protos(PROTO_FILE).unwrap(); + println!("cargo:rerun-if-changed={}", PROTO_FILE); +} diff --git a/talpid-core/src/tunnel/openvpn/mod.rs b/talpid-openvpn/src/lib.rs index 439427c8e6..6ea22792aa 100644 --- a/talpid-core/src/tunnel/openvpn/mod.rs +++ b/talpid-openvpn/src/lib.rs @@ -1,17 +1,16 @@ -use super::TunnelEvent; -#[cfg(target_os = "linux")] -use crate::routing::{self, RequiredRoute}; -use crate::{ - mktemp, - process::{ - openvpn::{OpenVpnCommand, OpenVpnProcHandle}, - stoppable_process::StoppableProcess, - }, - proxy::{self, ProxyMonitor, ProxyResourceData}, -}; +//! Manage OpenVPN tunnels. + +#![deny(missing_docs)] +#![deny(rust_2018_idioms)] + +use crate::proxy::{ProxyMonitor, ProxyResourceData}; use futures::channel::oneshot; #[cfg(windows)] use lazy_static::lazy_static; +use process::{ + openvpn::{OpenVpnCommand, OpenVpnProcHandle}, + stoppable_process::StoppableProcess, +}; #[cfg(target_os = "linux")] use std::collections::{HashMap, HashSet}; #[cfg(windows)] @@ -28,10 +27,12 @@ use std::{ thread, time::Duration, }; +#[cfg(target_os = "linux")] +use talpid_routing::{self, RequiredRoute}; +use talpid_tunnel::TunnelEvent; use talpid_types::{net::openvpn, ErrorExt}; use tokio::task; -#[cfg(target_os = "linux")] -use which; + #[cfg(windows)] use widestring::U16CString; #[cfg(windows)] @@ -40,6 +41,10 @@ use windows_sys::{core::GUID, Win32::NetworkManagement::Ndis::NET_LUID_LH}; #[cfg(windows)] mod wintun; +mod mktemp; +mod process; +mod proxy; + #[cfg(windows)] lazy_static! { static ref ADAPTER_ALIAS: U16CString = U16CString::from_str("Mullvad").unwrap(); @@ -228,7 +233,7 @@ impl WintunContext for WintunContextImpl { async fn wait_for_interfaces(&self) -> io::Result<()> { let luid = self.adapter.luid(); - crate::windows::wait_for_interfaces(luid, true, self.wait_v6_interface).await + talpid_windows_net::wait_for_interfaces(luid, true, self.wait_v6_interface).await } fn prepare_interface(&self) { @@ -252,7 +257,7 @@ impl OpenVpnMonitor<OpenVpnCommand> { log_path: Option<PathBuf>, resource_dir: &Path, tunnel_close_rx: oneshot::Receiver<()>, - #[cfg(target_os = "linux")] route_manager: routing::RouteManagerHandle, + #[cfg(target_os = "linux")] route_manager: talpid_routing::RouteManagerHandle, ) -> Result<Self> where L: (Fn(TunnelEvent) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>) @@ -358,7 +363,7 @@ impl OpenVpnMonitor<OpenVpnCommand> { #[cfg(target_os = "linux")] fn extract_routes(env: &HashMap<String, String>) -> Result<HashSet<RequiredRoute>> { let tun_interface = env.get("dev").ok_or(Error::MissingTunnelInterface)?; - let tun_node = routing::Node::device(tun_interface.to_string()); + let tun_node = talpid_routing::Node::device(tun_interface.to_string()); let mut routes = HashSet::new(); for network in &["0.0.0.0/0".parse().unwrap(), "::/0".parse().unwrap()] { routes.insert(RequiredRoute::new(*network, tun_node.clone())); @@ -806,7 +811,6 @@ impl ProcessHandle for OpenVpnProcHandle { } mod event_server { - use crate::tunnel::TunnelMetadata; use futures::stream::TryStreamExt; use parity_tokio_ipc::Endpoint as IpcEndpoint; use std::{ @@ -814,6 +818,7 @@ mod event_server { pin::Pin, task::{Context, Poll}, }; + use talpid_tunnel::TunnelMetadata; #[cfg(any(target_os = "linux", windows))] use talpid_types::ErrorExt; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; @@ -846,7 +851,7 @@ mod event_server { /// Implements a gRPC service used to process events sent to by OpenVPN. pub struct OpenvpnEventProxyImpl< L: (Fn( - super::TunnelEvent, + talpid_tunnel::TunnelEvent, ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>) + Send + Sync @@ -857,14 +862,14 @@ mod event_server { pub proxy_auth_file_path: Option<super::PathBuf>, pub abort_server_tx: triggered::Trigger, #[cfg(target_os = "linux")] - pub route_manager_handle: super::routing::RouteManagerHandle, + pub route_manager_handle: talpid_routing::RouteManagerHandle, #[cfg(target_os = "linux")] pub ipv6_enabled: bool, } impl< L: (Fn( - super::TunnelEvent, + talpid_tunnel::TunnelEvent, ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>) + Send @@ -877,7 +882,7 @@ mod event_server { request: Request<EventDetails>, ) -> std::result::Result<Response<()>, tonic::Status> { let env = request.into_inner().env; - (self.on_event)(super::TunnelEvent::InterfaceUp( + (self.on_event)(talpid_tunnel::TunnelEvent::InterfaceUp( Self::get_tunnel_metadata(&env)?, talpid_types::net::AllowedTunnelTraffic::All, )) @@ -925,11 +930,11 @@ mod event_server { #[cfg(windows)] { let tunnel_device = metadata.interface.clone(); - let luid = crate::windows::luid_from_alias(tunnel_device).map_err(|error| { + let luid = talpid_windows_net::luid_from_alias(tunnel_device).map_err(|error| { log::error!("{}", error.display_chain_with_msg("luid_from_alias failed")); tonic::Status::unavailable("failed to obtain interface luid") })?; - crate::windows::wait_for_addresses(luid) + talpid_windows_net::wait_for_addresses(luid) .await .map_err(|error| { log::error!( @@ -940,7 +945,7 @@ mod event_server { })?; } - (self.on_event)(super::TunnelEvent::Up(metadata)).await; + (self.on_event)(talpid_tunnel::TunnelEvent::Up(metadata)).await; Ok(Response::new(())) } @@ -996,7 +1001,7 @@ mod event_server { #[tonic::async_trait] impl< L: (Fn( - super::TunnelEvent, + talpid_tunnel::TunnelEvent, ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>) + Send @@ -1009,7 +1014,7 @@ mod event_server { request: Request<EventDetails>, ) -> std::result::Result<Response<()>, tonic::Status> { let env = request.into_inner().env; - (self.on_event)(super::TunnelEvent::AuthFailed( + (self.on_event)(talpid_tunnel::TunnelEvent::AuthFailed( env.get("auth_failed_reason").cloned(), )) .await; @@ -1040,7 +1045,7 @@ mod event_server { &self, _request: Request<EventDetails>, ) -> std::result::Result<Response<()>, tonic::Status> { - (self.on_event)(super::TunnelEvent::Down).await; + (self.on_event)(talpid_tunnel::TunnelEvent::Down).await; Ok(Response::new(())) } } diff --git a/talpid-core/src/mktemp.rs b/talpid-openvpn/src/mktemp.rs index 9e5709ce1f..9e5709ce1f 100644 --- a/talpid-core/src/mktemp.rs +++ b/talpid-openvpn/src/mktemp.rs diff --git a/talpid-core/src/process/mod.rs b/talpid-openvpn/src/process/mod.rs index 54cdeb9042..54cdeb9042 100644 --- a/talpid-core/src/process/mod.rs +++ b/talpid-openvpn/src/process/mod.rs diff --git a/talpid-core/src/process/openvpn.rs b/talpid-openvpn/src/process/openvpn.rs index 3d2f5668a0..4b8932ee13 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-openvpn/src/process/openvpn.rs @@ -73,6 +73,8 @@ pub struct OpenVpnCommand { tunnel_alias: Option<OsString>, enable_ipv6: bool, proxy_port: Option<u16>, + #[cfg(target_os = "linux")] + fwmark: Option<u32>, } impl OpenVpnCommand { @@ -95,9 +97,18 @@ impl OpenVpnCommand { tunnel_alias: None, enable_ipv6: true, proxy_port: None, + #[cfg(target_os = "linux")] + fwmark: None, } } + /// Sets what the firewall mark should be + #[cfg(target_os = "linux")] + pub fn fwmark(&mut self, fwmark: Option<u32>) -> &mut Self { + self.fwmark = fwmark; + self + } + /// Sets what configuration file will be given to OpenVPN pub fn config(&mut self, path: impl AsRef<Path>) -> &mut Self { self.config = Some(path.as_ref().to_path_buf()); @@ -253,11 +264,9 @@ impl OpenVpnCommand { args.extend(self.proxy_arguments().iter().map(OsString::from)); #[cfg(target_os = "linux")] - args.extend( - ["--mark", &crate::linux::TUNNEL_FW_MARK.to_string()] - .iter() - .map(OsString::from), - ); + if let Some(mark) = &self.fwmark { + args.extend(["--mark", &mark.to_string()].iter().map(OsString::from)); + } args } diff --git a/talpid-core/src/process/stoppable_process.rs b/talpid-openvpn/src/process/stoppable_process.rs index 3681c6cfa8..3681c6cfa8 100644 --- a/talpid-core/src/process/stoppable_process.rs +++ b/talpid-openvpn/src/process/stoppable_process.rs diff --git a/talpid-core/src/proxy/mod.rs b/talpid-openvpn/src/proxy/mod.rs index dd0e0b6cc9..dd0e0b6cc9 100644 --- a/talpid-core/src/proxy/mod.rs +++ b/talpid-openvpn/src/proxy/mod.rs diff --git a/talpid-core/src/proxy/noop.rs b/talpid-openvpn/src/proxy/noop.rs index 9eb96e0c95..9eb96e0c95 100644 --- a/talpid-core/src/proxy/noop.rs +++ b/talpid-openvpn/src/proxy/noop.rs diff --git a/talpid-core/src/proxy/shadowsocks.rs b/talpid-openvpn/src/proxy/shadowsocks.rs index 54efbe1205..b9c9dad407 100644 --- a/talpid-core/src/proxy/shadowsocks.rs +++ b/talpid-openvpn/src/proxy/shadowsocks.rs @@ -73,7 +73,7 @@ impl ShadowsocksProxyMonitor { #[cfg(target_os = "linux")] { - config.outbound_fwmark = Some(crate::linux::TUNNEL_FW_MARK); + config.outbound_fwmark = settings.fwmark; } let srv = local::create(config).await?; diff --git a/talpid-core/src/tunnel/openvpn/wintun.rs b/talpid-openvpn/src/wintun.rs index 92a670aa66..1e98aa08f8 100644 --- a/talpid-core/src/tunnel/openvpn/wintun.rs +++ b/talpid-openvpn/src/wintun.rs @@ -1,4 +1,3 @@ -use crate::windows::string_from_guid; use lazy_static::lazy_static; use std::{ ffi::CStr, @@ -16,6 +15,7 @@ use windows_sys::{ Foundation::{HINSTANCE, NO_ERROR}, NetworkManagement::{IpHelper::ConvertInterfaceLuidToGuid, Ndis::NET_LUID_LH}, System::{ + Com::StringFromGUID2, LibraryLoader::{ FreeLibrary, GetProcAddress, LoadLibraryExW, LOAD_WITH_ALTERED_SEARCH_PATH, }, @@ -103,7 +103,9 @@ impl WintunAdapter { } pub fn prepare_interface(&self) { - if let Err(error) = crate::tunnel::windows::initialize_interfaces(self.luid(), None) { + if let Err(error) = + talpid_tunnel::network_interface::initialize_interfaces(self.luid(), None) + { log::error!( "{}", error.display_chain_with_msg("Failed to set tunnel interface metric"), @@ -355,6 +357,17 @@ fn find_adapter_registry_key(find_guid: &str, permissions: REG_SAM_FLAGS) -> io: Err(io::Error::new(io::ErrorKind::NotFound, "device not found")) } +/// Obtain a string representation for a GUID object. +fn string_from_guid(guid: &GUID) -> String { + let mut buffer = [0u16; 40]; + let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) } + as usize; + // cannot fail because `buffer` is large enough + assert!(length > 0); + let length = length - 1; + String::from_utf16(&buffer[0..length]).unwrap() +} + #[cfg(test)] mod tests { use super::*; diff --git a/talpid-routing/Cargo.toml b/talpid-routing/Cargo.toml new file mode 100644 index 0000000000..e248f4e345 --- /dev/null +++ b/talpid-routing/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "talpid-routing" +version = "0.0.0" +authors = ["Mullvad VPN"] +description = "Library for managing routing tables" +license = "GPL-3.0" +edition = "2021" +publish = false + + +[dependencies] +err-derive = "0.3.1" +futures = "0.3.15" +ipnetwork = "0.16" +log = "0.4" +talpid-types = { path = "../talpid-types" } +tokio = { version = "1.8", features = ["process", "rt-multi-thread"] } + + +[target.'cfg(target_os = "android")'.dependencies] +jnix = { version = "0.5", features = ["derive"] } + + +[target.'cfg(target_os = "linux")'.dependencies] +libc = "0.2" +lazy_static = "1.0" +rtnetlink = "0.11" +netlink-packet-route = "0.13" +netlink-sys = "0.8.3" + +[target.'cfg(target_os = "macos")'.dependencies] +tokio-stream = { version = "0.1", features = ["io-util"] } + + +[target.'cfg(windows)'.dependencies] +libc = "0.2" +socket2 = { version = "0.4.2", features = ["all"] } +talpid-windows-net = { path = "../talpid-windows-net" } +widestring = "1.0" +winapi = { version = "0.3.6", features = ["ws2def"] } +windows-sys = { version = "0.42.0", features = [ + "Win32_NetworkManagement_Ndis", + "Win32_Globalization" +]} + +[dev-dependencies] +tokio = { version = "1", features = [ "test-util" ] } diff --git a/talpid-core/src/routing/android.rs b/talpid-routing/src/android.rs index 78f5c14a90..332e2ca093 100644 --- a/talpid-core/src/routing/android.rs +++ b/talpid-routing/src/android.rs @@ -1,5 +1,4 @@ -use crate::routing::{imp::RouteManagerCommand, RequiredRoute}; -// use futures01::{stream::Stream, sync::mpsc}; +use crate::{imp::RouteManagerCommand, RequiredRoute}; use futures::{channel::mpsc, stream::StreamExt}; use std::collections::HashSet; diff --git a/talpid-core/src/routing/mod.rs b/talpid-routing/src/lib.rs index 5d1247618e..06a8a09d18 100644 --- a/talpid-core/src/routing/mod.rs +++ b/talpid-routing/src/lib.rs @@ -1,5 +1,7 @@ -#![cfg_attr(target_os = "android", allow(dead_code))] -#![cfg_attr(target_os = "windows", allow(dead_code))] +//! Manage routing tables on various platforms. + +#![deny(missing_docs)] +#![deny(rust_2018_idioms)] use ipnetwork::IpNetwork; use std::{fmt, net::IpAddr}; @@ -18,7 +20,7 @@ mod imp; use netlink_packet_route::rtnl::constants::RT_TABLE_MAIN; #[cfg(target_os = "macos")] -pub(crate) use imp::{get_default_routes, listen_for_default_route_changes, PlatformError}; +pub use imp::{get_default_routes, listen_for_default_route_changes, PlatformError}; pub use imp::{Error, RouteManager}; @@ -35,7 +37,8 @@ pub struct Route { } impl Route { - fn new(node: Node, prefix: IpNetwork) -> Self { + /// Construct a new Route + pub fn new(node: Node, prefix: IpNetwork) -> Self { Self { node, prefix, @@ -88,7 +91,7 @@ impl RequiredRoute { node: node.into(), prefix, #[cfg(target_os = "linux")] - table_id: crate::linux::TUNNEL_TABLE_ID, + table_id: u32::from(RT_TABLE_MAIN), } } diff --git a/talpid-core/src/routing/linux.rs b/talpid-routing/src/linux.rs index 1443f87453..edd2893ff3 100644 --- a/talpid-core/src/routing/linux.rs +++ b/talpid-routing/src/linux.rs @@ -1,4 +1,4 @@ -use crate::routing::{ +use crate::{ imp::{CallbackMessage, RouteManagerCommand}, NetNode, Node, RequiredRoute, Route, }; @@ -17,6 +17,7 @@ use futures::{ }; use ipnetwork::IpNetwork; use lazy_static::lazy_static; +use libc::{AF_INET, AF_INET6}; use netlink_packet_route::{ constants::{ARPHRD_LOOPBACK, FIB_RULE_INVERT, FR_ACT_TO_TBL, NLM_F_REQUEST}, link::{nlas::Nla as LinkNla, LinkMessage}, @@ -37,8 +38,6 @@ use rtnetlink::{ Handle, IpVersion, }; -use libc::{AF_INET, AF_INET6}; - lazy_static! { static ref SUPPRESS_RULE_V4: RuleMessage = RuleMessage { header: RuleHeader { @@ -56,29 +55,33 @@ lazy_static! { v6_rule.header.family = AF_INET6 as u8; v6_rule }; - static ref NO_FWMARK_RULE_V4: RuleMessage = RuleMessage { +} + +fn all_rules(fwmark: u32, table: u32) -> [RuleMessage; 4] { + [ + no_fwmark_rule_v4(fwmark, table), + no_fwmark_rule_v6(fwmark, table), + SUPPRESS_RULE_V4.clone(), + SUPPRESS_RULE_V6.clone(), + ] +} + +fn no_fwmark_rule_v4(fwmark: u32, table: u32) -> RuleMessage { + RuleMessage { header: RuleHeader { family: AF_INET as u8, action: FR_ACT_TO_TBL, flags: FIB_RULE_INVERT, ..RuleHeader::default() }, - nlas: vec![ - RuleNla::FwMark(crate::linux::TUNNEL_FW_MARK), - RuleNla::Table(crate::linux::TUNNEL_TABLE_ID), - ], - }; - static ref NO_FWMARK_RULE_V6: RuleMessage = { - let mut v6_rule = NO_FWMARK_RULE_V4.clone(); - v6_rule.header.family = AF_INET6 as u8; - v6_rule - }; - static ref ALL_RULES: [&'static RuleMessage; 4] = [ - &*NO_FWMARK_RULE_V4, - &*NO_FWMARK_RULE_V6, - &*SUPPRESS_RULE_V4, - &*SUPPRESS_RULE_V6, - ]; + nlas: vec![RuleNla::FwMark(fwmark), RuleNla::Table(table)], + } +} + +fn no_fwmark_rule_v6(fwmark: u32, table: u32) -> RuleMessage { + let mut v6_rule = no_fwmark_rule_v4(fwmark, table); + v6_rule.header.family = AF_INET6 as u8; + v6_rule } pub type Result<T> = std::result::Result<T, Error>; @@ -136,10 +139,20 @@ pub struct RouteManagerImpl { // currently added routes added_routes: HashSet<Route>, + + /// Tunnel specific routing table, traffic not marked will be routed via this routing table. + table_id: u32, + /// Firewall mark identifies traffic which shouldn't be routed via the tunnel routing table. It + /// is used to construct a routing rule. + fwmark: u32, } impl RouteManagerImpl { - pub async fn new(required_routes: HashSet<RequiredRoute>) -> Result<Self> { + pub async fn new( + required_routes: HashSet<RequiredRoute>, + table_id: u32, + fwmark: u32, + ) -> Result<Self> { let (mut connection, handle, messages) = rtnetlink::new_connection().map_err(Error::Connect)?; @@ -161,6 +174,8 @@ impl RouteManagerImpl { iface_map, listeners: vec![], added_routes: HashSet::new(), + table_id, + fwmark, }; monitor.clear_routing_rules().await?; @@ -174,7 +189,7 @@ impl RouteManagerImpl { self.clear_routing_rules().await?; - for rule in ALL_RULES + for rule in all_rules(self.fwmark, self.table_id) .iter() .filter(|rule| rule.header.family as u16 == AF_INET || enable_ipv6) { @@ -194,7 +209,7 @@ impl RouteManagerImpl { async fn clear_routing_rules(&mut self) -> Result<()> { let rules = self.get_rules().await?; - for rule in &*ALL_RULES { + for rule in all_rules(self.fwmark, self.table_id) { let mut matching_rule = None; // `RTM_DELRULE` is way too picky about which rules are considered the same. @@ -368,8 +383,8 @@ impl RouteManagerImpl { RouteManagerCommand::NewChangeListener(result_tx) => { let _ = result_tx.send(self.listen()); } - RouteManagerCommand::GetDestinationRoute(destination, set_mark, result_tx) => { - let _ = result_tx.send(self.get_destination_route(&destination, set_mark).await); + RouteManagerCommand::GetDestinationRoute(destination, mark, result_tx) => { + let _ = result_tx.send(self.get_destination_route(&destination, mark).await); } RouteManagerCommand::GetMtuForRoute(ip, result_tx) => { let _ = result_tx.send(self.get_mtu_for_route(ip).await); @@ -735,7 +750,9 @@ impl RouteManagerImpl { const STANDARD_MTU: u16 = 1500; let mut attempted_ip = ip; for _ in 0..RECURSION_LIMIT { - let route = self.get_destination_route(&attempted_ip, false).await?; + let route = self + .get_destination_route(&attempted_ip, Some(self.fwmark)) + .await?; match route { Some(route) => { let node = route.get_node(); @@ -792,7 +809,7 @@ impl RouteManagerImpl { async fn get_destination_route( &self, destination: &IpAddr, - set_mark: bool, + fwmark: Option<u32>, ) -> Result<Option<Route>> { let mut request = self.handle.route().get(get_ip_version(destination)); let octets = match destination { @@ -800,10 +817,8 @@ impl RouteManagerImpl { IpAddr::V6(address) => address.octets().to_vec(), }; let message = request.message_mut(); - if set_mark { - message - .nlas - .push(RouteNla::Mark(crate::linux::TUNNEL_FW_MARK)); + if let Some(mark) = fwmark { + message.nlas.push(RouteNla::Mark(mark)); } message.header.destination_prefix_length = 8u8 * (octets.len() as u8); message.header.flags = RouteFlags::RTM_F_FIB_MATCH; @@ -891,7 +906,7 @@ mod test { fn test_drop_in_executor() { let runtime = tokio::runtime::Runtime::new().expect("Failed to initialize runtime"); runtime.block_on(async { - let manager = RouteManagerImpl::new(HashSet::new()) + let manager = RouteManagerImpl::new(HashSet::new(), 0, 0) .await .expect("Failed to initialize route manager"); std::mem::drop(manager); @@ -903,7 +918,7 @@ mod test { fn test_drop() { let runtime = tokio::runtime::Runtime::new().expect("Failed to initialize runtime"); let manager = runtime.block_on(async { - RouteManagerImpl::new(HashSet::new()) + RouteManagerImpl::new(HashSet::new(), 1000, 1000) .await .expect("Failed to initialize route manager") }); diff --git a/talpid-core/src/routing/macos.rs b/talpid-routing/src/macos.rs index f191551f42..568daf94f7 100644 --- a/talpid-core/src/routing/macos.rs +++ b/talpid-routing/src/macos.rs @@ -1,4 +1,4 @@ -use crate::routing::{imp::RouteManagerCommand, NetNode, Node, RequiredRoute, Route}; +use crate::{imp::RouteManagerCommand, NetNode, Node, RequiredRoute, Route}; use futures::{ channel::mpsc, @@ -300,8 +300,7 @@ fn ip_vers(prefix: IpNetwork) -> &'static str { /// Returns a stream that produces an item whenever a default route is either added or deleted from /// the routing table. -pub(crate) fn listen_for_default_route_changes() -> Result<impl Stream<Item = std::io::Result<()>>> -{ +pub fn listen_for_default_route_changes() -> Result<impl Stream<Item = std::io::Result<()>>> { let mut cmd = Command::new("route"); cmd.arg("-n") .arg("monitor") diff --git a/talpid-core/src/routing/unix.rs b/talpid-routing/src/unix.rs index 326fb1fad1..2a117b9e07 100644 --- a/talpid-core/src/routing/unix.rs +++ b/talpid-routing/src/unix.rs @@ -24,7 +24,7 @@ use std::net::IpAddr; #[path = "macos.rs"] mod imp; #[cfg(target_os = "macos")] -pub(crate) use imp::listen_for_default_route_changes; +pub use imp::listen_for_default_route_changes; #[allow(clippy::module_inception)] #[cfg(target_os = "linux")] @@ -121,13 +121,13 @@ impl RouteManagerHandle { pub async fn get_destination_route( &self, destination: IpAddr, - set_mark: bool, + mark: Option<Fwmark>, ) -> Result<Option<Route>, Error> { let (response_tx, response_rx) = oneshot::channel(); self.tx .unbounded_send(RouteManagerCommand::GetDestinationRoute( destination, - set_mark, + mark, response_tx, )) .map_err(|_| Error::RouteManagerDown)?; @@ -151,6 +151,10 @@ impl RouteManagerHandle { } } +/// Represents a firewall mark. +#[cfg(target_os = "linux")] +type Fwmark = u32; + /// Commands for the underlying route manager object. #[derive(Debug)] pub(crate) enum RouteManagerCommand { @@ -168,10 +172,11 @@ pub(crate) enum RouteManagerCommand { NewChangeListener(oneshot::Sender<mpsc::UnboundedReceiver<CallbackMessage>>), #[cfg(target_os = "linux")] GetMtuForRoute(IpAddr, oneshot::Sender<Result<u16, PlatformError>>), + /// Attempt to fetch a route for the given destination with an optional firewall mark. #[cfg(target_os = "linux")] GetDestinationRoute( IpAddr, - bool, + Option<Fwmark>, oneshot::Sender<Result<Option<Route>, PlatformError>>, ), } @@ -195,9 +200,20 @@ impl RouteManager { /// Constructs a RouteManager and applies the required routes. /// Takes a set of network destinations and network nodes as an argument, and applies said /// routes. - pub async fn new(required_routes: HashSet<RequiredRoute>) -> Result<Self, Error> { + pub async fn new( + required_routes: HashSet<RequiredRoute>, + #[cfg(target_os = "linux")] fwmark: u32, + #[cfg(target_os = "linux")] table_id: u32, + ) -> Result<Self, Error> { let (manage_tx, manage_rx) = mpsc::unbounded(); - let manager = imp::RouteManagerImpl::new(required_routes).await?; + let manager = imp::RouteManagerImpl::new( + required_routes, + #[cfg(target_os = "linux")] + fwmark, + #[cfg(target_os = "linux")] + table_id, + ) + .await?; tokio::spawn(manager.run(manage_rx)); Ok(Self { @@ -288,8 +304,7 @@ impl Drop for RouteManager { /// Returns a tuple containing a IPv4 and IPv6 default route nodes. #[cfg(target_os = "macos")] -pub(crate) async fn get_default_routes() -> Result<(Option<super::Node>, Option<super::Node>), Error> -{ +pub async fn get_default_routes() -> Result<(Option<super::Node>, Option<super::Node>), Error> { use futures::TryFutureExt; futures::try_join!( imp::RouteManagerImpl::get_default_node(IpVersion::V4).map_err(Into::into), diff --git a/talpid-core/src/routing/windows/default_route_monitor.rs b/talpid-routing/src/windows/default_route_monitor.rs index 3976903f11..3675417062 100644 --- a/talpid-core/src/routing/windows/default_route_monitor.rs +++ b/talpid-routing/src/windows/default_route_monitor.rs @@ -1,6 +1,6 @@ use super::{ - get_best_default_route, get_best_default_route::route_has_gateway, AddressFamily, Error, - InterfaceAndGateway, Result, + get_best_default_route, get_best_default_route::route_has_gateway, Error, InterfaceAndGateway, + Result, }; use std::{ @@ -24,6 +24,8 @@ use windows_sys::Win32::{ }, }; +use talpid_windows_net::AddressFamily; + const WIN_FALSE: BOOLEAN = 0; struct DefaultRouteMonitorContext { diff --git a/talpid-core/src/routing/windows/get_best_default_route.rs b/talpid-routing/src/windows/get_best_default_route.rs index 4ec7395fff..940fd93644 100644 --- a/talpid-core/src/routing/windows/get_best_default_route.rs +++ b/talpid-routing/src/windows/get_best_default_route.rs @@ -1,6 +1,8 @@ use super::{Error, Result}; -use crate::windows::{get_ip_interface_entry, try_socketaddr_from_inet_sockaddr, AddressFamily}; use std::{convert::TryInto, io, net::SocketAddr}; +use talpid_windows_net::{ + get_ip_interface_entry, try_socketaddr_from_inet_sockaddr, AddressFamily, +}; use widestring::{widecstr, WideCStr}; use windows_sys::Win32::{ Foundation::NO_ERROR, diff --git a/talpid-core/src/routing/windows/mod.rs b/talpid-routing/src/windows/mod.rs index 06d23368ca..7c0e2bd739 100644 --- a/talpid-core/src/routing/windows/mod.rs +++ b/talpid-routing/src/windows/mod.rs @@ -1,12 +1,18 @@ -use crate::{routing::RequiredRoute, windows::AddressFamily}; -use futures::channel::oneshot; -use std::{collections::HashSet, io, net::IpAddr}; -use talpid_types::ErrorExt; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; - +use crate::RequiredRoute; pub use default_route_monitor::EventType; +use futures::{ + channel::{ + mpsc::{self, UnboundedReceiver, UnboundedSender}, + oneshot, + }, + StreamExt, +}; pub use get_best_default_route::{get_best_default_route, route_has_gateway, InterfaceAndGateway}; +use net::AddressFamily; pub use route_manager::{Callback, CallbackHandle, Route, RouteManagerInternal}; +use std::{collections::HashSet, io, net::IpAddr}; +use talpid_types::ErrorExt; +use talpid_windows_net as net; mod default_route_monitor; mod get_best_default_route; @@ -106,7 +112,7 @@ impl RouteManagerHandle { ) -> Result<CallbackHandle> { let (response_tx, response_rx) = oneshot::channel(); self.tx - .send(RouteManagerCommand::RegisterDefaultRouteChangeCallback( + .unbounded_send(RouteManagerCommand::RegisterDefaultRouteChangeCallback( callback, response_tx, )) @@ -118,7 +124,7 @@ impl RouteManagerHandle { pub async fn add_routes(&self, routes: HashSet<RequiredRoute>) -> Result<()> { let (response_tx, response_rx) = oneshot::channel(); self.tx - .send(RouteManagerCommand::AddRoutes(routes, response_tx)) + .unbounded_send(RouteManagerCommand::AddRoutes(routes, response_tx)) .map_err(|_| Error::RouteManagerDown)?; response_rx.await.map_err(|_| Error::ManagerChannelDown)? } @@ -127,7 +133,7 @@ impl RouteManagerHandle { pub async fn get_mtu_for_route(&self, ip: IpAddr) -> Result<u16> { let (response_tx, response_rx) = oneshot::channel(); self.tx - .send(RouteManagerCommand::GetMtuForRoute(ip, response_tx)) + .unbounded_send(RouteManagerCommand::GetMtuForRoute(ip, response_tx)) .map_err(|_| Error::RouteManagerDown)?; response_rx.await.map_err(|_| Error::ManagerChannelDown)? } @@ -149,7 +155,7 @@ impl RouteManager { Ok(internal) => internal, Err(_) => return Err(Error::FailedToStartManager), }; - let (manage_tx, manage_rx) = mpsc::unbounded_channel(); + let (manage_tx, manage_rx) = mpsc::unbounded(); let manager = Self { manage_tx: Some(manage_tx), }; @@ -167,7 +173,7 @@ impl RouteManager { if let Some(tx) = &self.manage_tx { let (result_tx, result_rx) = oneshot::channel(); if tx - .send(RouteManagerCommand::RegisterDefaultRouteChangeCallback( + .unbounded_send(RouteManagerCommand::RegisterDefaultRouteChangeCallback( callback, result_tx, )) .is_err() @@ -193,7 +199,7 @@ impl RouteManager { mut manage_rx: UnboundedReceiver<RouteManagerCommand>, mut internal: RouteManagerInternal, ) { - while let Some(command) = manage_rx.recv().await { + while let Some(command) = manage_rx.next().await { match command { RouteManagerCommand::AddRoutes(routes, tx) => { let routes: Vec<_> = routes @@ -242,7 +248,7 @@ impl RouteManager { /// can be added pub fn stop(&mut self) { if let Some(tx) = self.manage_tx.take() { - if tx.send(RouteManagerCommand::Shutdown).is_err() { + if tx.unbounded_send(RouteManagerCommand::Shutdown).is_err() { log::error!("RouteManager channel already down or thread panicked"); } } @@ -253,7 +259,7 @@ impl RouteManager { if let Some(tx) = &self.manage_tx { let (result_tx, result_rx) = oneshot::channel(); if tx - .send(RouteManagerCommand::AddRoutes(routes, result_tx)) + .unbounded_send(RouteManagerCommand::AddRoutes(routes, result_tx)) .is_err() { return Err(Error::RouteManagerDown); @@ -268,7 +274,7 @@ impl RouteManager { /// [`RouteManager::add_routes`]. pub fn clear_routes(&self) -> Result<()> { if let Some(tx) = &self.manage_tx { - tx.send(RouteManagerCommand::ClearRoutes) + tx.unbounded_send(RouteManagerCommand::ClearRoutes) .map_err(|_| Error::RouteManagerDown) } else { Err(Error::RouteManagerDown) @@ -279,11 +285,13 @@ impl RouteManager { fn get_mtu_for_route(addr_family: AddressFamily) -> Result<Option<u16>> { match get_best_default_route(addr_family) { Ok(Some(route)) => { - let interface_row = crate::windows::get_ip_interface_entry(addr_family, &route.iface) - .map_err(|e| { - log::error!("Could not get ip interface entry: {}", e); - Error::GetMtu - })?; + let interface_row = + talpid_windows_net::get_ip_interface_entry(addr_family, &route.iface).map_err( + |e| { + log::error!("Could not get ip interface entry: {}", e); + Error::GetMtu + }, + )?; let mtu = interface_row.NlMtu; let mtu = u16::try_from(mtu).map_err(|_| Error::GetMtu)?; Ok(Some(mtu)) diff --git a/talpid-core/src/routing/windows/route_manager.rs b/talpid-routing/src/windows/route_manager.rs index f1d878dd28..ad35d05f77 100644 --- a/talpid-core/src/routing/windows/route_manager.rs +++ b/talpid-routing/src/windows/route_manager.rs @@ -2,10 +2,7 @@ use super::{ default_route_monitor::{DefaultRouteMonitor, EventType as RouteMonitorEventType}, get_best_default_route, Error, InterfaceAndGateway, Result, }; -use crate::{ - routing::NetNode, - windows::{inet_sockaddr_from_socketaddr, try_socketaddr_from_inet_sockaddr, AddressFamily}, -}; +use crate::NetNode; use ipnetwork::IpNetwork; use std::{ collections::HashMap, @@ -13,6 +10,9 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, sync::{Arc, Mutex}, }; +use talpid_windows_net::{ + inet_sockaddr_from_socketaddr, try_socketaddr_from_inet_sockaddr, AddressFamily, +}; use widestring::{WideCStr, WideCString}; use windows_sys::Win32::{ Foundation::{ @@ -862,7 +862,7 @@ impl<'a> Iterator for AdaptersIterator<'a> { pub fn win_ip_address_prefix_from_ipnetwork_port_zero(from: IpNetwork) -> IP_ADDRESS_PREFIX { // Port should not matter so we set it to 0 let prefix = - crate::windows::inet_sockaddr_from_socketaddr(std::net::SocketAddr::new(from.ip(), 0)); + talpid_windows_net::inet_sockaddr_from_socketaddr(std::net::SocketAddr::new(from.ip(), 0)); IP_ADDRESS_PREFIX { Prefix: prefix, PrefixLength: from.prefix(), @@ -872,7 +872,7 @@ pub fn win_ip_address_prefix_from_ipnetwork_port_zero(from: IpNetwork) -> IP_ADD /// Convert to a windows defined `SOCKADDR_INET` from a `IpAddr` but set the port to 0 pub fn inet_sockaddr_from_ipaddr(from: IpAddr) -> SOCKADDR_INET { // Port should not matter so we set it to 0 - crate::windows::inet_sockaddr_from_socketaddr(std::net::SocketAddr::new(from, 0)) + talpid_windows_net::inet_sockaddr_from_socketaddr(std::net::SocketAddr::new(from, 0)) } /// Convert to a `AddressFamily` from a `ipnetwork::IpNetwork` diff --git a/talpid-tunnel/Cargo.toml b/talpid-tunnel/Cargo.toml new file mode 100644 index 0000000000..cb5285fd1e --- /dev/null +++ b/talpid-tunnel/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "talpid-tunnel" +version = "0.0.0" +authors = ["Mullvad VPN"] +description = "Library for creating tunnel devices and interfacing with various VPN tunnels" +license = "GPL-3.0" +edition = "2021" +publish = false + +[dependencies] +err-derive = "0.3.1" +cfg-if = "1.0" +ipnetwork = "0.16" +talpid-routing = { path = "../talpid-routing" } +talpid-types = { path = "../talpid-types" } +futures = "0.3.15" +tokio = { version = "1.8", features = ["process", "rt-multi-thread", "fs"] } + +[target.'cfg(unix)'.dependencies] +duct = "0.13" +nix = "0.23" + +[target.'cfg(target_os = "android")'.dependencies] +jnix = { version = "0.5", features = ["derive"] } +log = "0.4" + +[target.'cfg(target_os = "linux")'.dependencies] +tun = "0.5.1" + +[target.'cfg(target_os = "macos")'.dependencies] +tun = "0.5.1" + +[target.'cfg(windows)'.dependencies] +talpid-windows-net = { path = "../talpid-windows-net" } + +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.42.0" +features = [ + "Win32_Foundation", + "Win32_Networking_WinSock", + "Win32_NetworkManagement_Ndis", +] diff --git a/talpid-tunnel/src/lib.rs b/talpid-tunnel/src/lib.rs new file mode 100644 index 0000000000..fc964ae82e --- /dev/null +++ b/talpid-tunnel/src/lib.rs @@ -0,0 +1,62 @@ +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + path::Path, + sync::{Arc, Mutex}, +}; + +#[cfg(windows)] +#[path = "windows.rs"] +pub mod network_interface; + +pub mod tun_provider; +use futures::{channel::oneshot, future::BoxFuture}; +use talpid_routing::RouteManagerHandle; +use talpid_types::net::AllowedTunnelTraffic; +use tun_provider::TunProvider; + +/// Arguments for creating a tunnel. +pub struct TunnelArgs<'a, L> +where + L: (Fn(TunnelEvent) -> BoxFuture<'static, ()>) + Send + Clone + Sync + 'static, +{ + /// Toktio runtime handle. + pub runtime: tokio::runtime::Handle, + /// Resource directory path. + pub resource_dir: &'a Path, + /// Callback function called when an event happens. + pub on_event: L, + /// Receiver oneshot channel for closing the tunnel. + pub tunnel_close_rx: oneshot::Receiver<()>, + /// Mutex to tunnel provider. + pub tun_provider: Arc<Mutex<TunProvider>>, + /// Connection retry attempts. + pub retry_attempt: u32, + /// Route manager handle. + pub route_manager: RouteManagerHandle, +} + +/// Information about a VPN tunnel. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct TunnelMetadata { + /// The name of the device which the tunnel is running on. + pub interface: String, + /// The local IPs on the tunnel interface. + pub ips: Vec<IpAddr>, + /// The IP to the default gateway on the tunnel interface. + pub ipv4_gateway: Ipv4Addr, + /// The IP to the IPv6 default gateway on the tunnel interface. + pub ipv6_gateway: Option<Ipv6Addr>, +} + +/// Possible events from the VPN tunnel and the child process managing it. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum TunnelEvent { + /// Sent when the tunnel fails to connect due to an authentication error. + AuthFailed(Option<String>), + /// Sent when the tunnel interface has been created, before routes are set up. + InterfaceUp(TunnelMetadata, AllowedTunnelTraffic), + /// Sent when the tunnel comes up and is ready for traffic. + Up(TunnelMetadata), + /// Sent when the tunnel goes down. + Down, +} diff --git a/talpid-core/src/tunnel/tun_provider/android/ipnetwork_sub.rs b/talpid-tunnel/src/tun_provider/android/ipnetwork_sub.rs index ffaa585302..ffaa585302 100644 --- a/talpid-core/src/tunnel/tun_provider/android/ipnetwork_sub.rs +++ b/talpid-tunnel/src/tun_provider/android/ipnetwork_sub.rs diff --git a/talpid-core/src/tunnel/tun_provider/android/mod.rs b/talpid-tunnel/src/tun_provider/android/mod.rs index b7ab5665d2..1cb6a49de4 100644 --- a/talpid-core/src/tunnel/tun_provider/android/mod.rs +++ b/talpid-tunnel/src/tun_provider/android/mod.rs @@ -65,6 +65,7 @@ pub struct AndroidTunProvider { last_tun_config: TunConfig, allow_lan: bool, custom_dns_servers: Option<Vec<IpAddr>>, + allowed_lan_networks: Vec<IpNetwork>, } impl AndroidTunProvider { @@ -73,6 +74,7 @@ impl AndroidTunProvider { context: AndroidContext, allow_lan: bool, custom_dns_servers: Option<Vec<IpAddr>>, + allowed_lan_networks: Vec<IpNetwork>, ) -> Self { let env = JnixEnv::from( context @@ -89,6 +91,7 @@ impl AndroidTunProvider { last_tun_config: TunConfig::default(), allow_lan, custom_dns_servers, + allowed_lan_networks, } } @@ -234,12 +237,11 @@ impl AndroidTunProvider { .cloned() .partition::<Vec<_>, _>(|route| route.is_ipv4()); - let (original_lan_ipv4_networks, original_lan_ipv6_networks) = - crate::firewall::ALLOWED_LAN_NETS - .iter() - .chain(crate::firewall::ALLOWED_LAN_MULTICAST_NETS.iter()) - .cloned() - .partition::<Vec<_>, _>(|network| network.is_ipv4()); + let (original_lan_ipv4_networks, original_lan_ipv6_networks) = self + .allowed_lan_networks + .iter() + .cloned() + .partition::<Vec<_>, _>(|network| network.is_ipv4()); let lan_ipv4_networks = original_lan_ipv4_networks .into_iter() diff --git a/talpid-core/src/tunnel/tun_provider/mod.rs b/talpid-tunnel/src/tun_provider/mod.rs index 9ac1e14895..9ac1e14895 100644 --- a/talpid-core/src/tunnel/tun_provider/mod.rs +++ b/talpid-tunnel/src/tun_provider/mod.rs diff --git a/talpid-core/src/tunnel/tun_provider/stub.rs b/talpid-tunnel/src/tun_provider/stub.rs index ff659210cf..ff659210cf 100644 --- a/talpid-core/src/tunnel/tun_provider/stub.rs +++ b/talpid-tunnel/src/tun_provider/stub.rs diff --git a/talpid-core/src/network_interface.rs b/talpid-tunnel/src/tun_provider/unix.rs index 0e00dd9b9f..106b3353a6 100644 --- a/talpid-core/src/network_interface.rs +++ b/talpid-tunnel/src/tun_provider/unix.rs @@ -1,15 +1,83 @@ +use super::TunConfig; use nix::fcntl; use std::{ io, net::IpAddr, + ops::Deref, os::unix::io::{AsRawFd, IntoRawFd, RawFd}, }; use tun::{platform, Configuration, Device}; +/// Errors that can occur while setting up a tunnel device. +#[derive(Debug, err_derive::Error)] +#[error(no_from)] +pub enum Error { + /// Failure to create a tunnel device. + #[error(display = "Failed to create a tunnel device")] + CreateTunnelDevice(#[cause] NetworkInterfaceError), + + /// Failure to set a tunnel device IP address. + #[error(display = "Failed to set tunnel IP address: {}", _0)] + SetIpAddr(IpAddr, #[cause] NetworkInterfaceError), + + /// Failure to set the tunnel device as up. + #[error(display = "Failed to set the tunnel device as up")] + SetUp(#[cause] NetworkInterfaceError), +} + +/// Factory of tunnel devices on Unix systems. +pub struct UnixTunProvider; + +impl Default for UnixTunProvider { + fn default() -> Self { + Self::new() + } +} + +impl UnixTunProvider { + pub fn new() -> Self { + UnixTunProvider + } + + pub fn get_tun(&mut self, config: TunConfig) -> Result<UnixTun, Error> { + let mut tunnel_device = TunnelDevice::new().map_err(Error::CreateTunnelDevice)?; + + for ip in config.addresses.iter() { + tunnel_device + .set_ip(*ip) + .map_err(|cause| Error::SetIpAddr(*ip, cause))?; + } + + tunnel_device.set_up(true).map_err(Error::SetUp)?; + + Ok(UnixTun(tunnel_device)) + } +} + +/// Generic tunnel device. +/// +/// Contains the file descriptor representing the device. +pub struct UnixTun(TunnelDevice); + +impl UnixTun { + /// Retrieve the tunnel interface name. + pub fn interface_name(&self) -> &str { + self.get_name() + } +} + +impl Deref for UnixTun { + type Target = TunnelDevice; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + /// Errors that can happen when working with *nix tunnel interfaces. #[derive(err_derive::Error, Debug)] #[error(no_from)] -pub enum Error { +pub enum NetworkInterfaceError { /// Failed to set IP address #[error(display = "Failed to set IPv4 address")] SetIpv4Error(#[error(source)] tun::Error), @@ -34,20 +102,18 @@ pub enum Error { /// A trait for managing link devices pub trait NetworkInterface: Sized { /// Bring a given interface up or down - fn set_up(&mut self, up: bool) -> Result<(), Error>; + fn set_up(&mut self, up: bool) -> Result<(), NetworkInterfaceError>; /// Set host IPs for interface - fn set_ip(&mut self, ip: IpAddr) -> Result<(), Error>; + fn set_ip(&mut self, ip: IpAddr) -> Result<(), NetworkInterfaceError>; /// Set MTU for interface - fn set_mtu(&mut self, mtu: u16) -> Result<(), Error>; + fn set_mtu(&mut self, mtu: u16) -> Result<(), NetworkInterfaceError>; /// Get name of interface fn get_name(&self) -> &str; } -trait WireguardLink: AsRawFd + IntoRawFd {} - fn apply_async_flags(fd: RawFd) -> Result<(), nix::Error> { fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFL)?; let arg = fcntl::FcntlArg::F_SETFL(fcntl::OFlag::O_RDWR | fcntl::OFlag::O_NONBLOCK); @@ -55,7 +121,7 @@ fn apply_async_flags(fd: RawFd) -> Result<(), nix::Error> { Ok(()) } -/// A tunnel devie +/// A tunnel device pub struct TunnelDevice { dev: platform::Device, } @@ -63,15 +129,16 @@ pub struct TunnelDevice { impl TunnelDevice { /// Creates a new Tunnel device #[allow(unused_mut)] - pub fn new() -> Result<Self, Error> { + pub fn new() -> Result<Self, NetworkInterfaceError> { let mut config = Configuration::default(); #[cfg(target_os = "linux")] config.platform(|config| { config.packet_information(true); }); - let mut dev = platform::create(&config).map_err(Error::CreateDeviceError)?; - apply_async_flags(dev.as_raw_fd()).map_err(Error::SetDeviceAsyncError)?; + let mut dev = + platform::create(&config).map_err(NetworkInterfaceError::CreateDeviceError)?; + apply_async_flags(dev.as_raw_fd()).map_err(NetworkInterfaceError::SetDeviceAsyncError)?; Ok(Self { dev }) } } @@ -89,9 +156,12 @@ impl IntoRawFd for TunnelDevice { } impl NetworkInterface for TunnelDevice { - fn set_ip(&mut self, ip: IpAddr) -> Result<(), Error> { + fn set_ip(&mut self, ip: IpAddr) -> Result<(), NetworkInterfaceError> { match ip { - IpAddr::V4(ipv4) => self.dev.set_address(ipv4).map_err(Error::SetIpv4Error), + IpAddr::V4(ipv4) => self + .dev + .set_address(ipv4) + .map_err(NetworkInterfaceError::SetIpv4Error), IpAddr::V6(ipv6) => { #[cfg(target_os = "linux")] { @@ -106,7 +176,7 @@ impl NetworkInterface for TunnelDevice { ) .run() .map(|_| ()) - .map_err(Error::SetIpv6Error) + .map_err(NetworkInterfaceError::SetIpv6Error) } #[cfg(target_os = "macos")] { @@ -119,20 +189,22 @@ impl NetworkInterface for TunnelDevice { ) .run() .map(|_| ()) - .map_err(Error::SetIpv6Error) + .map_err(NetworkInterfaceError::SetIpv6Error) } } } } - fn set_up(&mut self, up: bool) -> Result<(), Error> { - self.dev.enabled(up).map_err(Error::ToggleDeviceError) + fn set_up(&mut self, up: bool) -> Result<(), NetworkInterfaceError> { + self.dev + .enabled(up) + .map_err(NetworkInterfaceError::ToggleDeviceError) } - fn set_mtu(&mut self, mtu: u16) -> Result<(), Error> { + fn set_mtu(&mut self, mtu: u16) -> Result<(), NetworkInterfaceError> { self.dev .set_mtu(i32::from(mtu)) - .map_err(Error::ToggleDeviceError) + .map_err(NetworkInterfaceError::ToggleDeviceError) } fn get_name(&self) -> &str { diff --git a/talpid-core/src/tunnel/windows.rs b/talpid-tunnel/src/windows.rs index cb1cea345c..d9c54f6940 100644 --- a/talpid-core/src/tunnel/windows.rs +++ b/talpid-tunnel/src/windows.rs @@ -1,5 +1,5 @@ -use crate::windows::{get_ip_interface_entry, set_ip_interface_entry, AddressFamily}; use std::io; +use talpid_windows_net::{get_ip_interface_entry, set_ip_interface_entry, AddressFamily}; use windows_sys::Win32::{ Foundation::ERROR_NOT_FOUND, NetworkManagement::Ndis::NET_LUID_LH, Networking::WinSock::RouterDiscoveryDisabled, diff --git a/talpid-types/src/net/openvpn.rs b/talpid-types/src/net/openvpn.rs index 97fdf07de2..aaf08103c3 100644 --- a/talpid-types/src/net/openvpn.rs +++ b/talpid-types/src/net/openvpn.rs @@ -117,6 +117,8 @@ pub struct ShadowsocksProxySettings { /// Password on peer. pub password: String, pub cipher: String, + #[cfg(target_os = "linux")] + pub fwmark: Option<u32>, } impl ShadowsocksProxySettings { diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index b5c3268af5..a6acb6fc6f 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -30,6 +30,8 @@ pub struct ConnectionConfig { /// Gateway used by the tunnel (a private address). pub ipv4_gateway: Ipv4Addr, pub ipv6_gateway: Option<Ipv6Addr>, + #[cfg(target_os = "linux")] + pub fwmark: Option<u32>, } impl ConnectionConfig { diff --git a/talpid-windows-net/Cargo.toml b/talpid-windows-net/Cargo.toml new file mode 100644 index 0000000000..044d5efdd9 --- /dev/null +++ b/talpid-windows-net/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "talpid-windows-net" +version = "0.0.0" +authors = ["Mullvad VPN"] +description = "Work with Windows network interfaces and their configuration" +license = "GPL-3.0" +edition = "2021" +publish = false + +[target.'cfg(windows)'.dependencies] +err-derive = "0.3.1" +libc = "0.2" +socket2 = { version = "0.4.2", features = ["all"] } +futures = "0.3.15" +winapi = { version = "0.3.6", features = ["ws2def"] } +windows-sys = { version = "0.42", features = [ + "Win32_Foundation", + "Win32_Globalization", + "Win32_System_Com", + "Win32_System_IO", + "Win32_Networking_WinSock", + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", +]} diff --git a/talpid-windows-net/src/lib.rs b/talpid-windows-net/src/lib.rs new file mode 100644 index 0000000000..48dc8062ee --- /dev/null +++ b/talpid-windows-net/src/lib.rs @@ -0,0 +1,10 @@ +//! Interface with low-level windows specific bits. + +#![deny(missing_docs)] +#![deny(rust_2018_idioms)] + +/// Nicer interfaces with Windows networking code. +#[cfg(windows)] +pub mod net; +#[cfg(windows)] +pub use net::*; diff --git a/talpid-core/src/windows/mod.rs b/talpid-windows-net/src/net.rs index 5504e11d93..6a11cf7018 100644 --- a/talpid-core/src/windows/mod.rs +++ b/talpid-windows-net/src/net.rs @@ -6,17 +6,14 @@ use std::{ mem::{self, MaybeUninit}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, os::windows::ffi::{OsStrExt, OsStringExt}, - path::PathBuf, - ptr, sync::Mutex, time::{Duration, Instant}, }; -use widestring::WideCStr; use winapi::shared::ws2def::SOCKADDR_STORAGE as sockaddr_storage; use windows_sys::{ - core::{GUID, PWSTR}, + core::GUID, Win32::{ - Foundation::{ERROR_NOT_FOUND, HANDLE, NO_ERROR, S_OK}, + Foundation::{ERROR_NOT_FOUND, HANDLE, NO_ERROR}, NetworkManagement::{ IpHelper::{ CancelMibChangeNotify2, ConvertInterfaceAliasToLuid, ConvertInterfaceLuidToAlias, @@ -33,13 +30,9 @@ use windows_sys::{ IpDadStateTentative, AF_INET, AF_INET6, AF_UNSPEC, IN6_ADDR, IN_ADDR, NL_DAD_STATE, SOCKADDR_IN as sockaddr_in, SOCKADDR_IN6 as sockaddr_in6, SOCKADDR_INET, }, - System::Com::{CoTaskMemFree, StringFromGUID2}, - UI::Shell::{FOLDERID_System, SHGetKnownFolderPath}, }, }; -pub mod window; - /// Result type for this module. pub type Result<T> = std::result::Result<T, Error>; @@ -90,20 +83,34 @@ pub enum Error { UnknownAddressFamily(u32), } -/// Address family. These correspond to the `AF_*` constants. -#[derive(Debug, Clone, Copy)] -pub enum AddressFamily { - /// IPv4 address family - Ipv4 = AF_INET as isize, - /// IPv6 address family - Ipv6 = AF_INET6 as isize, +/// Handles cases where there DAD state is neither tentative nor preferred. +#[derive(err_derive::Error, Debug)] +pub enum DadStateError { + /// Invalid DAD state. + #[error(display = "Invalid DAD state")] + Invalid, + + /// Duplicate unicast address. + #[error(display = "A duplicate IP address was detected")] + Duplicate, + + /// Deprecated unicast address. + #[error(display = "The IP address has been deprecated")] + Deprecated, + + /// Unknown DAD state constant. + #[error(display = "Unknown DAD state: {}", _0)] + Unknown(i32), } -impl fmt::Display for AddressFamily { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - AddressFamily::Ipv4 => write!(f, "IPv4 (AF_INET)"), - AddressFamily::Ipv6 => write!(f, "IPv6 (AF_INET6)"), +#[allow(non_upper_case_globals)] +impl From<NL_DAD_STATE> for DadStateError { + fn from(state: NL_DAD_STATE) -> DadStateError { + match state { + IpDadStateInvalid => DadStateError::Invalid, + IpDadStateDuplicate => DadStateError::Duplicate, + IpDadStateDeprecated => DadStateError::Deprecated, + other => DadStateError::Unknown(other), } } } @@ -263,38 +270,6 @@ pub async fn wait_for_interfaces(luid: NET_LUID_LH, ipv4: bool, ipv6: bool) -> i Ok(()) } -/// Handles cases where there DAD state is neither tentative nor preferred. -#[derive(err_derive::Error, Debug)] -pub enum DadStateError { - /// Invalid DAD state. - #[error(display = "Invalid DAD state")] - Invalid, - - /// Duplicate unicast address. - #[error(display = "A duplicate IP address was detected")] - Duplicate, - - /// Deprecated unicast address. - #[error(display = "The IP address has been deprecated")] - Deprecated, - - /// Unknown DAD state constant. - #[error(display = "Unknown DAD state: {}", _0)] - Unknown(i32), -} - -#[allow(non_upper_case_globals)] -impl From<NL_DAD_STATE> for DadStateError { - fn from(state: NL_DAD_STATE) -> DadStateError { - match state { - IpDadStateInvalid => DadStateError::Invalid, - IpDadStateDuplicate => DadStateError::Duplicate, - IpDadStateDeprecated => DadStateError::Deprecated, - other => DadStateError::Unknown(other), - } - } -} - /// Wait for addresses to be usable on an network adapter. pub async fn wait_for_addresses(luid: NET_LUID_LH) -> Result<()> { // Obtain unicast IP addresses @@ -402,17 +377,6 @@ pub fn get_unicast_table( Ok(unicast_rows) } -/// Obtain a string representation for a GUID object. -pub fn string_from_guid(guid: &GUID) -> String { - let mut buffer = [0u16; 40]; - let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) } - as usize; - // cannot fail because `buffer` is large enough - assert!(length > 0); - let length = length - 1; - String::from_utf16(&buffer[0..length]).unwrap() -} - /// Returns the GUID of a network interface given its LUID. pub fn guid_from_luid(luid: &NET_LUID_LH) -> io::Result<GUID> { let mut guid = MaybeUninit::zeroed(); @@ -509,27 +473,22 @@ pub fn try_socketaddr_from_inet_sockaddr(addr: SOCKADDR_INET) -> Result<SocketAd .ok_or(Error::UnknownAddressFamily(family)) } -/// Returns the system directory, i.e. `%windir%\system32`. -pub fn get_system_dir() -> io::Result<PathBuf> { - let mut folder_path: PWSTR = ptr::null_mut(); - let status = unsafe { SHGetKnownFolderPath(&FOLDERID_System, 0, 0, &mut folder_path) }; - let result = if status == S_OK { - let path = unsafe { WideCStr::from_ptr_str(folder_path) }; - Ok(path.to_ustring().to_os_string().into()) - } else { - Err(io::Error::new( - io::ErrorKind::NotFound, - "Cannot find the system directory", - )) - }; - unsafe { CoTaskMemFree(folder_path as *mut _) }; - result +/// Address family. These correspond to the `AF_*` constants. +#[derive(Debug, Clone, Copy)] +pub enum AddressFamily { + /// IPv4 address family + Ipv4 = AF_INET as isize, + /// IPv6 address family + Ipv6 = AF_INET6 as isize, } -/// Casts a struct to a slice of possibly uninitialized bytes. -#[cfg(target_os = "windows")] -pub fn as_uninit_byte_slice<T: Copy + Sized>(value: &T) -> &[mem::MaybeUninit<u8>] { - unsafe { std::slice::from_raw_parts(value as *const _ as *const _, mem::size_of::<T>()) } +impl fmt::Display for AddressFamily { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + AddressFamily::Ipv4 => write!(f, "IPv4 (AF_INET)"), + AddressFamily::Ipv6 => write!(f, "IPv6 (AF_INET6)"), + } + } } #[cfg(test)] diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml new file mode 100644 index 0000000000..162492e8cb --- /dev/null +++ b/talpid-wireguard/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "talpid-wireguard" +version = "0.0.0" +authors = ["Mullvad VPN"] +description = "Library for creating various WireGuard tunnels" +license = "GPL-3.0" +edition = "2021" +publish = false + + +[dependencies] +err-derive = "0.3.1" +futures = "0.3.15" +hex = "0.4" +ipnetwork = "0.16" +lazy_static = "1.0" +libc = "0.2" +log = "0.4" +parking_lot = "0.11" +talpid-routing = { path = "../talpid-routing" } +talpid-types = { path = "../talpid-types" } +talpid-tunnel-config-client = { path = "../talpid-tunnel-config-client" } +talpid-tunnel = { path = "../talpid-tunnel" } +zeroize = "1" +chrono = "0.4.21" +tokio = { version = "1.8", features = ["process", "rt-multi-thread", "fs"] } +tunnel-obfuscation = { path = "../tunnel-obfuscation" } +rand = "0.8.5" + +[target.'cfg(target_os="android")'.dependencies] +duct = "0.13" + +[target.'cfg(not(target_os="android"))'.dependencies] +byteorder = "1" +internet-checksum = "0.2" +socket2 = { version = "0.4.2", features = ["all"] } + +[target.'cfg(unix)'.dependencies] +nix = "0.23" + +[target.'cfg(target_os = "linux")'.dependencies] +rtnetlink = "0.11" +netlink-packet-core = "0.4.2" +netlink-packet-route = "0.13" +netlink-packet-utils = "0.5.1" +netlink-proto = "0.10" +talpid-dbus = { path = "../talpid-dbus" } +tokio-stream = { version = "0.1", features = ["io-util"] } + +[target.'cfg(windows)'.dependencies] +bitflags = "1.2" +talpid-windows-net = { path = "../talpid-windows-net" } +widestring = "0.5" + +# Figure out which features are needed and which are not +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.42.0" +features = [ + "Win32_Foundation", + "Win32_Globalization", + "Win32_Security", + "Win32_System_Com", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Ioctl", + "Win32_System_IO", + "Win32_System_LibraryLoader", + "Win32_System_ProcessStatus", + "Win32_System_Registry", + "Win32_System_Services", + "Win32_System_SystemServices", + "Win32_System_Threading", + "Win32_System_WindowsProgramming", + "Win32_Networking_WinSock", + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", + "Win32_UI_Shell", + "Win32_UI_WindowsAndMessaging", +] diff --git a/talpid-wireguard/build.rs b/talpid-wireguard/build.rs new file mode 100644 index 0000000000..bcf7f1cfbf --- /dev/null +++ b/talpid-wireguard/build.rs @@ -0,0 +1,36 @@ +use std::{env, path::PathBuf}; + +fn main() { + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + declare_libs_dir("../dist-assets/binaries"); + declare_libs_dir("../build/lib"); + + let link_type = match target_os.as_str() { + "android" => "", + "linux" | "macos" => "=static", + // We would like to avoid panicing on windows even if we can not link correctly + // because we would like to be able to run check and clippy. + // This does not allow for correct linking or buijding. + #[cfg(not(windows))] + "windows" => "", + #[cfg(windows)] + "windows" => "dylib", + _ => panic!("Unsupported platform: {}", target_os), + }; + + println!("cargo:rustc-link-lib{}=wg", link_type); +} + +fn declare_libs_dir(base: &str) { + let target_triplet = env::var("TARGET").expect("TARGET is not set"); + let lib_dir = manifest_dir().join(base).join(target_triplet); + println!("cargo:rerun-if-changed={}", lib_dir.display()); + println!("cargo:rustc-link-search={}", lib_dir.display()); +} + +fn manifest_dir() -> PathBuf { + env::var("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .expect("CARGO_MANIFEST_DIR env var not set") +} diff --git a/talpid-core/src/tunnel/wireguard/config.rs b/talpid-wireguard/src/config.rs index 89cab4865a..2198b5a3fe 100644 --- a/talpid-core/src/tunnel/wireguard/config.rs +++ b/talpid-wireguard/src/config.rs @@ -20,7 +20,7 @@ pub struct Config { pub mtu: u16, /// Firewall mark #[cfg(target_os = "linux")] - pub fwmark: u32, + pub fwmark: Option<u32>, /// Enable IPv6 routing rules #[cfg(target_os = "linux")] pub enable_ipv6: bool, @@ -118,7 +118,7 @@ impl Config { ipv6_gateway, mtu, #[cfg(target_os = "linux")] - fwmark: crate::linux::TUNNEL_FW_MARK, + fwmark: connection_config.fwmark, #[cfg(target_os = "linux")] enable_ipv6: generic_options.enable_ipv6, #[cfg(target_os = "windows")] @@ -137,7 +137,9 @@ impl Config { .add("listen_port", "0"); #[cfg(target_os = "linux")] - wg_conf.add("fwmark", self.fwmark.to_string().as_str()); + if let Some(fwmark) = &self.fwmark { + wg_conf.add("fwmark", fwmark.to_string().as_str()); + } wg_conf.add("replace_peers", "true"); diff --git a/talpid-core/src/tunnel/wireguard/connectivity_check.rs b/talpid-wireguard/src/connectivity_check.rs index cce8b53ec4..02283add65 100644 --- a/talpid-core/src/tunnel/wireguard/connectivity_check.rs +++ b/talpid-wireguard/src/connectivity_check.rs @@ -1,6 +1,6 @@ use crate::{ ping_monitor::{new_pinger, Pinger}, - tunnel::wireguard::stats::StatsMap, + stats::StatsMap, }; use std::{ cmp, @@ -391,7 +391,7 @@ mod test { use futures::Future; use super::*; - use crate::tunnel::wireguard::{ + use crate::{ config::Config, stats::{self, Stats}, TunnelError, diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-wireguard/src/lib.rs index b982dc148d..da4ea128a7 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-wireguard/src/lib.rs @@ -1,8 +1,9 @@ +//! Manage WireGuard tunnels. + +#![deny(missing_docs)] +#![deny(rust_2018_idioms)] + use self::config::Config; -#[cfg(not(windows))] -use super::tun_provider; -use super::{tun_provider::TunProvider, TunnelArgs, TunnelEvent, TunnelMetadata}; -use crate::routing::{self, RequiredRoute}; use futures::future::{abortable, AbortHandle as FutureAbortHandle, BoxFuture, Future}; #[cfg(windows)] use futures::{channel::mpsc, StreamExt}; @@ -23,6 +24,12 @@ use std::{ sync::{mpsc as sync_mpsc, Arc, Mutex}, time::Duration, }; +use talpid_routing as routing; +use talpid_routing::{self, RequiredRoute}; +#[cfg(not(windows))] +use talpid_tunnel::tun_provider; +use talpid_tunnel::{tun_provider::TunProvider, TunnelArgs, TunnelEvent, TunnelMetadata}; + #[cfg(windows)] use talpid_types::BoxedError; use talpid_types::{ @@ -41,6 +48,7 @@ use tunnel_obfuscation::{ pub mod config; mod connectivity_check; mod logging; +mod ping_monitor; mod stats; mod wireguard_go; #[cfg(target_os = "linux")] @@ -59,7 +67,7 @@ type EventCallback = Box<dyn (Fn(TunnelEvent) -> BoxFuture<'static, ()>) + Send pub enum Error { /// Failed to set up routing. #[error(display = "Failed to setup routing")] - SetupRoutingError(#[error(source)] crate::routing::Error), + SetupRoutingError(#[error(source)] talpid_routing::Error), /// Tunnel timed out #[error(display = "Tunnel timed out")] @@ -93,7 +101,7 @@ pub enum Error { /// Failed to set IP addresses on WireGuard interface #[cfg(target_os = "windows")] #[error(display = "Failed to set IP addresses on WireGuard interface")] - SetIpAddressesError(#[error(source)] crate::windows::Error), + SetIpAddressesError(#[error(source)] talpid_windows_net::Error), } /// Spawns and monitors a wireguard tunnel @@ -160,7 +168,7 @@ async fn maybe_create_obfuscator( let settings = Udp2TcpSettings { peer: *endpoint, #[cfg(target_os = "linux")] - fwmark: Some(crate::linux::TUNNEL_FW_MARK), + fwmark: config.fwmark, }; let obfuscator = create_obfuscator(&ObfuscationSettings::Udp2Tcp(settings)) .await @@ -423,12 +431,12 @@ impl WireguardMonitor { })?; // TODO: The LUID can be obtained directly. - let luid = crate::windows::luid_from_alias(iface_name).map_err(|error| { + let luid = talpid_windows_net::luid_from_alias(iface_name).map_err(|error| { log::error!("Failed to obtain tunnel interface LUID: {}", error); CloseMsg::SetupError(Error::IpInterfacesError) })?; for address in addresses { - crate::windows::add_ip_address_for_interface(luid, *address) + talpid_windows_net::add_ip_address_for_interface(luid, *address) .map_err(|error| CloseMsg::SetupError(Error::SetIpAddressesError(error)))?; } Ok(()) @@ -514,7 +522,7 @@ impl WireguardMonitor { ) -> Result<Box<dyn Tunnel>> { #[cfg(target_os = "linux")] if !*FORCE_USERSPACE_WIREGUARD { - if crate::dns::will_use_nm() { + if will_nm_manage_dns() { match wireguard_kernel::NetworkManagerTunnel::new(runtime, config) { Ok(tunnel) => { log::debug!("Using NetworkManager to use kernel WireGuard implementation"); @@ -627,7 +635,7 @@ impl WireguardMonitor { #[cfg(target_os = "linux")] { // No need due to policy based routing. - std::iter::empty() + std::iter::empty::<RequiredRoute>() } #[cfg(not(target_os = "linux"))] endpoints.iter().map(|ip| { @@ -838,3 +846,19 @@ pub enum TunnelError { #[error(display = "Failed to set up logging")] LoggingError(#[error(source)] logging::Error), } + +#[cfg(target_os = "linux")] +fn will_nm_manage_dns() -> bool { + use talpid_dbus::network_manager::NetworkManager; + + if talpid_dbus::systemd_resolved::SystemdResolved::new().is_ok() { + return false; + } + + NetworkManager::new() + .and_then(|nm| { + nm.ensure_can_be_used_to_manage_dns()?; + Ok(true) + }) + .unwrap_or(false) +} diff --git a/talpid-core/src/tunnel/wireguard/logging.rs b/talpid-wireguard/src/logging.rs index 35ec10fc2f..35ec10fc2f 100644 --- a/talpid-core/src/tunnel/wireguard/logging.rs +++ b/talpid-wireguard/src/logging.rs diff --git a/talpid-core/src/ping_monitor/android.rs b/talpid-wireguard/src/ping_monitor/android.rs index 9263dbac79..9263dbac79 100644 --- a/talpid-core/src/ping_monitor/android.rs +++ b/talpid-wireguard/src/ping_monitor/android.rs diff --git a/talpid-core/src/ping_monitor/icmp.rs b/talpid-wireguard/src/ping_monitor/icmp.rs index 0bcd9da72f..0bcd9da72f 100644 --- a/talpid-core/src/ping_monitor/icmp.rs +++ b/talpid-wireguard/src/ping_monitor/icmp.rs diff --git a/talpid-core/src/ping_monitor/mod.rs b/talpid-wireguard/src/ping_monitor/mod.rs index 9b105a50dc..9b105a50dc 100644 --- a/talpid-core/src/ping_monitor/mod.rs +++ b/talpid-wireguard/src/ping_monitor/mod.rs diff --git a/talpid-core/src/tunnel/wireguard/stats.rs b/talpid-wireguard/src/stats.rs index bd7b578545..bd7b578545 100644 --- a/talpid-core/src/tunnel/wireguard/stats.rs +++ b/talpid-wireguard/src/stats.rs diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-wireguard/src/wireguard_go.rs index a1ca8be6ba..4232d99e70 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs +++ b/talpid-wireguard/src/wireguard_go.rs @@ -2,15 +2,9 @@ use super::{ stats::{Stats, StatsMap}, Config, Tunnel, TunnelError, }; -#[cfg(not(windows))] -use crate::tunnel::tun_provider::TunProvider; -use crate::tunnel::wireguard::logging::{ - clean_up_logging, initialize_logging, wg_go_logging_callback, WgLogLevel, -}; +use crate::logging::{clean_up_logging, initialize_logging, wg_go_logging_callback, WgLogLevel}; #[cfg(windows)] use futures::SinkExt; -#[cfg(not(windows))] -use ipnetwork::IpNetwork; use std::{ ffi::{c_void, CStr}, future::Future, @@ -21,20 +15,22 @@ use std::{ #[cfg(windows)] use talpid_types::BoxedError; use zeroize::Zeroize; +#[cfg(not(windows))] +use {ipnetwork::IpNetwork, talpid_tunnel::tun_provider::TunProvider}; #[cfg(target_os = "windows")] use std::ffi::CString; #[cfg(target_os = "android")] -use crate::tunnel::tun_provider; +use talpid_tunnel::tun_provider; #[cfg(not(target_os = "windows"))] use { - crate::tunnel::tun_provider::{Tun, TunConfig}, std::{ net::IpAddr, os::unix::io::{AsRawFd, RawFd}, }, + talpid_tunnel::tun_provider::{Tun, TunConfig}, }; type Result<T> = std::result::Result<T, TunnelError>; @@ -63,7 +59,7 @@ pub struct WgGoTunnel { // context that maps to fs::File instance, used with logging callback _logging_context: LoggingContext, #[cfg(target_os = "windows")] - _route_callback_handle: Option<crate::routing::CallbackHandle>, + _route_callback_handle: Option<talpid_routing::CallbackHandle>, #[cfg(target_os = "windows")] setup_handle: tokio::task::JoinHandle<()>, } @@ -114,7 +110,7 @@ impl WgGoTunnel { pub fn start_tunnel( config: &Config, log_path: Option<&Path>, - route_manager_handle: crate::tunnel::RouteManagerHandle, + route_manager_handle: talpid_routing::RouteManagerHandle, mut done_tx: futures::channel::mpsc::Sender<std::result::Result<(), BoxedError>>, runtime: &tokio::runtime::Handle, ) -> Result<Self> { @@ -176,9 +172,11 @@ impl WgGoTunnel { log::debug!("Waiting for tunnel IP interfaces to arrive"); let prepare_interfaces = async move { - crate::windows::wait_for_interfaces(luid, true, has_ipv6).await?; + talpid_windows_net::wait_for_interfaces(luid, true, has_ipv6).await?; - if let Err(error) = crate::tunnel::windows::initialize_interfaces(luid, None) { + if let Err(error) = + talpid_tunnel::network_interface::initialize_interfaces(luid, None) + { log::error!( "{}", error.display_chain_with_msg("Failed to set tunnel interface metric"), @@ -211,7 +209,7 @@ impl WgGoTunnel { #[cfg(target_os = "windows")] pub fn default_route_changed_callback<'a>( event_type: crate::routing::EventType<'a>, - address_family: crate::windows::AddressFamily, + address_family: talpid_windows_net::AddressFamily, ) { use crate::routing::EventType::*; use windows_sys::Win32::NetworkManagement::IpHelper::ConvertInterfaceLuidToIndex; diff --git a/talpid-core/src/tunnel/wireguard/wireguard_kernel/mod.rs b/talpid-wireguard/src/wireguard_kernel/mod.rs index 7a64ee3f65..7a64ee3f65 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_kernel/mod.rs +++ b/talpid-wireguard/src/wireguard_kernel/mod.rs diff --git a/talpid-core/src/tunnel/wireguard/wireguard_kernel/netlink_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs index b1159bb6de..b1159bb6de 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_kernel/netlink_tunnel.rs +++ b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs diff --git a/talpid-core/src/tunnel/wireguard/wireguard_kernel/nl_message.rs b/talpid-wireguard/src/wireguard_kernel/nl_message.rs index 87f1537ea0..87f1537ea0 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_kernel/nl_message.rs +++ b/talpid-wireguard/src/wireguard_kernel/nl_message.rs diff --git a/talpid-core/src/tunnel/wireguard/wireguard_kernel/nm_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs index 92a8f5e81c..7428952585 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_kernel/nm_tunnel.rs +++ b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs @@ -100,7 +100,7 @@ impl Tunnel for NetworkManagerTunnel { let interface_name = self.interface_name.clone(); let mut wg = self.netlink_connections.wg_handle.clone(); Box::pin(async move { - let index = crate::linux::iface_index(&interface_name).map_err(|err| { + let index = iface_index(&interface_name).map_err(|err| { log::error!("Failed to fetch WireGuard device index: {}", err); TunnelError::SetConfigError })?; @@ -120,7 +120,9 @@ fn convert_config_to_dbus(config: &Config) -> DeviceConfig { let mut peer_configs = vec![]; wireguard_config.insert("mtu".into(), Variant(Box::new(config.mtu as u32))); - wireguard_config.insert("fwmark".into(), Variant(Box::new(config.fwmark as u32))); + if let Some(fwmark) = config.fwmark { + wireguard_config.insert("fwmark".into(), Variant(Box::new(fwmark as u32))); + } wireguard_config.insert("peer-routes".into(), Variant(Box::new(false))); wireguard_config.insert( "private-key".into(), @@ -202,3 +204,30 @@ fn convert_config_to_dbus(config: &Config) -> DeviceConfig { settings } + +/// Converts an interface name into the corresponding index. +#[cfg(target_os = "linux")] +fn iface_index(name: &str) -> std::result::Result<libc::c_uint, IfaceIndexLookupError> { + let c_name = std::ffi::CString::new(name) + .map_err(|e| IfaceIndexLookupError::InvalidInterfaceName(name.to_owned(), e))?; + let index = unsafe { libc::if_nametoindex(c_name.as_ptr()) }; + if index == 0 { + Err(IfaceIndexLookupError::InterfaceLookupError( + name.to_owned(), + std::io::Error::last_os_error(), + )) + } else { + Ok(index) + } +} + +/// Failure to lookup an interfaces index by its name. +#[derive(Debug, err_derive::Error)] +pub enum IfaceIndexLookupError { + /// The interface name is invalid - contains null bytes or is too long. + #[error(display = "Invalid network interface name: {}", _0)] + InvalidInterfaceName(String, #[error(source)] std::ffi::NulError), + /// Interface wasn't found by its name. + #[error(display = "Failed to get index for interface {}", _0)] + InterfaceLookupError(String, #[error(source)] std::io::Error), +} diff --git a/talpid-core/src/tunnel/wireguard/wireguard_kernel/parsers.rs b/talpid-wireguard/src/wireguard_kernel/parsers.rs index f08b2d6dfa..f08b2d6dfa 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_kernel/parsers.rs +++ b/talpid-wireguard/src/wireguard_kernel/parsers.rs diff --git a/talpid-core/src/tunnel/wireguard/wireguard_kernel/wg_message.rs b/talpid-wireguard/src/wireguard_kernel/wg_message.rs index d4b0ac78c6..0c36d45635 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_kernel/wg_message.rs +++ b/talpid-wireguard/src/wireguard_kernel/wg_message.rs @@ -96,7 +96,7 @@ impl DeviceMessage { let nlas = vec![ DeviceNla::IfIndex(interface_index), DeviceNla::ListenPort(0), - DeviceNla::Fwmark(crate::linux::TUNNEL_FW_MARK), + DeviceNla::Fwmark(config.fwmark.unwrap_or(0)), DeviceNla::PrivateKey(config.tunnel.private_key.to_bytes()), DeviceNla::Flags(WGDEVICE_F_REPLACE_PEERS), DeviceNla::Peers(peers), diff --git a/talpid-core/src/tunnel/wireguard/wireguard_nt.rs b/talpid-wireguard/src/wireguard_nt.rs index 08a75fd887..1044a3fd9a 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_nt.rs +++ b/talpid-wireguard/src/wireguard_nt.rs @@ -4,7 +4,6 @@ use super::{ stats::{Stats, StatsMap}, Tunnel, }; -use crate::windows; use bitflags::bitflags; use futures::SinkExt; use ipnetwork::IpNetwork; @@ -23,6 +22,7 @@ use std::{ sync::{Arc, Mutex}, }; use talpid_types::{BoxedError, ErrorExt}; +use talpid_windows_net as net; use widestring::{U16CStr, U16CString}; use windows_sys::{ core::GUID, @@ -193,7 +193,7 @@ impl From<IpAddr> for WgIpAddr { impl From<Ipv6Addr> for WgIpAddr { fn from(address: Ipv6Addr) -> Self { Self { - v6: windows::in6addr_from_ipaddr(address), + v6: net::in6addr_from_ipaddr(address), } } } @@ -201,7 +201,7 @@ impl From<Ipv6Addr> for WgIpAddr { impl From<Ipv4Addr> for WgIpAddr { fn from(address: Ipv4Addr) -> Self { Self { - v4: windows::inaddr_from_ipaddr(address), + v4: net::inaddr_from_ipaddr(address), } } } @@ -262,12 +262,12 @@ impl PartialEq for WgAllowedIp { } match self.address_family as u32 { AF_INET => { - windows::ipaddr_from_inaddr(unsafe { self.address.v4 }) - == windows::ipaddr_from_inaddr(unsafe { other.address.v4 }) + net::ipaddr_from_inaddr(unsafe { self.address.v4 }) + == net::ipaddr_from_inaddr(unsafe { other.address.v4 }) } AF_INET6 => { - windows::ipaddr_from_in6addr(unsafe { self.address.v6 }) - == windows::ipaddr_from_in6addr(unsafe { other.address.v6 }) + net::ipaddr_from_in6addr(unsafe { self.address.v6 }) + == net::ipaddr_from_in6addr(unsafe { other.address.v6 }) } _ => { log::error!("Allowed IP uses unknown address family"); @@ -284,11 +284,11 @@ impl fmt::Debug for WgAllowedIp { match self.address_family as u32 { AF_INET => s.field( "address", - &windows::ipaddr_from_inaddr(unsafe { self.address.v4 }), + &net::ipaddr_from_inaddr(unsafe { self.address.v4 }), ), AF_INET6 => s.field( "address", - &windows::ipaddr_from_in6addr(unsafe { self.address.v6 }), + &net::ipaddr_from_in6addr(unsafe { self.address.v6 }), ), _ => s.field("address", &"<unknown>"), }; @@ -340,7 +340,7 @@ impl From<SOCKADDR_INET> for SockAddrInet { } impl PartialEq for SockAddrInet { fn eq(&self, other: &Self) -> bool { - let self_addr = match windows::try_socketaddr_from_inet_sockaddr(self.addr) { + let self_addr = match net::try_socketaddr_from_inet_sockaddr(self.addr) { Ok(addr) => addr, Err(error) => { log::error!( @@ -350,7 +350,7 @@ impl PartialEq for SockAddrInet { return true; } }; - let other_addr = match windows::try_socketaddr_from_inet_sockaddr(other.addr) { + let other_addr = match net::try_socketaddr_from_inet_sockaddr(other.addr) { Ok(addr) => addr, Err(error) => { log::error!( @@ -368,7 +368,7 @@ impl Eq for SockAddrInet {} impl fmt::Debug for SockAddrInet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("SockAddrInet"); - let self_addr = windows::try_socketaddr_from_inet_sockaddr(self.addr) + let self_addr = net::try_socketaddr_from_inet_sockaddr(self.addr) .map(|addr| addr.to_string()) .unwrap_or("<unknown>".to_string()); s.field("addr", &self_addr).finish() @@ -473,12 +473,12 @@ async fn setup_ip_listener( }; log::debug!("Waiting for tunnel IP interfaces to arrive"); - windows::wait_for_interfaces(luid, true, has_ipv6) + net::wait_for_interfaces(luid, true, has_ipv6) .await .map_err(Error::IpInterfacesError)?; log::debug!("Waiting for tunnel IP interfaces: Done"); - crate::tunnel::windows::initialize_interfaces(luid, Some(mtu)) + talpid_tunnel::network_interface::initialize_interfaces(luid, Some(mtu)) .map_err(Error::SetTunnelMtuError)?; if let Some(device) = &*device.lock().unwrap() { @@ -568,7 +568,7 @@ impl WgNtAdapter { } fn name(&self) -> io::Result<U16CString> { - windows::alias_from_luid(&self.luid()).and_then(|alias| { + net::alias_from_luid(&self.luid()).and_then(|alias| { U16CString::from_os_str(alias) .map_err(|_| io::Error::new(io::ErrorKind::Other, "unexpected null char")) }) @@ -824,7 +824,7 @@ fn serialize_config(config: &Config) -> Result<Vec<MaybeUninit<u8>>> { peers_count: config.peers.len() as u32, }; - buffer.extend(windows::as_uninit_byte_slice(&header)); + buffer.extend(as_uninit_byte_slice(&header)); for peer in &config.peers { let flags = if peer.psk.is_some() { @@ -842,14 +842,14 @@ fn serialize_config(config: &Config) -> Result<Vec<MaybeUninit<u8>>> { .map(|psk| psk.as_bytes().clone()) .unwrap_or([0u8; WIREGUARD_KEY_LENGTH]), persistent_keepalive: 0, - endpoint: windows::inet_sockaddr_from_socketaddr(peer.endpoint).into(), + endpoint: net::inet_sockaddr_from_socketaddr(peer.endpoint).into(), tx_bytes: 0, rx_bytes: 0, last_handshake: 0, allowed_ips_count: peer.allowed_ips.len() as u32, }; - buffer.extend(windows::as_uninit_byte_slice(&wg_peer)); + buffer.extend(as_uninit_byte_slice(&wg_peer)); for allowed_ip in &peer.allowed_ips { let address_family = match allowed_ip { @@ -864,7 +864,7 @@ fn serialize_config(config: &Config) -> Result<Vec<MaybeUninit<u8>>> { let wg_allowed_ip = WgAllowedIp::new(address, address_family, allowed_ip.prefix() as u8)?; - buffer.extend(windows::as_uninit_byte_slice(&wg_allowed_ip)); + buffer.extend(as_uninit_byte_slice(&wg_allowed_ip)); } } @@ -889,7 +889,7 @@ unsafe fn deserialize_config( let peer: WgPeer = *(peer_data.as_ptr() as *const WgPeer); tail = new_tail; - if let Err(error) = windows::try_socketaddr_from_inet_sockaddr(peer.endpoint.addr) { + if let Err(error) = net::try_socketaddr_from_inet_sockaddr(peer.endpoint.addr) { log::error!( "{}", error.display_chain_with_msg("Received invalid endpoint address") @@ -986,6 +986,10 @@ impl Tunnel for WgNtTunnel { } } +pub fn as_uninit_byte_slice<T: Copy + Sized>(value: &T) -> &[mem::MaybeUninit<u8>] { + unsafe { std::slice::from_raw_parts(value as *const _ as *const _, mem::size_of::<T>()) } +} + #[cfg(test)] mod tests { use super::*; @@ -1037,8 +1041,10 @@ mod tests { public_key: WG_PUBLIC_KEY.as_bytes().clone(), preshared_key: [0; WIREGUARD_KEY_LENGTH], persistent_keepalive: 0, - endpoint: windows::inet_sockaddr_from_socketaddr("1.2.3.4:1234".parse().unwrap()) - .into(), + endpoint: talpid_windows_net::inet_sockaddr_from_socketaddr( + "1.2.3.4:1234".parse().unwrap() + ) + .into(), tx_bytes: 0, rx_bytes: 0, last_handshake: 0, @@ -1074,7 +1080,7 @@ mod tests { #[test] fn test_config_deserialization() { - let config_buffer = windows::as_uninit_byte_slice(&*WG_STRUCT_CONFIG); + let config_buffer = as_uninit_byte_slice(&*WG_STRUCT_CONFIG); let (iface, peers) = unsafe { deserialize_config(config_buffer) }.unwrap(); assert_eq!(iface, WG_STRUCT_CONFIG.interface); assert_eq!(peers.len(), 1); |
