1use std::io::{Error, Result};
2
3#[cfg(target_family = "unix")]
4use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
5
6#[cfg(feature = "pnet")]
7use pnet::datalink::MacAddr;
8
9#[cfg(target_os = "linux")]
10mod linux;
11
12#[cfg(target_os = "linux")]
13pub use linux::{Ifreq, SIOCGIFINDEX};
14
15pub struct IpTool {
19 #[cfg(target_family = "unix")]
20 fd: RawFd,
21}
22
23impl AsRawFd for IpTool {
30 fn as_raw_fd(&self) -> RawFd {
31 self.fd
32 }
33}
34
35impl FromRawFd for IpTool {
36 unsafe fn from_raw_fd(fd: RawFd) -> Self {
37 Self { fd }
38 }
39}
40
41#[allow(dead_code)] pub(crate) fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
44 let mut c = 0;
45
46 for (d, s) in dst.iter_mut().zip(src.iter()) {
47 *d = *s;
48 c += 1;
49 }
50
51 c
52}
53
54pub fn parse_mac_addr(mac: &str) -> Result<[libc::c_char; 14]> {
55 let mut addr: [libc::c_char; 14] = [0; 14];
56 let mac_vec: Vec<&str> = mac.split(':').collect();
57 if mac_vec.len() != 6 {
58 return Err(Error::from_raw_os_error(libc::EINVAL));
60 }
61 for x in 0..6 {
62 if let Ok(data) = u8::from_str_radix(mac_vec[x], 16) {
63 addr[x] = data as i8;
64 } else {
65 return Err(Error::from_raw_os_error(libc::EINVAL));
67 }
68 }
69
70 Ok(addr)
71}
72
73#[cfg(feature = "pnet")]
74pub trait MacAddrLinxExt: From<[u8; 6]> {
75 fn from_interface(interface: &str) -> Result<Self>;
76}
77
78#[cfg(feature = "pnet")]
79impl MacAddrLinxExt for MacAddr {
80 fn from_interface(interface: &str) -> Result<Self> {
81 let tool = IpTool::new()?;
82
83 let hwaddr = tool.get_mac_sa_data(interface)?;
84 let hwaddr = unsafe { *(&hwaddr as *const _ as *const [u8; 6]) };
86
87 let hwaddr: [u8; 6] = hwaddr.into();
88
89 Ok(hwaddr.into())
90 }
91}
92
93#[cold]
94#[inline]
95#[allow(dead_code)] fn last_err() -> Error {
98 Error::last_os_error()
99}
100
101#[cfg(test)]
102mod test {
103
104 #[test]
105 #[allow(overflowing_literals)]
106 fn parse_mac_addr() {
107 let addr = "5A:E6:60:8F:5F:DE";
108 let mut addr_vec: [libc::c_char; 14] = [0; 14];
109 addr_vec[0] = 0x5A;
110 addr_vec[1] = 0xE6;
111 addr_vec[2] = 0x60;
112 addr_vec[3] = 0x8F;
113 addr_vec[4] = 0x5F;
114 addr_vec[5] = 0xDE;
115
116 assert_eq!(super::parse_mac_addr(addr).unwrap(), addr_vec);
117
118 super::parse_mac_addr("5A:3B:2D").unwrap_err();
120 }
121
122 #[cfg(feature = "pnet")]
123 #[test]
124 fn macaddr_from_interface() {
125 use super::MacAddrLinxExt;
126
127 assert!(pnet::util::MacAddr::from_interface("lo").unwrap().is_zero());
128 }
129}