iptool/
lib.rs

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
15// TODO: macOS
16
17/// Only use it for a short amount of time, as it does not close it's ioctl socket
18pub struct IpTool {
19    #[cfg(target_family = "unix")]
20    fd: RawFd,
21}
22
23/*
24TODO: is it already non send?
25impl !Send for IpTool {}
26impl !Sync for IpTool {}
27 */
28
29impl 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// Helper function
42#[allow(dead_code)] // not used yet on non linux
43pub(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        // TODO: unlikly (https://doc.rust-lang.org/nightly/std/intrinsics/fn.unlikely.html)
59        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            // TODO: unlikly (https://doc.rust-lang.org/nightly/std/intrinsics/fn.unlikely.html)
66            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: [u8; 6] = hwaddr.try_into()?;
85        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)] // not used yet on non linux
96/// Own (cold) function to optimize if statements
97fn 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        // not long enough address
119        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}