详细介绍:Rust: 获取 MAC 地址方法大全

在 Rust 中获取 MAC 地址(物理地址)需要依赖第三方库,因为标准库不提供此功能。以下是全面的解决方案:

添加依赖

使用 mac_address 库:

[dependencies]
mac_address = "1.1.0"

基础方法

1. 获取所有网络接口的 MAC 地址

use mac_address::get_mac_addresses;
fn main() {
match get_mac_addresses() {
Ok(interfaces) =>
{
println!("所有网络接口的 MAC 地址:");
for interface in interfaces {
println!("接口: {}, MAC: {}", interface.name, interface.address);
}
}
Err(e) =>
eprintln!("获取 MAC 地址失败: {}", e),
}
}

2. 获取主网络接口的 MAC 地址

use mac_address::get_mac_address;
fn main() {
match get_mac_address() {
Ok(Some(mac)) =>
println!("主 MAC 地址: {}", mac),
Ok(None) =>
println!("未找到 MAC 地址"),
Err(e) =>
eprintln!("获取 MAC 地址失败: {}", e),
}
}

高级用法

1. 获取特定接口的 MAC 地址

use mac_address::mac_address_by_name;
fn main() {
let interface_name = "eth0";
// 替换为你的接口名
match mac_address_by_name(interface_name) {
Ok(Some(mac)) =>
println!("{} 的 MAC 地址: {}", interface_name, mac),
Ok(None) =>
println!("未找到 {} 的 MAC 地址", interface_name),
Err(e) =>
eprintln!("获取 MAC 地址失败: {}", e),
}
}

2. 过滤虚拟接口

use mac_address::get_mac_addresses;
fn get_physical_macs() ->
Vec<
mac_address::MacAddress>
{
get_mac_addresses()
.unwrap_or_default()
.into_iter()
.filter(|iface| {
// 过滤掉虚拟接口
!iface.name.starts_with("docker") &&
!iface.name.starts_with("veth") &&
!iface.name.starts_with("br-") &&
!iface.name.starts_with("lo")
})
.map(|iface| iface.address)
.collect()
}
fn main() {
println!("物理接口的 MAC 地址:");
for mac in get_physical_macs() {
println!("- {}", mac);
}
}

3. 格式化 MAC 地址

use mac_address::MacAddress;
fn format_mac(mac: &
MacAddress) ->
String {
mac.bytes()
.iter()
.map(|b| format!("{:02X}", b))
.collect::<
Vec<_>>
  ()
  .join(":")
  }
  fn main() {
  if let Ok(Some(mac)) = get_mac_address() {
  println!("格式化 MAC 地址: {}", format_mac(&mac));
  }
  }

跨平台兼容性处理

1. 处理不同平台的接口命名

use mac_address::get_mac_addresses;
fn get_primary_mac() ->
Option<
mac_address::MacAddress>
{
let interfaces = get_mac_addresses().ok()?;
// 不同平台的优先级
let preferred_names = if cfg!(target_os = "windows") {
vec!["Ethernet", "Wi-Fi"]
} else if cfg!(target_os = "macos") {
vec!["en0", "en1"]
} else {
vec!["eth0", "wlan0"]
};
for name in preferred_names {
if let Some(iface) = interfaces.iter().find(|i| i.name.contains(name)) {
return Some(iface.address.clone());
}
}
// 返回第一个非虚拟接口
interfaces
.into_iter()
.find(|iface| !iface.name.starts_with("lo"))
.map(|iface| iface.address)
}

2. Windows 特定处理

#[cfg(windows)]
fn get_windows_mac() ->
Option<
mac_address::MacAddress>
{
use winreg::enums::*;
use winreg::RegKey;
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
let interfaces = hklm.open_subkey("SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}")
.ok()?;
for subkey_name in interfaces.enum_keys().filter_map(|k| k.ok()) {
if let Ok(subkey) = interfaces.open_subkey(subkey_name) {
if let Ok(addr) = subkey.get_value::<
String, _>
("NetCfgInstanceId") {
if let Ok(Some(mac)) = mac_address_by_name(&addr) {
return Some(mac);
}
}
}
}
None
}

️ 错误处理与日志

1. 自定义错误类型

use thiserror::Error;
  #[derive(Error, Debug)]
enum MacError {
  #[error("MAC 地址获取失败")]
  RetrievalError(#[from] mac_address::MacAddressError),
  #[error("未找到 MAC 地址")]
NotFound,
  #[error("无效的 MAC 地址")]
InvalidFormat,
}
fn get_safe_mac() ->
Result<
String, MacError>
{
let mac = get_mac_address()?
.ok_or(MacError::NotFound)?;
if mac.bytes().iter().all(|&b| b == 0) {
return Err(MacError::InvalidFormat);
}
Ok(format_mac(&mac))
}

2. 带重试的 MAC 获取

use std::thread;
use std::time::Duration;
fn get_mac_with_retry(max_retries: u8) ->
Option<
mac_address::MacAddress>
{
for attempt in 0..=max_retries {
match get_mac_address() {
Ok(Some(mac)) =>
return Some(mac),
Ok(None) =>
log::warn!("未找到 MAC 地址 (尝试 {}/{})", attempt + 1, max_retries),
Err(e) =>
log::error!("获取失败: {} (尝试 {}/{})", e, attempt + 1, max_retries),
}
if attempt < max_retries {
thread::sleep(Duration::from_secs(1));
}
}
None
}

MAC 地址实用函数

1. 验证 MAC 地址格式

fn is_valid_mac(mac: &
str) ->
bool {
let parts: Vec<
&
str>
= mac.split(':').collect();
parts.len() == 6 && parts.iter().all(|p| p.len() == 2 && p.chars().all(|c| c.is_ascii_hexdigit()))
}
fn main() {
println!("AA:BB:CC:DD:EE:FF 是否有效? {}", is_valid_mac("AA:BB:CC:DD:EE:FF"));
println!("无效MAC是否有效? {}", is_valid_mac("GG:HH:II:JJ:KK:LL"));
}

2. MAC 地址转换

fn mac_to_bytes(mac: &
str) ->
Option<
[u8;
6]>
{
let parts: Vec<
&
str>
= mac.split(':').collect();
if parts.len() != 6 {
return None;
}
let mut bytes = [0u8;
6];
for (i, part) in parts.iter().enumerate() {
bytes[i] = u8::from_str_radix(part, 16).ok()?;
}
Some(bytes)
}
fn bytes_to_mac(bytes: &
[u8;
6]) ->
String {
bytes.iter()
.map(|b| format!("{:02X}", b))
.collect::<
Vec<_>>
  ()
  .join(":")
  }

完整示例:系统信息工具

use mac_address::{get_mac_address, get_mac_addresses
};
use sysinfo::{
System, SystemExt
};
fn main() {
let mut sys = System::new_all();
sys.refresh_all();
println!("系统信息:");
println!("操作系统: {}", sys.name().unwrap_or_default());
println!("内核版本: {}", sys.kernel_version().unwrap_or_default());
println!("主机名: {}", sys.host_name().unwrap_or_default());
match get_mac_address() {
Ok(Some(mac)) =>
{
println!("主 MAC 地址: {}", mac);
}
_ =>
println!("无法获取 MAC 地址"),
}
println!("\n所有网络接口:");
for interface in sys.networks().iter() {
println!("接口: {}", interface.0);
println!(" IP地址: {:?}", interface.1.addresses);
println!(" 接收数据: {} MB", interface.1.total_received() / 1024 / 1024);
println!(" 发送数据: {} MB", interface.1.total_transmitted() / 1024 / 1024);
if let Ok(Some(mac)) = mac_address_by_name(interface.0) {
println!(" MAC地址: {}", mac);
}
}
}

⚠️ 注意事项

  1. 权限要求

    • Linux/macOS:需要 root 权限或 CAP_NET_ADMIN 能力
    • Windows:普通用户权限通常足够
  2. 虚拟接口

    • 虚拟接口(如 Docker、VPN)也会有 MAC 地址
    • 可能需要过滤掉非物理接口
  3. 隐私考虑

    • MAC 地址是敏感信息
    • 遵守 GDPR 等隐私法规
    • 必要时进行哈希处理
  4. 平台差异

    • macOS 的 en0 通常是主接口
    • Linux 的 eth0 是常见物理接口
    • Windows 的接口名可能包含 GUID

最佳实践

  1. 明确接口名称

    // 优先选择已知物理接口
    let physical_interfaces = &
    ["eth0", "en0", "wlan0"];
    for name in physical_interfaces {
    if let Ok(Some(mac)) = mac_address_by_name(name) {
    return Some(mac);
    }
    }
  2. 错误处理

    • 处理可能的 None 结果
    • 记录无法获取 MAC 的原因
    • 提供备用方案(如生成随机设备 ID)
  3. 格式化一致性

    // 统一使用大写带冒号格式
    let formatted = mac.to_string().to_uppercase();

通过以上方法,您可以在 Rust 应用中可靠地获取 MAC 地址。根据目标平台和具体需求选择最合适的实现方案。

posted @ 2025-08-06 09:34  yjbjingcha  阅读(39)  评论(0)    收藏  举报