network-interface icon indicating copy to clipboard operation
network-interface copied to clipboard

Check if a network interface is a loopback interface

Open alexkirsz opened this issue 2 years ago • 8 comments

Hey!

I'm looking for the same functionality as the is_loopback() method of the pnet_datalink crate.

FWIW, here's what GPT-4 generated to do this:

#[cfg(unix)]
fn main() {
    use libc::{getifaddrs, freeifaddrs, ifaddrs, IFF_LOOPBACK};
    use std::ffi::CStr;
    use std::ptr;

    unsafe {
        let mut ifap: *mut ifaddrs = ptr::null_mut();
        if getifaddrs(&mut ifap) == 0 {
            let mut ifa = ifap;
            while !ifa.is_null() {
                let iface = &*ifa;
                if iface.ifa_flags & IFF_LOOPBACK == IFF_LOOPBACK {
                    let name = CStr::from_ptr(iface.ifa_name).to_string_lossy().into_owned();
                    println!("Loopback Interface: {}", name);
                }
                ifa = iface.ifa_next;
            }
            freeifaddrs(ifap);
        } else {
            eprintln!("Error getting network interfaces");
        }
    }
}

#[cfg(windows)]
fn main() {
    use winapi::shared::minwindef::ULONG;
    use winapi::shared::ntdef::NULL;
    use winapi::shared::ws2def::AF_UNSPEC;
    use winapi::um::iphlpapi::{GetAdaptersAddresses, PIP_ADAPTER_ADDRESSES};
    use winapi::um::winsock2::SOCKET_ERROR;

    use std::mem::size_of_val;
    use std::ptr;

    unsafe {
        let mut buf_len: ULONG = 0;
        if GetAdaptersAddresses(AF_UNSPEC as u32, 0, ptr::null_mut(), ptr::null_mut(), &mut buf_len) == SOCKET_ERROR {
            let mut buffer = vec![0u8; buf_len as usize];
            let p_addresses = buffer.as_mut_ptr() as PIP_ADAPTER_ADDRESSES;
            if GetAdaptersAddresses(AF_UNSPEC as u32, 0, ptr::null_mut(), p_addresses, &mut buf_len) == 0 {
                let mut current_address = p_addresses;
                while !current_address.is_null() {
                    let iface = &*current_address;
                    if iface.IfType == winapi::shared::ifdef::IF_TYPE_SOFTWARE_LOOPBACK {
                        let name = String::from_utf16_lossy(std::slice::from_raw_parts(iface.FriendlyName.as_ptr(), iface.FriendlyName.iter().position(|&c| c == 0).unwrap_or(0)));
                        println!("Loopback Interface: {}", name);
                    }
                    current_address = iface.Next;
                }
            } else {
                eprintln!("Error getting network interfaces");
            }
        } else {
            eprintln!("Error getting network interfaces");
        }
    }
}

alexkirsz avatar May 06 '23 18:05 alexkirsz

Hi @alexkirsz thanks for openning this feature request!

Are you planning to work on this?

LeoBorai avatar Aug 19 '23 13:08 LeoBorai

Code borrowed from if_addrs:

fn is_loopback_ipv6(ip: Ipv6Addr) -> bool {
    ip.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
}

fn is_loopback_ipv4(ip: Ipv4Addr) -> bool {
    ip.octets()[0] == 127
}

nvandamme avatar Apr 04 '24 22:04 nvandamme

We use this:

    let network_interfaces = NetworkInterface::show()?;
    let network_interfaces = network_interfaces
        .into_iter()
        .filter(|itf| !itf.name.starts_with("lo"))
        .collect::<Vec<NetworkInterface>>();

utkarshgupta137 avatar Apr 05 '24 09:04 utkarshgupta137

@nvandamme I think it would be nice to get a flag from the OS whether the IF is loopback or not and don't decide on its address.

@utkarshgupta137 Your app code seems to be platform specific (Linux/UNIX) and might not work on Windows. Also I think it's risky to just test for IF name to start with "lo".

By the way: Despite not having a solution (I guess @alexkirsz might go in the right direction?) either, here is my workaround I'm using in my app code:

  let network_interfaces = NetworkInterface::show().unwrap();

  for itf in network_interfaces.iter() {
      if let Some(mac_addr) = &itf.mac_addr {
          if mac_addr == "00:00:00:00:00:00" {            // <- not strictly lo related, but I'd like to skip those anyway
              continue;
          }

          for addr in &itf.addr {
              let ip = addr.ip();
              if ip.is_loopback() {          // <- HERE; checks both IPv4 and IPv6
                  continue;
              }
              // ...
          }
      };
  }

podarcis avatar Apr 05 '24 10:04 podarcis

@podarcis Why ? Either way, local interfaces are following RFC's adresses allocations.... And if the OS allows otherwise, it is another level of problems awaiting network stack usage anyway...

nvandamme avatar Apr 06 '24 07:04 nvandamme

@nvandamme My initial thinking was that the OS knows best about whether it's a loopback or not. And I thought about other interfaces besides AF_PACKET, AF_INET and AF_INET6 might have the concept of loopback interfaces, however, it's the only types that are supported with this library (at least for now and Linux). So there might be no point in going the extra mile.

So what about NetworkInterface implements a function is_loopback() that simply iterates over the containing IPv4/IPv6 address vector and returns true on the first addr.ip().is_loopback()?

podarcis avatar Apr 06 '24 08:04 podarcis

@nvandamme My initial thinking was that the OS knows best about whether it's a loopback or not. And I thought about other interfaces besides AF_PACKET, AF_INET and AF_INET6 might have the concept of loopback interfaces, however, it's the only types that are supported with this library (at least for now and Linux). So there might be no point in going the extra mile.

So what about NetworkInterface implements a function is_loopback() that simply iterates over the containing IPv4/IPv6 address vector and returns true on the first addr.ip().is_loopback()?

@podarcis, ok, for example cases using lo aliases with routed IPs, like management IPs on router and switches (linux/unix sure can handle this, but windows, I'm not sure) ? And in this case, the allocated IPs are fully routed to all network routes, so not really acting as a pure loopback IP anymore.

The only other case would be others layer 2 ethernet protocoles that might use a form of loopback (profinet, 6lowpan...)

nvandamme avatar Apr 07 '24 00:04 nvandamme

@EstebanBorai What do you think? Expose a function is_loopback() on the interface checking on its adresses for loopback? Or just add a recipe to the README (see my example above)?

podarcis avatar Apr 07 '24 07:04 podarcis