From 7b8d85381f4fb730f6cbaa00e0a418aa585c49a0 Mon Sep 17 00:00:00 2001 From: "Morten Laursen (win)" Date: Tue, 24 Oct 2023 13:57:07 +0200 Subject: [PATCH 1/2] Fix Windows broadcast address lookup When one network adapter has multiple ipv4 addresses, the previous implementation returned the same broadcast address for all IPs. This is fixed by correctly looking up the broadcast address in the prefix list. Tested on 3 different Win 10 Home/Pro PCs. --- src/target/windows.rs | 125 +++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 38 deletions(-) diff --git a/src/target/windows.rs b/src/target/windows.rs index f9b7e59..1624ad5 100644 --- a/src/target/windows.rs +++ b/src/target/windows.rs @@ -103,38 +103,6 @@ impl NetworkInterfaceConfig for NetworkInterface { let index = get_adapter_address_index(&adapter_address)?; - // Find broadcast address - // - // see https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh - // - // On Windows Vista and later, the linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix - // member include three IP adapter prefixes for each IP address assigned to the adapter. These include - // 0. the host IP address prefix - // 1. the subnet IP address prefix - // 2. and the subnet broadcast IP address prefix. << we want this - // In addition, for each adapter there is a - // 3. multicast address prefix - // 4. and a broadcast address prefix.sb - // - // We only care for AF_INET entry with index 2. - let mut current_prefix_address = unsafe { (*adapter_address).FirstPrefix }; - let mut prefix_index_ipv4 = 0; - let mut bc_addr_ipv4 = None; - while !current_prefix_address.is_null() { - let address = unsafe { (*current_prefix_address).Address }; - if unsafe { (*address.lpSockaddr).sa_family } == AF_INET { - // only consider broadcast for IPv4 - if prefix_index_ipv4 == 2 { - // 3rd IPv4 entry is broadcast address - let sockaddr: *mut SOCKADDR_IN = address.lpSockaddr as *mut SOCKADDR_IN; - bc_addr_ipv4 = Some(make_ipv4_addr(&sockaddr)?); - break; - } - prefix_index_ipv4 += 1; // only increase for AF_INET - } - current_prefix_address = unsafe { (*current_prefix_address).Next }; - } - // see https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples let mac_addr_len = unsafe { (*adapter_address).PhysicalAddressLength } as _; let mac_addr = match mac_addr_len { @@ -154,13 +122,13 @@ impl NetworkInterfaceConfig for NetworkInterface { match unsafe { (*address.lpSockaddr).sa_family } { AF_INET => { let sockaddr: *mut SOCKADDR_IN = address.lpSockaddr as *mut SOCKADDR_IN; - let addr = make_ipv4_addr(&sockaddr)?; + let addr = make_ipv4_addr(&sockaddr); let netmask = make_ipv4_netmask(¤t_unicast_address); let network_interface = NetworkInterface::new_afinet( &address_name, addr, netmask, - bc_addr_ipv4, + lookup_ipv4_broadcast_addr(&adapter_address, &sockaddr), index, ) .with_mac_addr(mac_addr.clone()); @@ -201,6 +169,56 @@ impl NetworkInterfaceConfig for NetworkInterface { } } +// Find broadcast address +// +// see https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh +// +// On Windows Vista and later, the linked IP_ADAPTER_PREFIX structures pointed +// to by the FirstPrefix member include three IP adapter prefixes for each IPv4 +// address assigned to the adapter. These include +// 0. the host IP address prefix +// 1. the subnet IP address prefix +// 2. and the subnet broadcast IP address prefix. << we want these +// In addition, for each adapter with n IP adresses there are (not used) +// 3*n + 0. multicast address prefix +// 3*n + 1. and a broadcast address prefix.sb +// +// The order of addresses in prefix list and unicast list is not guaranteed to +// be the same, so we search for the unicast address in the prefix list, and +// then the broadcast address is next in list. +fn lookup_ipv4_broadcast_addr( + adapter_address: &*mut IP_ADAPTER_ADDRESSES_LH, + unicast_ip: &*mut SOCKADDR_IN, +) -> Option { + let mut prefix_address = unsafe { (*(*adapter_address)).FirstPrefix }; + let mut prefix_index_v4 = 0; + let mut broadcast_index: Option = None; + + // Find adapter + while !prefix_address.is_null() { + let address = unsafe { (*prefix_address).Address }; + + if unsafe { (*address.lpSockaddr).sa_family } == AF_INET { + if let Some(broadcast_index) = broadcast_index { + if prefix_index_v4 == broadcast_index { + let sockaddr: *mut SOCKADDR_IN = address.lpSockaddr as *mut SOCKADDR_IN; + return Some(make_ipv4_addr(&sockaddr)); + } + } else { + if prefix_index_v4 % 3 == 1 + && ipv4_addr_equal(&(address.lpSockaddr as *mut SOCKADDR_IN), unicast_ip) + { + broadcast_index = Some(prefix_index_v4 + 1); + } + } + prefix_index_v4 += 1; + } + + prefix_address = unsafe { (*prefix_address).Next }; + } + None +} + /// Retrieves the network interface name fn make_adapter_address_name(adapter_address: &*mut AdapterAddress) -> Result { let address_name = unsafe { (*(*adapter_address)).FriendlyName }; @@ -220,7 +238,7 @@ fn make_ipv6_addr(sockaddr: &*mut SOCKADDR_IN6) -> Result { } /// Creates a `Ipv4Addr` from a `SOCKADDR_IN` -fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Result { +fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Ipv4Addr { let address = unsafe { (*(*sockaddr)).sin_addr.S_un.S_addr() }; if cfg!(target_endian = "little") { @@ -229,10 +247,17 @@ fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Result { // on CPU endianess to avoid having twisted IP addresses // // refer: https://github.com/rust-lang/rust/issues/48819 - return Ok(Ipv4Addr::from(address.swap_bytes())); + return Ipv4Addr::from(address.swap_bytes()); } - Ok(Ipv4Addr::from(*address)) + Ipv4Addr::from(*address) +} + +/// Compare 2 ipv4 addresses. Caller must ensure pointers are non-null. +fn ipv4_addr_equal(sockaddr1: &*mut SOCKADDR_IN, sockaddr2: &*mut SOCKADDR_IN) -> bool { + let address1 = unsafe { (*(*sockaddr1)).sin_addr.S_un.S_addr() }; + let address2 = unsafe { (*(*sockaddr2)).sin_addr.S_un.S_addr() }; + address1 == address2 } /// This function relies on the `GetAdapterAddresses` API which is available only on Windows Vista @@ -282,7 +307,7 @@ fn get_adapter_address_index(adapter_address: &*mut AdapterAddress) -> Result Date: Wed, 25 Oct 2023 09:14:43 +0200 Subject: [PATCH 2/2] fix linter warnings All changes are from cargo clippy --fix --- src/target/windows.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/target/windows.rs b/src/target/windows.rs index 1624ad5..8eb7e8c 100644 --- a/src/target/windows.rs +++ b/src/target/windows.rs @@ -204,12 +204,10 @@ fn lookup_ipv4_broadcast_addr( let sockaddr: *mut SOCKADDR_IN = address.lpSockaddr as *mut SOCKADDR_IN; return Some(make_ipv4_addr(&sockaddr)); } - } else { - if prefix_index_v4 % 3 == 1 - && ipv4_addr_equal(&(address.lpSockaddr as *mut SOCKADDR_IN), unicast_ip) - { - broadcast_index = Some(prefix_index_v4 + 1); - } + } else if prefix_index_v4 % 3 == 1 + && ipv4_addr_equal(&(address.lpSockaddr as *mut SOCKADDR_IN), unicast_ip) + { + broadcast_index = Some(prefix_index_v4 + 1); } prefix_index_v4 += 1; } @@ -326,7 +324,7 @@ mod tests { } }) .collect(); - assert!(mac_addr_list.len() > 0); + assert!(!mac_addr_list.is_empty()); let interfaces = NetworkInterface::show().unwrap(); for mac_addr in mac_addr_list {