fix(client): flush the OS' DNS cache whenever resources change (#5700)

Closes #5052

On my dev VMs:
- systemd-resolved = 15 ms to flush
- Windows = 600 ms to flush

I tested with the headless Clients on Linux and Windows and it fixes the
issue. On Windows I didn't replicate the issue with the GUI Client, on
Linux this patch also fixes it for the GUI Client.
This commit is contained in:
Reactor Scram
2024-07-03 21:14:43 +00:00
committed by GitHub
parent 2db32c247f
commit f6e99752ec
7 changed files with 38 additions and 16 deletions

View File

@@ -164,7 +164,7 @@ impl TunDeviceManager {
}
for route in self.routes.difference(&new_routes) {
delete_route(route, index, handle).await?;
remove_route(route, index, handle).await?;
}
self.routes = new_routes;
@@ -232,7 +232,7 @@ async fn add_route(route: &IpNetwork, idx: u32, handle: &Handle) -> Result<()> {
Ok(())
}
async fn delete_route(route: &IpNetwork, idx: u32, handle: &Handle) -> Result<()> {
async fn remove_route(route: &IpNetwork, idx: u32, handle: &Handle) -> Result<()> {
let message = match route {
IpNetwork::V4(ipnet) => make_route_v4(idx, handle, *ipnet).message_mut().clone(),
IpNetwork::V6(ipnet) => make_route_v6(idx, handle, *ipnet).message_mut().clone(),

View File

@@ -134,8 +134,6 @@ impl Tun {
self.remove_route(*old_route)?;
}
// TODO: Might be calling this more often than it needs
flush_dns().expect("Should be able to flush Windows' DNS cache");
self.routes = new_routes;
Ok(())
}
@@ -245,16 +243,6 @@ impl Tun {
}
}
/// Flush Windows' system-wide DNS cache
pub(crate) fn flush_dns() -> Result<()> {
tracing::info!("Flushing Windows DNS cache");
Command::new("powershell")
.creation_flags(CREATE_NO_WINDOW)
.args(["-Command", "Clear-DnsClientCache"])
.status()?;
Ok(())
}
// Moves packets from the user towards the Internet
fn start_recv_thread(
packet_tx: mpsc::Sender<wintun::Packet>,

View File

@@ -1,6 +1,6 @@
use anyhow::{bail, Context as _, Result};
use connlib_shared::tun_device_manager::linux::IFACE_NAME;
use std::{net::IpAddr, str::FromStr};
use std::{net::IpAddr, process::Command, str::FromStr};
mod etc_resolv_conf;
@@ -63,6 +63,19 @@ impl DnsController {
}
.context("Failed to control DNS")
}
/// Flush systemd-resolved's system-wide DNS cache
///
/// Does nothing if we're using other DNS control methods or none at all
pub(crate) fn flush(&self) -> Result<()> {
// Flushing is only implemented for systemd-resolved
if matches!(self.dns_control_method, Some(DnsControlMethod::Systemd)) {
tracing::debug!("Flushing systemd-resolved DNS cache...");
Command::new("resolvectl").arg("flush-caches").status()?;
tracing::debug!("Flushed DNS.");
}
Ok(())
}
}
/// Reads FIREZONE_DNS_CONTROL. Returns None if invalid or not set

View File

@@ -51,6 +51,19 @@ impl DnsController {
activate(dns_config).context("Failed to activate DNS control")?;
Ok(())
}
/// Flush Windows' system-wide DNS cache
///
/// `&self` is needed to match the Linux signature
pub(crate) fn flush(&self) -> Result<()> {
tracing::debug!("Flushing Windows DNS cache...");
Command::new("powershell")
.creation_flags(CREATE_NO_WINDOW)
.args(["-Command", "Clear-DnsClientCache"])
.status()?;
tracing::debug!("Flushed DNS.");
Ok(())
}
}
pub(crate) fn system_resolvers() -> Result<Vec<IpAddr>> {

View File

@@ -209,6 +209,9 @@ impl Handler {
let dur = instant.elapsed();
tracing::info!(?dur, "Connlib started");
}
// On every resources update, flush DNS to mitigate <https://github.com/firezone/firezone/issues/5052>
self.dns_controller.flush()?;
}
self.ipc_tx
.send(&msg)

View File

@@ -78,6 +78,7 @@ struct CliCommon {
max_partition_time: Option<humantime::Duration>,
}
/// Messages we get from connlib, including ones that aren't sent to IPC clients
enum InternalServerMsg {
Ipc(IpcServerMsg),
OnSetInterfaceConfig {
@@ -91,6 +92,7 @@ enum InternalServerMsg {
},
}
/// Messages that we can send to IPC clients
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum IpcServerMsg {
Ok,

View File

@@ -192,7 +192,10 @@ pub fn run_only_headless_client() -> Result<()> {
}) => return Err(anyhow!(error_msg).context("Firezone disconnected")),
InternalServerMsg::Ipc(IpcServerMsg::Ok)
| InternalServerMsg::Ipc(IpcServerMsg::OnTunnelReady)
| InternalServerMsg::Ipc(IpcServerMsg::OnUpdateResources(_)) => {}
| InternalServerMsg::Ipc(IpcServerMsg::OnUpdateResources(_)) => {
// On every resources update, flush DNS to mitigate <https://github.com/firezone/firezone/issues/5052>
dns_controller.flush()?;
}
InternalServerMsg::OnSetInterfaceConfig { ipv4, ipv6, dns } => {
tun_device.set_ips(ipv4, ipv6).await?;
dns_controller.set_dns(&dns).await?;