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

Check if a network interface is a loopback interface #39

Open
alexkirsz opened this issue May 6, 2023 · 8 comments
Open

Check if a network interface is a loopback interface #39

alexkirsz opened this issue May 6, 2023 · 8 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@alexkirsz
Copy link

alexkirsz commented May 6, 2023

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");
        }
    }
}
@EstebanBorai
Copy link
Owner

Hi @alexkirsz thanks for openning this feature request!

Are you planning to work on this?

@EstebanBorai EstebanBorai added enhancement New feature or request help wanted Extra attention is needed labels Oct 20, 2023
@nvandamme
Copy link

nvandamme commented Apr 4, 2024

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
}

@utkarshgupta137
Copy link

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>>();

@podarcis
Copy link

podarcis commented Apr 5, 2024

@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;
              }
              // ...
          }
      };
  }

@nvandamme
Copy link

@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...

@podarcis
Copy link

podarcis commented Apr 6, 2024

@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()?

@nvandamme
Copy link

nvandamme commented Apr 7, 2024

@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...)

@podarcis
Copy link

podarcis commented Apr 7, 2024

@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)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants