diff --git a/Cargo.lock b/Cargo.lock index 4b87709..8c5b88a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -77,6 +77,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bitflags" version = "1.3.2" @@ -121,9 +127,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.2.67" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "shlex", @@ -137,9 +143,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -248,17 +254,17 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "defguard_boringtun" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a265bf3af32d0a0e19d5a22cb2c46b685afa6b7e00fcdade90546fbb7d5eca4f" +checksum = "6d920cd0791b2199308a3c8cc0d41b88d96e4c07d70e98b658a4ee72ad32ca7e" dependencies = [ "aead", - "base64", + "base64 0.22.1", "blake2", "chacha20poly1305", "hex", @@ -270,16 +276,16 @@ dependencies = [ "parking_lot", "ring", "socket2", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", "x25519-dalek 2.0.1", ] [[package]] name = "defguard_wireguard_rs" -version = "0.11.0" +version = "0.11.2" dependencies = [ - "base64", + "base64 0.23.1", "defguard_boringtun", "env_logger", "ipnet", @@ -294,7 +300,7 @@ dependencies = [ "regex", "serde", "serde_test", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", "tracing-subscriber", "windows", @@ -321,7 +327,7 @@ dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -330,7 +336,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -381,9 +387,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "generic-array" @@ -465,9 +471,9 @@ checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d" [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is_terminal_polyfill" @@ -477,11 +483,12 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "jiff" -version = "0.2.32" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "961d16382652bfdd8c6f68b223b26a8c93e0d475c672f414411db31c6c5c900e" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" dependencies = [ "defmt", + "jiff-core", "jiff-static", "log", "portable-atomic", @@ -489,15 +496,25 @@ dependencies = [ "serde_core", ] +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + [[package]] name = "jiff-static" -version = "0.2.32" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" dependencies = [ + "jiff-core", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -508,9 +525,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libloading" @@ -545,32 +562,31 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "netlink-packet-core" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3463cbb78394cb0141e2c926b93fc2197e473394b761986eca3b9da2c63ae0f4" -dependencies = [ - "paste", -] +checksum = "2d6e2daf9a8c2e21302714706860a0e6be02e03d17294ecc66b354ea7d4059dd" [[package]] name = "netlink-packet-generic" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f891b2e0054cac5a684a06628f59568f841c93da4e551239da6e518f539e775" +checksum = "e609a7be61318181c110df304d54ed3f3e129b9569b8ca16e40a9d644f46930c" dependencies = [ "netlink-packet-core", + "zerocopy", ] [[package]] name = "netlink-packet-route" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2288fcb784eb3defd5fb16f4c4160d5f477de192eac730f43e1d11c24d9a007" +checksum = "59d48ffc8b73a506e1ff0878528c72668f2bfbc3d875b6be890a96be5d0d5576" dependencies = [ "bitflags 2.13.1", "libc", "log", "netlink-packet-core", + "zerocopy", ] [[package]] @@ -581,14 +597,14 @@ checksum = "3176f18d11a1ae46053e59ec89d46ba318ae1343615bd3f8c908bfc84edae35c" dependencies = [ "byteorder", "pastey", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "netlink-packet-wireguard" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb217ca08c02978c3ea5ef0f013d20c602f2a17a9f00d9e4b1518a3500c2f332" +checksum = "1586e7d665d51e10a6e67eaf433beeea784c8dcb96d89c7b9b99444de4221d16" dependencies = [ "bitflags 2.13.1", "libc", @@ -599,9 +615,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.8" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" +checksum = "c99d38e00b420df49e940fe0826e667c3dad82ac68eb4c2bbf754c1c14a83a10" dependencies = [ "bytes", "libc", @@ -670,12 +686,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pastey" version = "0.1.1" @@ -701,9 +711,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" @@ -716,18 +726,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -776,9 +786,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -828,9 +838,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -838,22 +848,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -913,6 +923,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -924,11 +945,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -939,18 +960,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -981,7 +1002,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1130,7 +1151,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1141,7 +1162,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1316,6 +1337,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "zeroize" version = "1.9.0" diff --git a/Cargo.toml b/Cargo.toml index 066708c..6466c66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "defguard_wireguard_rs" -version = "0.11.0" +version = "0.11.2" edition = "2024" rust-version = "1.91" description = "A unified multi-platform high-level API for managing WireGuard interfaces" @@ -12,7 +12,7 @@ keywords = ["wireguard", "network", "vpn"] categories = ["network-programming"] [dependencies] -base64 = "0.22" +base64 = "0.23" log = "0.4" serde = { version = "1.0", features = ["derive"], optional = true } thiserror = "2.0" @@ -41,12 +41,12 @@ windows = { version = "0.62", features = [ wireguard-nt = "0.5" [target.'cfg(target_os = "linux")'.dependencies] -netlink-packet-core = "0.8" -netlink-packet-generic = "0.4" -netlink-packet-route = "0.31" +netlink-packet-core = "0.9" +netlink-packet-generic = "0.5" +netlink-packet-route = "0.33" netlink-packet-utils = "0.6" -netlink-packet-wireguard = "0.4" -netlink-sys = "0.8" +netlink-packet-wireguard = "0.5" +netlink-sys = "0.9" [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))'.dependencies] regex = "1.12" diff --git a/examples/userspace.rs b/examples/userspace.rs index 5eea1fe..ab80927 100644 --- a/examples/userspace.rs +++ b/examples/userspace.rs @@ -1,6 +1,6 @@ use std::{ - io::{Read, Write, stdin, stdout}, - net::SocketAddr, + io::{self, Read, Write, stdin, stdout}, + net::{IpAddr, Ipv4Addr, SocketAddr}, str::FromStr, }; @@ -10,11 +10,13 @@ use defguard_wireguard_rs::{ }; use x25519_dalek::{EphemeralSecret, PublicKey}; -fn pause() { +fn pause() -> io::Result<()> { let mut stdout = stdout(); - stdout.write_all(b"Press Enter to continue...").unwrap(); - stdout.flush().unwrap(); - stdin().read_exact(&mut [0]).unwrap(); + stdout.write_all(b"Press Enter to continue...")?; + stdout.flush()?; + stdin().read_exact(&mut [0])?; + + Ok(()) } fn main() -> Result<(), Box> { @@ -23,11 +25,10 @@ fn main() -> Result<(), Box> { .with_max_level(tracing::Level::DEBUG) .init(); - let ifname: String = if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") { - "wg0".into() - } else { - "utun5".into() - }; + #[cfg(not(target_os = "macos"))] + let ifname = String::from("tun0"); + #[cfg(target_os = "macos")] + let ifname = String::from("utun5"); let mut api = WGApi::::new(ifname.clone())?; api.create_interface()?; @@ -40,7 +41,7 @@ fn main() -> Result<(), Box> { let mut peer = Peer::new(peer_key.clone()); // WireGuard server endpoint which peer connects to. - let endpoint: SocketAddr = "10.20.30.40:55001".parse().unwrap(); + let endpoint = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 20, 30, 40)), 55001); // Peer endpoint and interval. peer.endpoint = Some(endpoint); peer.persistent_keepalive_interval = Some(25); @@ -72,7 +73,7 @@ fn main() -> Result<(), Box> { api.configure_interface(&interface_config, &[])?; println!("Interface {ifname} configured."); - pause(); + pause().unwrap(); api.remove_interface()?; diff --git a/src/error.rs b/src/error.rs index 4442788..18aa865 100644 --- a/src/error.rs +++ b/src/error.rs @@ -26,6 +26,7 @@ pub enum WireguardInterfaceError { PeerConfigurationError(String), #[error("Interface data read error: {0}")] ReadInterfaceError(String), + #[cfg(target_os = "linux")] #[error("Netlink error: {0}")] NetlinkError(String), #[error("BSD error: {0}")] diff --git a/src/netlink.rs b/src/netlink.rs index 49fea0a..5e97f29 100644 --- a/src/netlink.rs +++ b/src/netlink.rs @@ -463,6 +463,27 @@ fn get_interface_index(ifname: &str) -> NetlinkResult> { Ok(None) } +/// Bring a network interface up. +pub(crate) fn set_link_up(if_name: &str) -> NetlinkResult<()> { + let Some(index) = get_interface_index(if_name)? else { + error!("Failed to bring interface {if_name} up: interface not found"); + return Err(NetlinkError::NotFound); + }; + + let mut message = LinkMessage::default(); + message.header.index = index; + message.header.flags = LinkFlags::Up; + message.header.change_mask = LinkFlags::Up; + + netlink_request( + RouteNetlinkMessage::SetLink(message), + NLM_F_REQUEST | NLM_F_ACK, + NETLINK_ROUTE, + )?; + + Ok(()) +} + #[cfg(test)] /// Get default route for a given address family. pub(crate) fn get_gateway(address_family: AddressFamily) -> NetlinkResult> { @@ -529,31 +550,31 @@ pub(crate) fn add_route( IpAddr::V6(ipv6) => RouteAddress::Inet6(ipv6), }; message.header = header; - if let Some(interface_index) = get_interface_index(ifname)? { - message - .attributes - .push(RouteAttribute::Oif(interface_index)); - message - .attributes - .push(RouteAttribute::Destination(route_address)); - if let Some(table) = table { - message.attributes.push(RouteAttribute::Table(table)); - } - match netlink_request( - RouteNetlinkMessage::NewRoute(message), - NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL, - NETLINK_ROUTE, - ) { - Ok(_msg) => Ok(()), - Err(NetlinkError::FileAlreadyExists) => Ok(()), - Err(err) => { - error!("Failed to add WireGuard interface route: {err}"); - Err(NetlinkError::AddRouteError) - } - } - } else { + let Some(interface_index) = get_interface_index(ifname)? else { error!("Failed to add WireGuard interface route interface {ifname} index not found"); - Err(NetlinkError::AddRouteError) + return Err(NetlinkError::NotFound); + }; + + message + .attributes + .push(RouteAttribute::Oif(interface_index)); + message + .attributes + .push(RouteAttribute::Destination(route_address)); + if let Some(table) = table { + message.attributes.push(RouteAttribute::Table(table)); + } + match netlink_request( + RouteNetlinkMessage::NewRoute(message), + NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL, + NETLINK_ROUTE, + ) { + Ok(_msg) => Ok(()), + Err(NetlinkError::FileAlreadyExists) => Ok(()), + Err(err) => { + error!("Failed to add WireGuard interface route: {err}"); + Err(NetlinkError::AddRouteError) + } } } diff --git a/src/wgapi_userspace.rs b/src/wgapi_userspace.rs index 6a2fe17..98e0010 100644 --- a/src/wgapi_userspace.rs +++ b/src/wgapi_userspace.rs @@ -103,6 +103,13 @@ impl WireguardInterfaceApi for WGApi { debug!("Userspace interface {} created successfully", self.ifname); self.device_handle = Some(device_handle); + // The interface created by BoringTun is down at this point. Unlike the BSDs, Linux doesn't + // put it up when addresses are assigned, so do it explicitly here. + #[cfg(target_os = "linux")] + { + netlink::set_link_up(&self.ifname)?; + } + Ok(()) }