Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Also return the interface name #11

Merged
merged 23 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ homepage = "https://github.com/mozilla/mtu/"
repository = "https://github.com/mozilla/mtu/"
authors = ["The Mozilla Necko Team <[email protected]>"]
readme = "README.md"
version = "0.1.1"
version = "0.1.2"
edition = "2021"
license = "MIT OR Apache-2.0"
# Don't increase beyond what Firefox is currently using:
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ A crate to return the maximum transmission unit (MTU) of the local network inter
This crate exports a single function

```rust
pub fn interface_mtu(remote: &SocketAddr) -> Result<usize, Error>
pub fn interface_and_mtu(remote: &SocketAddr) -> Result<(String, usize), Error>
```

that returns the MTU of the local network interface towards the `remote` destination, or an `Error` when the MTU could not be determined. It supports both IPv4 and IPv6.
that returns the interface name and MTU of the local network interface used for transmission towards the `remote` destination, or an `Error` when the MTU could not be determined. It supports both IPv4 and IPv6.

## Supported Platforms

Expand All @@ -22,6 +22,8 @@ that returns the MTU of the local network interface towards the `remote` destina

The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some platforms for some remote destinations. (For example, loopback destinations on Windows.)

The returned interface name is obtained from the operating system.

## Contributing

We're happy to receive PRs that improve this crate. Please take a look at our [community guidelines](CODE_OF_CONDUCT.md) beforehand.
76 changes: 52 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,34 @@
mod win_bindings;

/// Prepare a default error result.
fn default_result<T>() -> Result<T, Error> {
fn default_result<T>() -> Result<(String, T), Error> {
Err(Error::new(
ErrorKind::NotFound,
"Local interface MTU not found",
))
}

/// Return the maximum transmission unit (MTU) of the local network interface towards the
/// destination [`SocketAddr`] given in `remote`.
/// Return the interface name and the maximum transmission unit (MTU) of the local network
/// interface towards the destination [`SocketAddr`] given in `remote`.
///
/// The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some
/// platforms for some remote destinations. (For example, loopback destinations on
/// Windows.)
///
/// The returned interface name is obtained from the operating system.
///
/// # Examples
///
/// ```
/// let saddr = "127.0.0.1:443".parse().unwrap();
/// let mtu = mtu::interface_mtu(&saddr).unwrap();
/// println!("MTU towards {saddr:?} is {mtu}");
/// let (name, mtu) = mtu::interface_and_mtu(&saddr).unwrap();
/// println!("MTU towards {saddr:?} is {mtu} on {name}");
/// ```
///
/// # Errors
///
/// This function returns an error if the local interface MTU cannot be determined.
pub fn interface_mtu(remote: &SocketAddr) -> Result<usize, Error> {
pub fn interface_and_mtu(remote: &SocketAddr) -> Result<(String, usize), Error> {
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
#[allow(unused_assignments)] // Yes, res is reassigned in the platform-specific code.
let mut res = default_result();
Expand All @@ -64,27 +66,21 @@

#[cfg(any(target_os = "macos", target_os = "linux"))]
{
res = interface_mtu_linux_macos(&socket);
res = interface_and_mtu_linux_macos(&socket);
}

#[cfg(target_os = "windows")]
{
res = interface_mtu_windows(&socket);
res = interface_and_mtu_windows(&socket);
}
}

trace!("MTU towards {remote:?} is {res:?}");
res
}

#[doc(hidden)]
#[deprecated(since = "0.1.2", note = "Use `interface_mtu()` instead")]
pub fn get_interface_mtu(remote: &SocketAddr) -> Result<usize, Error> {
interface_mtu(remote)
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
fn interface_mtu_linux_macos(socket: &UdpSocket) -> Result<usize, Error> {
fn interface_and_mtu_linux_macos(socket: &UdpSocket) -> Result<(String, usize), Error> {
use std::ffi::{c_int, CStr};
#[cfg(target_os = "linux")]
use std::{ffi::c_char, mem, os::fd::AsRawFd};
Expand Down Expand Up @@ -158,7 +154,9 @@
&& name == iface
{
let data = unsafe { &*(ifa.ifa_data as *const if_data) };
res = usize::try_from(data.ifi_mtu).or(res);
if let Ok(mtu) = usize::try_from(data.ifi_mtu) {
res = Ok((iface.to_string(), mtu));
}
break;
}
}
Expand All @@ -175,8 +173,8 @@
});
if unsafe { ioctl(socket.as_raw_fd(), libc::SIOCGIFMTU, &ifr) } != 0 {
res = Err(Error::last_os_error());
} else {
res = unsafe { usize::try_from(ifr.ifr_ifru.ifru_mtu).or(res) };
} else if let Ok(mtu) = usize::try_from(unsafe { ifr.ifr_ifru.ifru_mtu }) {
res = Ok((iface.to_string(), mtu));
}
}
}
Expand All @@ -186,12 +184,13 @@
}

#[cfg(target_os = "windows")]
fn interface_mtu_windows(socket: &UdpSocket) -> Result<usize, Error> {
fn interface_and_mtu_windows(socket: &UdpSocket) -> Result<(String, usize), Error> {
use core::str;
use std::{ffi::c_void, slice};

use win_bindings::{
FreeMibTable, GetIpInterfaceTable, GetUnicastIpAddressTable, AF_INET, AF_INET6, AF_UNSPEC,
MIB_IPINTERFACE_ROW, MIB_IPINTERFACE_TABLE, MIB_UNICASTIPADDRESS_ROW,
if_indextoname, FreeMibTable, GetIpInterfaceTable, GetUnicastIpAddressTable, AF_INET,
AF_INET6, AF_UNSPEC, MIB_IPINTERFACE_ROW, MIB_IPINTERFACE_TABLE, MIB_UNICASTIPADDRESS_ROW,
MIB_UNICASTIPADDRESS_TABLE, NO_ERROR,
};

Expand Down Expand Up @@ -243,7 +242,16 @@
// For the matching address, find local interface and its MTU.
for iface in ifaces {
if iface.InterfaceIndex == addr.InterfaceIndex {
res = iface.NlMtu.try_into().or(res);
if let Ok(mtu) = iface.NlMtu.try_into() {
let mut name = [0u8; 256]; // IF_NAMESIZE not available?
larseggert marked this conversation as resolved.
Show resolved Hide resolved
if unsafe { !if_indextoname(iface.InterfaceIndex, &mut name).is_null() } {
if let Ok(name) = str::from_utf8(&name) {
res = Ok((name.to_string(), mtu));
}
} else {
res = Err(Error::last_os_error());
}
}

Check warning on line 254 in src/lib.rs

View check run for this annotation

Codecov / codecov/patch

src/lib.rs#L251-L254

Added lines #L251 - L254 were not covered by tests
break 'addr_loop;
}
}
Expand All @@ -256,6 +264,18 @@
res
}

#[doc(hidden)]
#[deprecated(since = "0.1.2", note = "Use `interface_and_mtu()` instead")]
pub fn interface_mtu(remote: &SocketAddr) -> Result<usize, Error> {
interface_and_mtu(remote).map(|(_, mtu)| mtu)
}

#[doc(hidden)]
#[deprecated(since = "0.1.2", note = "Use `interface_and_mtu()` instead")]
pub fn get_interface_mtu(remote: &SocketAddr) -> Result<usize, Error> {
interface_and_mtu(remote).map(|(_, mtu)| mtu)
}

#[cfg(test)]
mod test {
use std::net::ToSocketAddrs;
Expand All @@ -268,8 +288,8 @@
.unwrap()
.find(|a| a.is_ipv4() == ipv4);
if let Some(addr) = addr {
match super::interface_mtu(&addr) {
Ok(mtu) => assert_eq!(mtu, expected),
match super::interface_and_mtu(&addr) {
Ok((_, mtu)) => assert_eq!(mtu, expected),
Err(e) => {
// Some GitHub runners don't have IPv6. Just warn if we can't get the MTU.
assert!(addr.is_ipv6());
Expand Down Expand Up @@ -312,4 +332,12 @@
fn default_interface_mtu_v6() {
check_mtu("ietf.org:443", false, 1500);
}

#[test]
#[allow(deprecated)] // Purpose of the test is to cover deprecated functions.
fn deprecated_functions() {
let addr = "localhost:443".to_socket_addrs().unwrap().next().unwrap();
assert!(super::interface_mtu(&addr).is_ok());
assert!(super::get_interface_mtu(&addr).is_ok());
}
}
8 changes: 8 additions & 0 deletions src/win_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ pub unsafe fn GetUnicastIpAddressTable(
windows_targets::link!("iphlpapi.dll" "system" fn GetUnicastIpAddressTable(family : ADDRESS_FAMILY, table : *mut *mut MIB_UNICASTIPADDRESS_TABLE) -> WIN32_ERROR);
GetUnicastIpAddressTable(family, table)
}
#[inline]
pub unsafe fn if_indextoname(
interfaceindex: u32,
interfacename: &mut [u8; 256],
) -> windows_core::PSTR {
windows_targets::link!("iphlpapi.dll" "system" fn if_indextoname(interfaceindex : u32, interfacename : windows_core::PSTR) -> windows_core::PSTR);
if_indextoname(interfaceindex, core::mem::transmute(interfacename.as_ptr()))
}
#[repr(transparent)]
#[derive(PartialEq, Eq, Copy, Clone, Default)]
pub struct ADDRESS_FAMILY(pub u16);
Expand Down
1 change: 1 addition & 0 deletions tests/win_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ fn codegen_windows_bindings() {
"Windows.Win32.NetworkManagement.IpHelper.MIB_IPINTERFACE_TABLE",
"Windows.Win32.NetworkManagement.IpHelper.MIB_UNICASTIPADDRESS_ROW",
"Windows.Win32.NetworkManagement.IpHelper.MIB_UNICASTIPADDRESS_TABLE",
"Windows.Win32.NetworkManagement.IpHelper.if_indextoname",
"Windows.Win32.Networking.WinSock.AF_INET",
"Windows.Win32.Networking.WinSock.AF_INET6",
"Windows.Win32.Networking.WinSock.AF_UNSPEC",
Expand Down
Loading