Skip to content

Commit

Permalink
feat(windows): group ip addresses to interfaces based on interface index
Browse files Browse the repository at this point in the history
On Linux, all ip addresses of each interface are stored in a single NetworkInterface structure.
On windows each IP address was stored in a distinct NetworkInterface structure.
With this change, windows output structure is now the same as on Linux -
one NetworkInterface structure for each network interface.
  • Loading branch information
scanban committed Apr 23, 2024
1 parent 962fdf8 commit c2998eb
Showing 1 changed file with 138 additions and 90 deletions.
228 changes: 138 additions & 90 deletions src/target/windows.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::ffi::c_void;
use std::fmt::Pointer;
use std::mem::size_of;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::ptr::null_mut;
use std::slice::from_raw_parts;
use std::iter::Iterator;
use std::marker::PhantomData;

use libc::{free, malloc, wchar_t, wcslen};
use winapi::{
Expand All @@ -16,18 +19,18 @@ use winapi::{
winerror,
},
um::{
iptypes::{IP_ADAPTER_ADDRESSES_LH, IP_ADAPTER_UNICAST_ADDRESS_LH},
iptypes::{IP_ADAPTER_ADDRESSES, IP_ADAPTER_UNICAST_ADDRESS, IP_ADAPTER_PREFIX},
iphlpapi::GetAdaptersAddresses,
},
};

use crate::utils::hex::HexSlice;
use crate::utils::ffialloc::FFIAlloc;
use crate::{Error, NetworkInterface, NetworkInterfaceConfig, Result};
use crate::{Addr, Error, NetworkInterface, NetworkInterfaceConfig, Result, V4IfAddr, V6IfAddr};
use crate::interface::Netmask;

/// An alias for `IP_ADAPTER_ADDRESSES_LH`
type AdapterAddress = IP_ADAPTER_ADDRESSES_LH;
type AdapterAddress = IP_ADAPTER_ADDRESSES;

/// A constant to store `winapi::shared::ws2def::AF_INET` casted as `u16`
const AF_INET: u16 = winapi::shared::ws2def::AF_INET as u16;
Expand All @@ -44,6 +47,24 @@ const GET_ADAPTERS_ADDRESSES_FAMILY: u32 = AF_UNSPEC as u32;
/// A constant to store `winapi::um::iptypes::GAA_FLAG_INCLUDE_PREFIX`
const GET_ADAPTERS_ADDRESSES_FLAGS: ULONG = winapi::um::iptypes::GAA_FLAG_INCLUDE_PREFIX;

type MacAddress = Option<String>;
macro_rules! iterable_raw_pointer {
($t: ty, $n: ident) => {
impl IterableRawPointer for $t {
type Pointer = *const $t;
type Value = $t;

fn next(&self) -> Self::Pointer {
self.$n
}
}
};
}

iterable_raw_pointer!(IP_ADAPTER_ADDRESSES, Next);
iterable_raw_pointer!(IP_ADAPTER_UNICAST_ADDRESS, Next);
iterable_raw_pointer!(IP_ADAPTER_PREFIX, Next);

impl NetworkInterfaceConfig for NetworkInterface {
fn show() -> Result<Vec<NetworkInterface>> {
// Allocate a 15 KB buffer to start with.
Expand Down Expand Up @@ -96,70 +117,48 @@ impl NetworkInterfaceConfig for NetworkInterface {
}?;

// iterate over the contained structs
let mut adapter_address_it = adapter_address.as_ptr();
let mut network_interfaces: Vec<NetworkInterface> = Vec::new();

while !adapter_address_it.is_null() {
let address_name = make_adapter_address_name(adapter_address_it)?;

let index = get_adapter_address_index(adapter_address_it)?;

// see https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples
let mac_addr_len = unsafe { (*adapter_address_it).PhysicalAddressLength } as _;
let mac_addr = match mac_addr_len {
0 => None,
len => Some(format!(
"{}",
HexSlice::new(unsafe { &(*adapter_address_it).PhysicalAddress[..len] })
)),
let mut network_interfaces = Vec::<NetworkInterface>::new();

for adapter_address in RawPointerWrapper::new(adapter_address.as_ptr()) {
let name = make_adapter_address_name(adapter_address)?;
let index = get_adapter_address_index(adapter_address)?;
let mac_addr = make_mac_address(adapter_address);
let mut network_interface = NetworkInterface {
name,
addr: Vec::new(),
mac_addr,
index,
};

// Find interface addresses
let mut current_unicast_address = unsafe { (*adapter_address_it).FirstUnicastAddress };

while !current_unicast_address.is_null() {
let address = unsafe { (*current_unicast_address).Address };

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 netmask = make_ipv4_netmask(&current_unicast_address);
let network_interface = NetworkInterface::new_afinet(
&address_name,
addr,
netmask,
lookup_ipv4_broadcast_addr(adapter_address_it, &sockaddr),
index,
)
.with_mac_addr(mac_addr.clone());

network_interfaces.push(network_interface);
}
AF_INET6 => {
let sockaddr: *mut SOCKADDR_IN6 = address.lpSockaddr as *mut SOCKADDR_IN6;
let addr = make_ipv6_addr(&sockaddr)?;
let netmask = make_ipv6_netmask(&sockaddr);
let network_interface = NetworkInterface::new_afinet6(
&address_name,
addr,
netmask,
None,
index,
)
.with_mac_addr(mac_addr.clone());

network_interfaces.push(network_interface);
}
_ => {}
}

if !current_unicast_address.is_null() {
current_unicast_address = unsafe { (*current_unicast_address).Next };
}
for current_unicast_address in
RawPointerWrapper::new(adapter_address.FirstUnicastAddress)
{
let address = current_unicast_address.Address;

network_interface
.addr
.push(match unsafe { (*address.lpSockaddr).sa_family } {
AF_INET => {
let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN) };
Addr::V4(V4IfAddr {
ip: make_ipv4_addr(sockaddr),
broadcast: lookup_ipv4_broadcast_addr(adapter_address, sockaddr),
netmask: make_ipv4_netmask(current_unicast_address),
})
}
AF_INET6 => {
let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN6) };
Addr::V6(V6IfAddr {
ip: make_ipv6_addr(sockaddr)?,
broadcast: None,
netmask: make_ipv6_netmask(sockaddr),
})
}
_ => continue,
});
}

adapter_address_it = unsafe { (*adapter_address_it).Next };
network_interfaces.push(network_interface);
}

Ok(network_interfaces)
Expand All @@ -184,39 +183,35 @@ impl NetworkInterfaceConfig for NetworkInterface {
// 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: *const IP_ADAPTER_ADDRESSES_LH,
unicast_ip: &*mut SOCKADDR_IN,
adapter_address: &IP_ADAPTER_ADDRESSES,
unicast_ip: &SOCKADDR_IN,
) -> Option<Ipv4Addr> {
let mut prefix_address = unsafe { (*adapter_address).FirstPrefix };
let mut prefix_index_v4 = 0;
let mut broadcast_index: Option<i32> = None;

// Find adapter
while !prefix_address.is_null() {
let address = unsafe { (*prefix_address).Address };
for prefix_address in RawPointerWrapper::new(adapter_address.FirstPrefix) {
let address = prefix_address.Address;

if unsafe { (*address.lpSockaddr).sa_family } == AF_INET {
let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN) };

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));
return Some(make_ipv4_addr(sockaddr));
}
} else if prefix_index_v4 % 3 == 1
&& ipv4_addr_equal(&(address.lpSockaddr as *mut SOCKADDR_IN), unicast_ip)
{
} else if prefix_index_v4 % 3 == 1 && ipv4_addr_equal(sockaddr, 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: *const AdapterAddress) -> Result<String> {
let address_name = unsafe { (*adapter_address).FriendlyName };
fn make_adapter_address_name(adapter_address: &AdapterAddress) -> Result<String> {
let address_name = adapter_address.FriendlyName;
let address_name_length = unsafe { wcslen(address_name as *const wchar_t) };
let byte_slice = unsafe { from_raw_parts(address_name, address_name_length) };
let string = String::from_utf16(byte_slice).map_err(Error::from)?;
Expand All @@ -225,16 +220,16 @@ fn make_adapter_address_name(adapter_address: *const AdapterAddress) -> Result<S
}

/// Creates a `Ipv6Addr` from a `SOCKADDR_IN6`
fn make_ipv6_addr(sockaddr: &*mut SOCKADDR_IN6) -> Result<Ipv6Addr> {
let address_bytes = unsafe { (*(*sockaddr)).sin6_addr.u.Byte() };
fn make_ipv6_addr(sockaddr: &SOCKADDR_IN6) -> Result<Ipv6Addr> {
let address_bytes = unsafe { sockaddr.sin6_addr.u.Byte() };
let ip = Ipv6Addr::from(*address_bytes);

Ok(ip)
}

/// Creates a `Ipv4Addr` from a `SOCKADDR_IN`
fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Ipv4Addr {
let address = unsafe { (*(*sockaddr)).sin_addr.S_un.S_addr() };
fn make_ipv4_addr(sockaddr: &SOCKADDR_IN) -> Ipv4Addr {
let address = unsafe { sockaddr.sin_addr.S_un.S_addr() };

if cfg!(target_endian = "little") {
// due to a difference on how bytes are arranged on a
Expand All @@ -248,10 +243,10 @@ fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Ipv4Addr {
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() };
/// Compare 2 ipv4 addresses.
fn ipv4_addr_equal(sockaddr1: &SOCKADDR_IN, sockaddr2: &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
}

Expand All @@ -260,9 +255,9 @@ fn ipv4_addr_equal(sockaddr1: &*mut SOCKADDR_IN, sockaddr2: &*mut SOCKADDR_IN) -
///
/// An implementation of `GetIpAddrTable` to get all available network interfaces would be required
/// in order to support previous versions of Windows.
fn make_ipv4_netmask(unicast_address: &*mut IP_ADAPTER_UNICAST_ADDRESS_LH) -> Netmask<Ipv4Addr> {
fn make_ipv4_netmask(unicast_address: &IP_ADAPTER_UNICAST_ADDRESS) -> Netmask<Ipv4Addr> {
let mut mask: c_ulong = 0;
let on_link_prefix_length = unsafe { (*(*unicast_address)).OnLinkPrefixLength };
let on_link_prefix_length = unicast_address.OnLinkPrefixLength;
unsafe {
ConvertLengthToIpv4Mask(on_link_prefix_length as u32, &mut mask as *mut c_ulong);
}
Expand All @@ -279,12 +274,25 @@ fn make_ipv4_netmask(unicast_address: &*mut IP_ADAPTER_UNICAST_ADDRESS_LH) -> Ne
Some(Ipv4Addr::from(mask))
}

fn make_ipv6_netmask(_sockaddr: &*mut SOCKADDR_IN6) -> Netmask<Ipv6Addr> {
fn make_ipv6_netmask(_sockaddr: &SOCKADDR_IN6) -> Netmask<Ipv6Addr> {
None
}

fn get_adapter_address_index(adapter_address: *const AdapterAddress) -> Result<u32> {
let adapter_luid = &unsafe { (*adapter_address).Luid } as *const IF_LUID;
/// Creates MacAddress from AdapterAddress
fn make_mac_address(adapter_address: &AdapterAddress) -> MacAddress {
// see https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples
let mac_addr_len = adapter_address.PhysicalAddressLength as usize;
match mac_addr_len {
0 => None,
len => Some(format!(
"{}",
HexSlice::new(&adapter_address.PhysicalAddress[..len])
)),
}
}

fn get_adapter_address_index(adapter_address: &AdapterAddress) -> Result<u32> {
let adapter_luid = &adapter_address.Luid as *const IF_LUID;

let index = &mut 0u32 as *mut u32;

Expand All @@ -297,6 +305,46 @@ fn get_adapter_address_index(adapter_address: *const AdapterAddress) -> Result<u
}
}

/// Trait for linked lists in Windows API structures iteration
trait IterableRawPointer {
type Pointer;
type Value;

/// Returns: pointer to the next element in the linked list
/// null at the end
fn next(&self) -> Self::Pointer;
}

/// Raw pointer container
struct RawPointerWrapper<'a, T>(*const T, PhantomData<&'a T>)
where
T: IterableRawPointer<Value = T, Pointer = *const T>;

impl<'a, T> RawPointerWrapper<'a, T>
where
T: IterableRawPointer<Value = T, Pointer = *const T>,
{
fn new(ptr: *const T) -> RawPointerWrapper<'a, T> {
Self(ptr, PhantomData)
}
}

/// Iterator implementation for RawPointer
impl<'a, T> Iterator for RawPointerWrapper<'a, T>
where
T: IterableRawPointer<Value = T, Pointer = *const T>,
{
type Item = &'a T::Value;

fn next(&mut self) -> Option<Self::Item> {
let ret = unsafe { self.0.as_ref() };
if let Some(v) = ret {
self.0 = v.next();
}
ret
}
}

#[cfg(test)]
mod tests {
use std::{process::Command, cmp::min};
Expand Down

0 comments on commit c2998eb

Please sign in to comment.