From a46dba43e62b7cfcb3a2a753d659a3b571ec8737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 2 Oct 2024 10:37:06 +0200 Subject: [PATCH] Add remove_endpoint_routing() --- src/bsd/mod.rs | 12 ++++-------- src/bsd/route.rs | 8 ++++++-- src/error.rs | 1 + src/utils.rs | 18 +++++++++--------- src/wgapi_userspace.rs | 14 +++++++++++++- src/wireguard_interface.rs | 6 ++++++ 6 files changed, 39 insertions(+), 20 deletions(-) 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 2824eb9..88230fd 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 1dfe1d8..9fa17b8 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, }; @@ -227,7 +227,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 { @@ -242,14 +242,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}"), @@ -265,10 +265,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}"); } @@ -285,7 +285,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}"); @@ -321,7 +321,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; addr.to_socket_addrs() .map_err(|_| error())? diff --git a/src/wgapi_userspace.rs b/src/wgapi_userspace.rs index 8e7fb28..cd660b0 100644 --- a/src/wgapi_userspace.rs +++ b/src/wgapi_userspace.rs @@ -16,9 +16,10 @@ use crate::netlink; #[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "netbsd"))] use crate::utils::clear_dns; use crate::{ + bsd::delete_gateway, check_command_output_status, error::WireguardInterfaceError, - utils::{add_peer_routing, configure_dns}, + utils::{add_peer_routing, configure_dns, resolve}, wgapi::{Userspace, WGApi}, wireguard_interface::WireguardInterfaceApi, Host, InterfaceConfiguration, IpAddrMask, Key, Peer, @@ -235,6 +236,17 @@ impl WireguardInterfaceApi for WGApi { Ok(()) } + fn remove_endpoint_routing(&self, endpoint: &str) -> Result<(), WireguardInterfaceError> { + let endpoint_addr = resolve(endpoint)?; + let host = IpAddrMask::host(endpoint_addr.ip()); + match 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(