Skip to content

Commit

Permalink
Linux implementation of 'get_mac_address_by_ip'
Browse files Browse the repository at this point in the history
getaddrif() does not return the information indexed by adapter
so we have to build our own index.  Tested on my Linux vm - seems
to work!
  • Loading branch information
capveg-netdebug committed Jan 11, 2024
1 parent a6e4143 commit 59473cd
Showing 1 changed file with 53 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/linux.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#![allow(dead_code)]

use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};

use crate::MacAddressError;
use nix::ifaddrs::*;

Expand Down Expand Up @@ -30,6 +35,54 @@ pub fn get_mac(name: Option<&str>) -> Result<Option<[u8; 6]>, MacAddressError> {
Ok(None)
}

/// Uses the `getifaddrs` call to retrieve a list of network interfaces on the
/// host device and returns the MAC address that matching the adapter with
/// the given IP
///
/// Because nix returns all of the IP's and MAC's in a combined list, we need
/// to map the IP to an inteface name and track the MAC addresses by interface name
/// and see if there's a match
pub fn get_mac_address_by_ip(ip: &IpAddr) -> Result<Option<[u8; 6]>, MacAddressError> {
let ifiter = getifaddrs()?;

let mut ip_on_inferface: Option<String> = None;
let mut mac_to_interface: HashMap<String, Option<[u8; 6]>> = HashMap::new();
for interface in ifiter {
if let Some(iface_address) = interface.address {
// is this a mac address?
if let Some(link) = iface_address.as_link_addr() {
mac_to_interface.insert(interface.interface_name.clone(), link.addr());
// did we just find what we're looking for?
if let Some(intf_name) = &ip_on_inferface {
if *intf_name == interface.interface_name {
return Ok(link.addr());
}
}
}
if let Some(adapter_ip) = if let Some(sin4) = iface_address.as_sockaddr_in() {
// v4 addr?
Some(IpAddr::from(Ipv4Addr::from(sin4.ip())))
} else if let Some(sin6) = iface_address.as_sockaddr_in6() {
// v6 addr?
Some(IpAddr::from(Ipv6Addr::from(sin6.ip())))
} else {
// something else, ignore
None
} {
// found an IP for this adapter - if it's the one we're looking for, save it
if adapter_ip == *ip {
ip_on_inferface = Some(interface.interface_name.clone());
if let Some(mac) = mac_to_interface.get(&interface.interface_name) {
return Ok(mac.clone());
}
}
}
}
}

Ok(None)
}

pub fn get_ifname(mac: &[u8; 6]) -> Result<Option<String>, MacAddressError> {
let ifiter = getifaddrs()?;

Expand Down

0 comments on commit 59473cd

Please sign in to comment.