diff --git a/Cargo.lock b/Cargo.lock index 416cf5f..9a4669f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" @@ -371,9 +371,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -383,9 +383,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -394,9 +394,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc_version" @@ -441,9 +441,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", diff --git a/src/bsd/mod.rs b/src/bsd/mod.rs index a9effc0..46408f3 100644 --- a/src/bsd/mod.rs +++ b/src/bsd/mod.rs @@ -363,11 +363,7 @@ pub fn get_gateway(ip_version: IpVersion) -> Result, IoError> { } /// Add routing gateway. -pub fn add_gateway_host( - dest: &IpAddrMask, - gateway: IpAddr, - is_blackhole: bool, -) -> Result<(), IoError> { +pub fn add_gateway(dest: &IpAddrMask, gateway: IpAddr, is_blackhole: bool) -> Result<(), IoError> { match (dest.ip, dest.mask(), gateway) { (IpAddr::V4(ip), IpAddr::V4(mask), IpAddr::V4(gw)) => { let payload = DestAddrMask::::new(ip.into(), mask.into(), gw.into()); @@ -386,18 +382,18 @@ pub fn add_gateway_host( } /// Add routing gateway. -pub fn delete_gateway_host(dest: &IpAddrMask) -> Result<(), IoError> { +pub fn delete_gateway(dest: &IpAddrMask) -> Result<(), IoError> { match (dest.ip, dest.mask()) { (IpAddr::V4(ip), IpAddr::V4(mask)) => { let payload = DestAddrMask::::new(ip.into(), mask.into(), SockAddrIn::default()); - let rtmsg = RtMessage::new_for_delete_gateway(payload); + let rtmsg = RtMessage::new_for_delete_gateway(payload, dest.is_host()); return rtmsg.execute(); } (IpAddr::V6(ip), IpAddr::V6(mask)) => { let payload = DestAddrMask::::new(ip.into(), mask.into(), SockAddrIn6::default()); - let rtmsg = RtMessage::new_for_delete_gateway(payload); + let rtmsg = RtMessage::new_for_delete_gateway(payload, dest.is_host()); return rtmsg.execute(); } _ => error!("Unsupported address for add route"), diff --git a/src/bsd/route.rs b/src/bsd/route.rs index 5bde180..e4da3ea 100644 --- a/src/bsd/route.rs +++ b/src/bsd/route.rs @@ -308,12 +308,16 @@ impl RtMessage { // TODO: check if gateway field and flags are needed. #[must_use] - pub(super) fn new_for_delete_gateway(payload: Payload) -> Self { + pub(super) fn new_for_delete_gateway(payload: Payload, is_host: bool) -> Self { + let mut flags = RTF_UP | RTF_STATIC | RTF_GATEWAY; + if is_host { + flags |= RTF_HOST; + } let header = RtMsgHdr::new( size_of::() as u16, MessageType::Delete, 0, - RTF_UP | RTF_STATIC | RTF_HOST | RTF_GATEWAY, + flags, RTA_DST | RTA_GATEWAY | RTA_NETMASK, ); diff --git a/src/error.rs b/src/error.rs index 4024179..51413b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,7 @@ use thiserror::Error; #[derive(Debug, Error)] +#[non_exhaustive] pub enum WireguardInterfaceError { #[error("Interface setup error: {0}")] Interface(String), diff --git a/src/utils.rs b/src/utils.rs index 1694e87..b952fc1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,7 +15,7 @@ use std::{ use crate::check_command_output_status; #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] use crate::{ - bsd::{add_gateway_host, add_linked_route, get_gateway}, + bsd::{add_gateway, add_linked_route, get_gateway}, net::IpAddrMask, IpVersion, }; @@ -238,7 +238,7 @@ pub(crate) fn add_peer_routing( peers: &[Peer], ifname: &str, ) -> Result<(), WireguardInterfaceError> { - use crate::bsd::delete_gateway_host; + use crate::bsd::delete_gateway; debug!("Adding peer routing for interface: {ifname}"); for peer in peers { @@ -253,14 +253,14 @@ pub(crate) fn add_peer_routing( default1 = IpAddrMask::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 1); // 128.0.0.0/1 default2 = IpAddrMask::new(IpAddr::V4(Ipv4Addr::new(128, 0, 0, 0)), 1); - gateway = get_gateway(IpVersion::IPv4).unwrap(); + gateway = get_gateway(IpVersion::IPv4); } else { // ::/1 default1 = IpAddrMask::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 1); // 8000::/1 default2 = IpAddrMask::new(IpAddr::V6(Ipv6Addr::new(0x8000, 0, 0, 0, 0, 0, 0, 0)), 1); - gateway = get_gateway(IpVersion::IPv6).unwrap(); + gateway = get_gateway(IpVersion::IPv6); } match add_linked_route(&default1, ifname) { Ok(()) => debug!("Route to {default1} has been added for interface {ifname}"), @@ -276,10 +276,10 @@ pub(crate) fn add_peer_routing( } if let Some(endpoint) = peer.endpoint { let host = IpAddrMask::host(endpoint.ip()); - if let Some(gateway) = gateway { + if let Ok(Some(gateway)) = gateway { // Try to silently remove route to host as it may already exist. - let _ = delete_gateway_host(&host); - match add_gateway_host(&host, gateway, false) { + let _ = delete_gateway(&host); + match add_gateway(&host, gateway, false) { Ok(()) => { debug!("Route to {host} has been added for gateway {gateway}"); } @@ -296,7 +296,7 @@ pub(crate) fn add_peer_routing( } else { IpAddr::V6(Ipv6Addr::LOCALHOST) }; - match add_gateway_host(&host, localhost, true) { + match add_gateway(&host, localhost, true) { Ok(()) => debug!("Blackhole route to {host} has been added"), Err(err) => { error!("Failed to add blackhole route to {host}: {err}"); @@ -332,7 +332,7 @@ pub(crate) fn clean_fwmark_rules(fwmark: u32) -> Result<(), WireguardInterfaceEr } /// Resolves domain name to [`SocketAddr`]. -pub fn resolve(addr: &str) -> Result { +pub(crate) fn resolve(addr: &str) -> Result { let error = || { WireguardInterfaceError::PeerConfigurationError(format!( "Failed to resolve address: {addr}" diff --git a/src/wgapi.rs b/src/wgapi.rs index 30da77b..87e82e6 100644 --- a/src/wgapi.rs +++ b/src/wgapi.rs @@ -17,7 +17,6 @@ pub struct WGApi { impl WGApi { /// Create new instance of `WGApi`. - #[must_use] pub fn new(ifname: String) -> Result { Ok(WGApi { ifname, diff --git a/src/wgapi_userspace.rs b/src/wgapi_userspace.rs index a3aebce..2a5cd55 100644 --- a/src/wgapi_userspace.rs +++ b/src/wgapi_userspace.rs @@ -9,12 +9,12 @@ use std::{ time::Duration, }; -#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] -use crate::bsd; #[cfg(target_os = "linux")] use crate::netlink; #[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "netbsd"))] use crate::utils::clear_dns; +#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] +use crate::{bsd, utils::resolve}; use crate::{ check_command_output_status, error::WireguardInterfaceError, @@ -235,6 +235,18 @@ impl WireguardInterfaceApi for WGApi { Ok(()) } + #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] + fn remove_endpoint_routing(&self, endpoint: &str) -> Result<(), WireguardInterfaceError> { + let endpoint_addr = resolve(endpoint)?; + let host = IpAddrMask::host(endpoint_addr.ip()); + match bsd::delete_gateway(&host) { + Ok(()) => debug!("Removed routing to {host}"), + Err(err) => debug!("Failed to remove routing to {host}: {err}"), + } + + Ok(()) + } + /// Remove WireGuard network interface. fn remove_interface(&self) -> Result<(), WireguardInterfaceError> { info!("Removing interface {}", self.ifname); diff --git a/src/wireguard_interface.rs b/src/wireguard_interface.rs index 420fa35..384febe 100644 --- a/src/wireguard_interface.rs +++ b/src/wireguard_interface.rs @@ -17,6 +17,12 @@ pub trait WireguardInterfaceApi { /// address. fn configure_peer_routing(&self, peers: &[Peer]) -> Result<(), WireguardInterfaceError>; + /// Remove routing to the given endpoint. + /// This is needed for proper routing table cleanup. + fn remove_endpoint_routing(&self, _endpoint: &str) -> Result<(), WireguardInterfaceError> { + Ok(()) + } + /// Updates configuration of an existing WireGuard interface. #[cfg(not(target_os = "windows"))] fn configure_interface(