Skip to content
Merged
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
68 changes: 50 additions & 18 deletions src/interface/shared.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
use std::net::Ipv6Addr;
#[cfg(feature = "gateway")]
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};

/// Retrieve the IP address of the default network interface.
///
/// This function attempts to bind a UDP socket to an unspecified IP address (0.0.0.0)
/// and port (0), which allows the system to select an appropriate IP address and port.
/// After binding, it attempts to connect the socket to a designated non-routable IP address
/// (`10.254.254.254` on port 1). This is a trick commonly used to prompt the OS to
/// populate the socket's local address with the IP address of the interface that would
/// route to the specified address.
///
/// The function returns the local IP address if these operations succeed.
/// If any operation fails (binding, connecting, or retrieving the address),
/// the function returns `None`, indicating the inability to determine the local IP.
///
/// Returns:
/// - `Some(IpAddr)`: IP address of the default network interface if successful.
/// - `None`: If any error occurs during the operations.
#[cfg(feature = "gateway")]
pub fn get_local_ipaddr() -> Option<IpAddr> {
fn try_ipv4() -> Option<IpAddr> {
// Attempt to bind a UDP socket to an unspecified address and port.
let socket = match UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)) {
Ok(s) => s,
Expand All @@ -39,3 +23,51 @@ pub fn get_local_ipaddr() -> Option<IpAddr> {
Err(_) => return None,
};
}

fn try_ipv6() -> Option<IpAddr> {
// same thing but for IPv6
let socket = match UdpSocket::bind(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0)) {
Ok(s) => s,
Err(_) => return None,
};
match socket.connect(SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(
0xfdff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
)),
1,
)) {
Ok(()) => (),
Err(_) => return None,
};
match socket.local_addr() {
Ok(addr) => return Some(addr.ip()),
Err(_) => return None,
};
}

/// Retrieve the IP address of the default network interface.
///
/// This function attempts to bind a UDP socket to an unspecified IP address (0.0.0.0 or ::)
/// and port (0), which allows the system to select an appropriate IP address and port.
/// After binding, it attempts to connect the socket to a designated non-routable IP address
/// (`10.254.254.254` or `fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff` on port 1). This is a
/// trick commonly used to prompt the OS to populate the socket's local address with the IP
/// address of the interface that would route to the specified address.
///
/// The function returns the local IP address if these operations succeed.
/// If any operation fails (binding, connecting, or retrieving the address),
/// the function returns `None`, indicating the inability to determine the local IP.
///
/// Returns:
/// - `Some(IpAddr)`: IP address of the default network interface if successful.
/// - `None`: If any error occurs during the operations.
#[cfg(feature = "gateway")]
pub fn get_local_ipaddr() -> Option<IpAddr> {
// binding the IPv4 socket can actually succeed but a later step will fail in IPv6-only,
// we call `try_ipv6` in any case where `try_ipv4` fails
if let Some(addr) = try_ipv4() {
Some(addr)
} else {
try_ipv6()
}
}