diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d04e9fc2f..9b7d120e7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1136,15 +1136,12 @@ dependencies = [ "known-folders", "libc", "log", - "netlink-packet-core", - "netlink-packet-route", "os_info", "phoenix-channel", "proptest", "rand 0.8.5", "rand_core 0.6.4", "ring", - "rtnetlink", "secrecy", "serde", "serde_json", @@ -1831,14 +1828,23 @@ dependencies = [ ] [[package]] -name = "firezone-cli-utils" +name = "firezone-bin-shared" version = "0.1.0" dependencies = [ + "anyhow", "clap", + "connlib-shared", + "futures", + "ip_network", + "netlink-packet-core", + "netlink-packet-route", + "rtnetlink", + "tokio", "tracing", "tracing-log", "tracing-subscriber", "url", + "windows 0.57.0", ] [[package]] @@ -1855,7 +1861,7 @@ dependencies = [ "dns-lookup", "domain", "either", - "firezone-cli-utils", + "firezone-bin-shared", "firezone-tunnel", "futures", "futures-bounded", @@ -1939,7 +1945,7 @@ dependencies = [ "connlib-client-shared", "connlib-shared", "dirs", - "firezone-cli-utils", + "firezone-bin-shared", "futures", "git-version", "humantime", @@ -2024,6 +2030,7 @@ dependencies = [ "connlib-shared", "derivative", "domain", + "firezone-bin-shared", "firezone-relay", "futures", "futures-bounded", @@ -2037,8 +2044,6 @@ dependencies = [ "itertools 0.13.0", "libc", "log", - "netlink-packet-core", - "netlink-packet-route", "pretty_assertions", "proptest", "proptest-state-machine", @@ -2046,7 +2051,6 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rangemap", - "rtnetlink", "secrecy", "serde", "serde_json", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 154900523..8466d367c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,7 +7,7 @@ members = [ "connlib/tunnel", "connlib/snownet", "gateway", - "firezone-cli-utils", + "bin-shared", "gui-smoke-test", "headless-client", "snownet-tests", @@ -45,7 +45,7 @@ connlib-client-shared = { path = "connlib/clients/shared"} firezone-gateway = { path = "gateway"} firezone-headless-client = { path = "headless-client"} firezone-gui-client = { path = "gui-client/src-tauri"} -firezone-cli-utils = { path = "firezone-cli-utils"} +firezone-bin-shared = { path = "bin-shared"} snownet = { path = "connlib/snownet"} firezone-relay = { path = "relay"} connlib-shared = { path = "connlib/shared"} diff --git a/rust/bin-shared/Cargo.toml b/rust/bin-shared/Cargo.toml new file mode 100644 index 000000000..19e074efa --- /dev/null +++ b/rust/bin-shared/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "firezone-bin-shared" +version = "0.1.0" +edition = "2021" +description = "Firezone-specific modules shared between binaries." + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" +clap = { version = "4.5", features = ["derive", "env"] } +connlib-shared = { workspace = true } +futures = "0.3" +ip_network = { version = "0.4", default-features = false, features = ["serde"] } +tokio = { workspace = true, features = ["rt"] } +tracing = { workspace = true } +tracing-log = "0.2" +tracing-subscriber = { workspace = true, features = ["env-filter"] } +url = { version = "2.3.1", default-features = false } + +[target.'cfg(target_os = "linux")'.dependencies] +netlink-packet-core = { version = "0.7", default-features = false } +netlink-packet-route = { version = "0.19", default-features = false } +rtnetlink = { workspace = true } + +[target.'cfg(windows)'.dependencies.windows] +version = "0.57.0" +features = [ + "Win32_Foundation", +] + +[lints] +workspace = true diff --git a/rust/firezone-cli-utils/src/lib.rs b/rust/bin-shared/src/lib.rs similarity index 89% rename from rust/firezone-cli-utils/src/lib.rs rename to rust/bin-shared/src/lib.rs index 3d70ecf4f..388782682 100644 --- a/rust/firezone-cli-utils/src/lib.rs +++ b/rust/bin-shared/src/lib.rs @@ -1,3 +1,5 @@ +mod tun_device_manager; + use clap::Args; use tracing_log::LogTracer; use tracing_subscriber::{ @@ -5,6 +7,9 @@ use tracing_subscriber::{ }; use url::Url; +#[cfg(any(target_os = "linux", target_os = "windows"))] +pub use tun_device_manager::TunDeviceManager; + pub fn setup_global_subscriber(additional_layer: L) where L: Layer + Send + Sync, diff --git a/rust/connlib/shared/src/tun_device_manager.rs b/rust/bin-shared/src/tun_device_manager.rs similarity index 100% rename from rust/connlib/shared/src/tun_device_manager.rs rename to rust/bin-shared/src/tun_device_manager.rs diff --git a/rust/connlib/shared/src/tun_device_manager/linux.rs b/rust/bin-shared/src/tun_device_manager/linux.rs similarity index 93% rename from rust/connlib/shared/src/tun_device_manager/linux.rs rename to rust/bin-shared/src/tun_device_manager/linux.rs index 0f708ec4d..7f5720e78 100644 --- a/rust/connlib/shared/src/tun_device_manager/linux.rs +++ b/rust/bin-shared/src/tun_device_manager/linux.rs @@ -1,7 +1,7 @@ //! Virtual network interface -use crate::DEFAULT_MTU; use anyhow::{anyhow, Context as _, Result}; +use connlib_shared::DEFAULT_MTU; use futures::TryStreamExt; use ip_network::{IpNetwork, Ipv4Network, Ipv6Network}; use netlink_packet_route::route::{RouteProtocol, RouteScope}; @@ -12,8 +12,7 @@ use std::{ net::{Ipv4Addr, Ipv6Addr}, }; -pub const FIREZONE_MARK: u32 = 0xfd002021; -pub const IFACE_NAME: &str = "tun-firezone"; +const FIREZONE_MARK: u32 = 0xfd002021; // Keep this synced with `Sockets` until #5797. const FILE_ALREADY_EXISTS: i32 = -17; const FIREZONE_TABLE: u32 = 0x2021_fd00; @@ -35,6 +34,8 @@ impl Drop for TunDeviceManager { } impl TunDeviceManager { + pub const IFACE_NAME: &'static str = "tun-firezone"; // Keep this synced with `Tun` until we fix the module dependencies (i.e. move `Tun` out of `firezone-tunnel`). + /// Creates a new managed tunnel device. /// /// Panics if called without a Tokio runtime. @@ -51,15 +52,17 @@ impl TunDeviceManager { #[tracing::instrument(level = "trace", skip(self))] pub async fn set_ips(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Result<()> { + let name = Self::IFACE_NAME; + let handle = &self.connection.handle; let index = handle .link() .get() - .match_name(IFACE_NAME.to_string()) + .match_name(name.to_string()) .execute() .try_next() .await? - .ok_or_else(|| anyhow!("Interface '{IFACE_NAME}' does not exist"))? + .ok_or_else(|| anyhow!("Interface '{name}' does not exist"))? .header .index; @@ -139,6 +142,7 @@ impl TunDeviceManager { .map(IpNetwork::from) .chain(ipv6.into_iter().map(IpNetwork::from)) .collect(); + if new_routes == self.routes { tracing::debug!("Routes are unchanged"); @@ -152,7 +156,7 @@ impl TunDeviceManager { let index = handle .link() .get() - .match_name(IFACE_NAME.to_string()) + .match_name(Self::IFACE_NAME.to_string()) .execute() .try_next() .await? diff --git a/rust/connlib/shared/src/tun_device_manager/windows.rs b/rust/bin-shared/src/tun_device_manager/windows.rs similarity index 97% rename from rust/connlib/shared/src/tun_device_manager/windows.rs rename to rust/bin-shared/src/tun_device_manager/windows.rs index e625bd5ff..c7196e971 100644 --- a/rust/connlib/shared/src/tun_device_manager/windows.rs +++ b/rust/bin-shared/src/tun_device_manager/windows.rs @@ -1,5 +1,5 @@ -use crate::windows::{CREATE_NO_WINDOW, TUNNEL_NAME}; use anyhow::Result; +use connlib_shared::windows::{CREATE_NO_WINDOW, TUNNEL_NAME}; use ip_network::{Ipv4Network, Ipv6Network}; use std::{ net::{Ipv4Addr, Ipv6Addr}, diff --git a/rust/connlib/shared/Cargo.toml b/rust/connlib/shared/Cargo.toml index f61169a1d..8572bdc79 100644 --- a/rust/connlib/shared/Cargo.toml +++ b/rust/connlib/shared/Cargo.toml @@ -44,11 +44,6 @@ tokio = { version = "1.38", features = ["macros", "rt"] } [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] swift-bridge = { workspace = true } -[target.'cfg(target_os = "linux")'.dependencies] -netlink-packet-route = { version = "0.19", default-features = false } -netlink-packet-core = { version = "0.7", default-features = false } -rtnetlink = { workspace = true } - # Windows tunnel dependencies [target.'cfg(target_os = "windows")'.dependencies] wintun = "0.4.0" diff --git a/rust/connlib/shared/src/error.rs b/rust/connlib/shared/src/error.rs index 2632810d8..1f29a2458 100644 --- a/rust/connlib/shared/src/error.rs +++ b/rust/connlib/shared/src/error.rs @@ -27,14 +27,6 @@ pub enum ConnlibError { /// Glob for errors without a type. #[error("Other error: {0}")] Other(&'static str), - #[cfg(target_os = "linux")] - #[error(transparent)] - NetlinkError(rtnetlink::Error), - /// Io translation of netlink error - /// The IO version is easier to interpret - /// We maintain a different variant from the standard IO for this to keep more context - #[error("IO netlink error: {0}")] - NetlinkErrorIo(std::io::Error), /// No iface found #[error("No iface found")] NoIface, @@ -89,14 +81,3 @@ pub enum ConnlibError { #[error("connection to the portal failed: {0}")] PortalConnectionFailed(phoenix_channel::Error), } - -#[cfg(target_os = "linux")] -impl From for ConnlibError { - fn from(err: rtnetlink::Error) -> Self { - #[allow(clippy::wildcard_enum_match_arm)] - match err { - rtnetlink::Error::NetlinkError(err) => Self::NetlinkErrorIo(err.to_io()), - err => Self::NetlinkError(err), - } - } -} diff --git a/rust/connlib/shared/src/lib.rs b/rust/connlib/shared/src/lib.rs index 8b1f29f53..e4b45c527 100644 --- a/rust/connlib/shared/src/lib.rs +++ b/rust/connlib/shared/src/lib.rs @@ -6,7 +6,6 @@ pub mod callbacks; pub mod error; pub mod messages; -pub mod tun_device_manager; #[cfg(target_os = "windows")] pub mod windows; diff --git a/rust/connlib/tunnel/Cargo.toml b/rust/connlib/tunnel/Cargo.toml index feb940597..8c0e10d54 100644 --- a/rust/connlib/tunnel/Cargo.toml +++ b/rust/connlib/tunnel/Cargo.toml @@ -50,6 +50,9 @@ hickory-proto = { workspace = true } derivative = "2.2.0" ip-packet = { workspace = true, features = ["proptest"] } +[target.'cfg(target_os = "windows")'.dev-dependencies] +firezone-bin-shared = { workspace = true } # Required for benchmark. + [[bench]] name = "tunnel" harness = false @@ -57,12 +60,6 @@ harness = false [features] proptest = ["dep:proptest", "connlib-shared/proptest"] -# Linux tunnel dependencies -[target.'cfg(target_os = "linux")'.dependencies] -netlink-packet-route = { version = "0.19", default-features = false } -netlink-packet-core = { version = "0.7", default-features = false } -rtnetlink = { workspace = true } - # Windows tunnel dependencies [target.'cfg(target_os = "windows")'.dependencies] tokio = { workspace = true, features = ["sync"] } diff --git a/rust/connlib/tunnel/benches/tunnel.rs b/rust/connlib/tunnel/benches/tunnel.rs index a3a72ec36..c0d21e779 100644 --- a/rust/connlib/tunnel/benches/tunnel.rs +++ b/rust/connlib/tunnel/benches/tunnel.rs @@ -26,6 +26,7 @@ mod platform { #[cfg(target_os = "windows")] mod platform { use anyhow::Result; + use firezone_bin_shared::TunDeviceManager; use firezone_tunnel::Tun; use ip_packet::{IpPacket, Packet as _}; use std::{ @@ -59,8 +60,7 @@ mod platform { let ipv4 = Ipv4Addr::from([100, 90, 215, 97]); let ipv6 = Ipv6Addr::from([0xfd00, 0x2021, 0x1111, 0x0, 0x0, 0x0, 0x0016, 0x588f]); - let mut device_manager = - connlib_shared::tun_device_manager::platform::TunDeviceManager::new()?; + let mut device_manager = TunDeviceManager::new()?; device_manager.set_ips(ipv4, ipv6).await?; tun.add_route(ipv4.into())?; diff --git a/rust/connlib/tunnel/src/device_channel/tun_linux.rs b/rust/connlib/tunnel/src/device_channel/tun_linux.rs index 30e871497..3ca178aa3 100644 --- a/rust/connlib/tunnel/src/device_channel/tun_linux.rs +++ b/rust/connlib/tunnel/src/device_channel/tun_linux.rs @@ -1,6 +1,6 @@ use super::utils; use crate::device_channel::ioctl; -use connlib_shared::{tun_device_manager::platform::IFACE_NAME, Callbacks, Error, Result}; +use connlib_shared::{Callbacks, Error, Result}; use ip_network::IpNetwork; use libc::{ close, fcntl, makedev, mknod, open, F_GETFL, F_SETFL, IFF_NO_PI, IFF_TUN, O_NONBLOCK, O_RDWR, @@ -22,6 +22,7 @@ use tokio::io::unix::AsyncFd; const TUNSETIFF: libc::c_ulong = 0x4004_54ca; const TUN_DEV_MAJOR: u32 = 10; const TUN_DEV_MINOR: u32 = 200; +const IFACE_NAME: &str = "tun-firezone"; // Keep this synced with `TunDeviceManager` until we fix the module dependencies (i.e. move `Tun` out of `firezone-tunnel`). // Safety: We know that this is a valid C string. const TUN_FILE: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/net/tun\0") }; diff --git a/rust/connlib/tunnel/src/sockets.rs b/rust/connlib/tunnel/src/sockets.rs index 7f1441ae2..d4855c5fa 100644 --- a/rust/connlib/tunnel/src/sockets.rs +++ b/rust/connlib/tunnel/src/sockets.rs @@ -339,7 +339,9 @@ fn make_socket(addr: impl Into) -> Result { #[cfg(target_os = "linux")] { - socket.set_mark(connlib_shared::tun_device_manager::platform::FIREZONE_MARK)?; + const FIREZONE_MARK: u32 = 0xfd002021; // Keep this synced with `TunDeviceManager` until #5797. + + socket.set_mark(FIREZONE_MARK)?; } // Note: for AF_INET sockets IPV6_V6ONLY is not a valid flag diff --git a/rust/firezone-cli-utils/Cargo.toml b/rust/firezone-cli-utils/Cargo.toml deleted file mode 100644 index e4179c152..000000000 --- a/rust/firezone-cli-utils/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "firezone-cli-utils" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -url = { version = "2.3.1", default-features = false } -tracing-subscriber = { workspace = true, features = ["env-filter"] } -tracing = { workspace = true } -tracing-log = "0.2" -clap = { version = "4.5", features = ["derive", "env"] } - -[lints] -workspace = true diff --git a/rust/gateway/Cargo.toml b/rust/gateway/Cargo.toml index 0b481a13a..26bb8e6c0 100644 --- a/rust/gateway/Cargo.toml +++ b/rust/gateway/Cargo.toml @@ -17,7 +17,7 @@ connlib-shared = { workspace = true } firezone-tunnel = { workspace = true } futures = "0.3.29" futures-bounded = { workspace = true } -firezone-cli-utils = { workspace = true } +firezone-bin-shared = { workspace = true } phoenix-channel = { workspace = true } secrecy = { workspace = true } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } diff --git a/rust/gateway/src/main.rs b/rust/gateway/src/main.rs index 8db507016..a8df490d8 100644 --- a/rust/gateway/src/main.rs +++ b/rust/gateway/src/main.rs @@ -2,10 +2,10 @@ use crate::eventloop::{Eventloop, PHOENIX_TOPIC}; use anyhow::{Context, Result}; use backoff::ExponentialBackoffBuilder; use clap::Parser; -use connlib_shared::messages::Interface; -use connlib_shared::tun_device_manager::TunDeviceManager; -use connlib_shared::{get_user_agent, keypair, Callbacks, LoginUrl, StaticSecret}; -use firezone_cli_utils::{setup_global_subscriber, CommonArgs}; +use connlib_shared::{ + get_user_agent, keypair, messages::Interface, Callbacks, LoginUrl, StaticSecret, +}; +use firezone_bin_shared::{setup_global_subscriber, CommonArgs, TunDeviceManager}; use firezone_tunnel::{GatewayTunnel, Sockets}; use futures::channel::mpsc; use futures::{future, StreamExt, TryFutureExt}; diff --git a/rust/headless-client/Cargo.toml b/rust/headless-client/Cargo.toml index ff4376192..43a8cd151 100644 --- a/rust/headless-client/Cargo.toml +++ b/rust/headless-client/Cargo.toml @@ -13,7 +13,7 @@ atomicwrites = "0.4.3" # Needed to safely backup `/etc/resolv.conf` and write th clap = { version = "4.5", features = ["derive", "env", "string"] } connlib-client-shared = { workspace = true } connlib-shared = { workspace = true } -firezone-cli-utils = { workspace = true } +firezone-bin-shared = { workspace = true } futures = "0.3.30" git-version = "0.3.9" humantime = "2.1" diff --git a/rust/headless-client/src/dns_control/linux.rs b/rust/headless-client/src/dns_control/linux.rs index fa41aceb3..8ce60da0c 100644 --- a/rust/headless-client/src/dns_control/linux.rs +++ b/rust/headless-client/src/dns_control/linux.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Context as _, Result}; -use connlib_shared::tun_device_manager::linux::IFACE_NAME; +use firezone_bin_shared::TunDeviceManager; use std::{net::IpAddr, process::Command, str::FromStr}; mod etc_resolv_conf; @@ -95,7 +95,7 @@ fn configure_network_manager(_dns_config: &[IpAddr]) -> Result<()> { async fn configure_systemd_resolved(dns_config: &[IpAddr]) -> Result<()> { let status = tokio::process::Command::new("resolvectl") .arg("dns") - .arg(IFACE_NAME) + .arg(TunDeviceManager::IFACE_NAME) .args(dns_config.iter().map(ToString::to_string)) .status() .await @@ -106,7 +106,7 @@ async fn configure_systemd_resolved(dns_config: &[IpAddr]) -> Result<()> { let status = tokio::process::Command::new("resolvectl") .arg("domain") - .arg(IFACE_NAME) + .arg(TunDeviceManager::IFACE_NAME) .arg("~.") .status() .await diff --git a/rust/headless-client/src/ipc_service.rs b/rust/headless-client/src/ipc_service.rs index 313c6af79..72187dcb5 100644 --- a/rust/headless-client/src/ipc_service.rs +++ b/rust/headless-client/src/ipc_service.rs @@ -7,7 +7,6 @@ use crate::{ use anyhow::{Context as _, Result}; use clap::Parser; use connlib_client_shared::{file_logger, keypair, ConnectArgs, LoginUrl, Session, Sockets}; -use connlib_shared::tun_device_manager; use futures::{future, SinkExt as _, StreamExt as _}; use std::{net::IpAddr, path::PathBuf, pin::pin, time::Duration}; use tokio::{sync::mpsc, time::Instant}; @@ -16,6 +15,7 @@ use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer, Registry}; use url::Url; pub mod ipc; +use firezone_bin_shared::TunDeviceManager; use ipc::{Server as IpcServer, ServiceId}; #[cfg(target_os = "linux")] @@ -162,7 +162,7 @@ struct Handler { ipc_rx: ipc::ServerRead, ipc_tx: ipc::ServerWrite, last_connlib_start_instant: Option, - tun_device: tun_device_manager::TunDeviceManager, + tun_device: TunDeviceManager, } enum Event { @@ -178,7 +178,7 @@ impl Handler { .await .context("Failed to wait for incoming IPC connection from a GUI")?; let (cb_tx, cb_rx) = mpsc::channel(10); - let tun_device = tun_device_manager::TunDeviceManager::new()?; + let tun_device = TunDeviceManager::new()?; Ok(Self { callback_handler: CallbackHandler { cb_tx }, diff --git a/rust/headless-client/src/standalone.rs b/rust/headless-client/src/standalone.rs index 6e838ad2a..588a85904 100644 --- a/rust/headless-client/src/standalone.rs +++ b/rust/headless-client/src/standalone.rs @@ -7,8 +7,7 @@ use crate::{ use anyhow::{anyhow, Context as _, Result}; use clap::Parser; use connlib_client_shared::{file_logger, keypair, ConnectArgs, LoginUrl, Session, Sockets}; -use connlib_shared::tun_device_manager; -use firezone_cli_utils::setup_global_subscriber; +use firezone_bin_shared::{setup_global_subscriber, TunDeviceManager}; use futures::{FutureExt as _, StreamExt as _}; use secrecy::SecretString; use std::{ @@ -175,7 +174,7 @@ pub fn run_only_headless_client() -> Result<()> { let mut terminate = pin!(terminate.recv().fuse()); let mut hangup = pin!(hangup.recv().fuse()); let mut dns_controller = DnsController::default(); - let mut tun_device = tun_device_manager::TunDeviceManager::new()?; + let mut tun_device = TunDeviceManager::new()?; let mut cb_rx = ReceiverStream::new(cb_rx).fuse(); loop {