Skip to content

Commit c335c5c

Browse files
authored
handle disabled IPv6 on Windows machines (#131)
* check if IPv6 is available and filter addresses * bump version
1 parent db05064 commit c335c5c

3 files changed

Lines changed: 78 additions & 6 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "defguard_wireguard_rs"
3-
version = "0.9.6"
3+
version = "0.9.7"
44
edition = "2024"
55
rust-version = "1.87"
66
description = "A unified multi-platform high-level API for managing WireGuard interfaces"

src/wgapi_windows.rs

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ static WIREGUARD_DLL: LazyLock<Mutex<Wireguard>> = LazyLock::new(|| {
5151
)
5252
});
5353

54+
const IPV4_LABEL: &str = "IPv4";
55+
const IPV6_LABEL: &str = "IPv6";
56+
5457
#[derive(Debug, Error)]
5558
pub enum WindowsError {
5659
#[error("Empty interface array")]
@@ -304,15 +307,64 @@ impl WireguardInterfaceApi for WGApi<Kernel> {
304307

305308
// Configure the interface
306309
debug!("Applying configuration for adapter {}", self.ifname);
307-
let interface = wireguard_nt::SetInterface {
310+
let mut interface = wireguard_nt::SetInterface {
308311
listen_port: Some(config.port),
309312
public_key: None, // derived from private key
310313
private_key: Some(Key::from_str(&config.prvkey)?.as_array()),
311314
peers,
312315
};
313316
adapter.set_config(&interface).map_err(WindowsError::from)?;
314317

315-
// Set adapter addresses
318+
// Check which IP families are available on this adapter before attempting
319+
// to create routes.
320+
let adapter_luid = adapter.get_luid();
321+
let mut ipv4_available = false;
322+
let mut ipv6_available = false;
323+
for (family_name, family) in [(IPV4_LABEL, AF_INET), (IPV6_LABEL, AF_INET6)] {
324+
let mut row = MIB_IPINTERFACE_ROW::default();
325+
unsafe { InitializeIpInterfaceEntry(&mut row) };
326+
row.InterfaceLuid = unsafe { std::mem::transmute::<u64, NET_LUID_LH>(adapter_luid) };
327+
row.Family = ADDRESS_FAMILY(family.0);
328+
let err = unsafe { GetIpInterfaceEntry(&mut row) };
329+
if err.0 == 0 {
330+
debug!(
331+
"IP interface {family_name} for {ifname}: connected={conn}, if_index={idx}, mtu={mtu}",
332+
ifname = self.ifname,
333+
conn = row.Connected,
334+
idx = row.InterfaceIndex,
335+
mtu = row.NlMtu
336+
);
337+
if family == AF_INET {
338+
ipv4_available = true;
339+
} else {
340+
ipv6_available = true;
341+
}
342+
} else {
343+
info!(
344+
"IP interface {family_name} unavailable on {ifname} (luid={luid:#018x}): {err:#x} - skipping {family_name} routes",
345+
ifname = self.ifname,
346+
luid = adapter_luid,
347+
err = err.0
348+
);
349+
}
350+
}
351+
if !ipv4_available && !ipv6_available {
352+
Err(WindowsError::AdapterNotFound(self.ifname.clone()))?
353+
}
354+
355+
// Strip allowed IPs for unavailable families so CreateIpForwardEntry2
356+
// inside set_default_route only attempts routes that can succeed.
357+
if !ipv4_available || !ipv6_available {
358+
for peer in &mut interface.peers {
359+
peer.allowed_ips.retain(|ip| match ip {
360+
IpNet::V4(_) => ipv4_available,
361+
IpNet::V6(_) => ipv6_available,
362+
});
363+
}
364+
}
365+
366+
// Set adapter addresses. Skip addresses for families that are not
367+
// available on this adapter (e.g. IPv6 disabled system-wide).
316368
debug!(
317369
"Assigning addresses to adapter {}: {:?}",
318370
self.ifname, config.addresses
@@ -321,8 +373,28 @@ impl WireguardInterfaceApi for WGApi<Kernel> {
321373
.addresses
322374
.iter()
323375
.filter_map(|ip| match ip.address {
324-
IpAddr::V4(addr) => Some(IpNet::V4(Ipv4Net::new(addr, ip.cidr).ok()?)),
325-
IpAddr::V6(addr) => Some(IpNet::V6(Ipv6Net::new(addr, ip.cidr).ok()?)),
376+
IpAddr::V4(addr) => {
377+
if ipv4_available {
378+
Some(IpNet::V4(Ipv4Net::new(addr, ip.cidr).ok()?))
379+
} else {
380+
debug!(
381+
"Skipping IPv4 address {}: unavailable on adapter",
382+
ip.address
383+
);
384+
None
385+
}
386+
}
387+
IpAddr::V6(addr) => {
388+
if ipv6_available {
389+
Some(IpNet::V6(Ipv6Net::new(addr, ip.cidr).ok()?))
390+
} else {
391+
debug!(
392+
"Skipping IPv6 address {}: unavailable on adapter",
393+
ip.address
394+
);
395+
None
396+
}
397+
}
326398
})
327399
.collect();
328400
adapter

0 commit comments

Comments
 (0)