iptool/
linux.rs

1use std::io::{Error, Result};
2use std::net::IpAddr;
3
4use libc::{
5    c_int, c_short, c_uchar, close, in6_addr, ioctl, sockaddr, sockaddr_in, sockaddr_in6, socket,
6};
7
8use super::{copy_slice, last_err, parse_mac_addr, IpTool};
9
10pub const SIOCGIFINDEX: i32 = 0x8933;
11
12impl IpTool {
13    pub fn new() -> Result<Self> {
14        let fd = Self::get_ctl_fd()?;
15
16        Ok(Self { fd })
17    }
18
19    pub fn set_up(&self, dev: &str, up: bool) -> Result<()> {
20        let mut ifr = Ifreq::new(dev);
21
22        let res = unsafe { ioctl(self.fd, libc::SIOCGIFFLAGS as _, &mut ifr) };
23
24        if res < 0 {
25            return Err(last_err());
26        }
27
28        let flag_val = libc::IFF_UP as i16;
29
30        // SAFETY: union
31        unsafe {
32            ifr.ifr_ifru.ifru_flags = if up {
33                ifr.ifr_ifru.ifru_flags | flag_val
34            } else {
35                ifr.ifr_ifru.ifru_flags & (!flag_val)
36            };
37        }
38
39        let res = unsafe { ioctl(self.fd, libc::SIOCSIFFLAGS as _, &mut ifr) };
40
41        if res < 0 {
42            return Err(last_err());
43        }
44
45        if self.get_up(dev)? != up {
46            return Err(Error::from_raw_os_error(libc::ENOTRECOVERABLE));
47        }
48
49        Ok(())
50    }
51
52    pub fn get_up(&self, dev: &str) -> Result<bool> {
53        let mut ifr = Ifreq::new(dev);
54
55        let res = unsafe { ioctl(self.fd, libc::SIOCGIFFLAGS as _, &mut ifr) };
56        if res < 0 {
57            return Err(last_err());
58        }
59
60        // unions
61        let flags: i16 = unsafe { ifr.ifr_ifru.ifru_flags };
62        Ok((flags & libc::IFF_UP as i16) == 1)
63    }
64
65    pub fn set_mtu(&self, dev: &str, mtu: u32) -> Result<()> {
66        let mut ifr = Ifreq::new(dev);
67        ifr.ifr_ifru.ifru_mtu = mtu as i32;
68
69        let res = unsafe { ioctl(self.fd, libc::SIOCSIFMTU as _, &mut ifr) };
70        if res < 0 {
71            return Err(last_err());
72        }
73
74        Ok(())
75    }
76
77    pub fn get_mtu(&self, dev: &str) -> Result<u32> {
78        let mut ifr = Ifreq::new(dev);
79
80        let res = unsafe { ioctl(self.fd, libc::SIOCGIFMTU as _, &mut ifr) };
81        if res < 0 {
82            return Err(last_err());
83        }
84
85        let mtu = unsafe { ifr.ifr_ifru.ifru_mtu as u32 };
86        Ok(mtu)
87    }
88
89    pub fn get_index(&self, dev: &str) -> Result<c_int> {
90        let mut ifr = Ifreq::new(dev);
91
92        let res = unsafe { ioctl(self.fd, SIOCGIFINDEX as _, &mut ifr) };
93        if res < 0 {
94            return Err(last_err());
95        }
96
97        Ok(unsafe { ifr.ifr_ifru.ifru_ivalue })
98    }
99
100    // TODO: mut?
101    pub fn set_address(&self, dev: &str, address: &IpAddr, prefix_length: u32) -> Result<()> {
102        let index = self.get_index(dev)?;
103        let res = match address {
104            IpAddr::V4(_addr) => {
105                // TODO: ipv4
106                return Err(Error::from_raw_os_error(libc::ENOSYS));
107            }
108            IpAddr::V6(addr) => {
109                let mut ifr = Ifreq6 {
110                    prefix_length,
111                    ifindex: index as _,
112                    addr: in6_addr {
113                        s6_addr: addr.octets(),
114                    },
115                };
116                unsafe { ioctl(self.fd, libc::SIOCSIFADDR as _, &mut ifr) }
117            }
118        };
119
120        if res < 0 {
121            return Err(last_err());
122        }
123
124        Ok(())
125        //let mut ifr = Ifreq::new(dev);
126        /*match address {
127            IpAddr::V4(addr) => {
128                ifr.ifr_ifru.ifru_addr_v4.sin_family = libc::AF_INET as libc::sa_family_t;
129                ifr.ifr_ifru.ifru_addr_v4.sin_addr.s_addr = u32::from_ne_bytes(addr.octets());
130            }
131        }*/
132
133        /*let res = unsafe { libc::ioctl(self.fd, libc::SIOCSIFADDR as _, &mut ifr) };*/
134    }
135
136    pub fn get_address(&self, _dev: &str) -> Result<IpAddr> {
137        // TODO
138        Err(Error::from_raw_os_error(libc::ENOSYS))
139    }
140
141    pub fn set_mac(&self, dev: &str, mac: &str) -> Result<()> {
142        self.set_mac_sa_data(dev, parse_mac_addr(mac)?)
143    }
144    pub fn set_mac_sa_data(&self, dev: &str, mac: [libc::c_char; 14]) -> Result<()> {
145        let mut ifr = Ifreq::new(dev);
146        ifr.ifr_ifru.ifru_hwaddr.sa_family = libc::ARPHRD_ETHER;
147        ifr.ifr_ifru.ifru_hwaddr.sa_data = mac;
148
149        let res = unsafe { ioctl(self.fd, libc::SIOCSIFHWADDR as _, &mut ifr) };
150        if res < 0 {
151            return Err(last_err());
152        }
153
154        Ok(())
155    }
156
157    pub fn get_mac_sa_data(&self, dev: &str) -> Result<[libc::c_char; 14]> {
158        let mut ifr = Ifreq::new(dev);
159        ifr.ifr_ifru.ifru_hwaddr.sa_family = libc::ARPHRD_ETHER;
160
161        let res = unsafe { ioctl(self.fd, libc::SIOCGIFHWADDR as _, &mut ifr) };
162        if res < 0 {
163            return Err(last_err());
164        }
165
166        let sa_data = unsafe { ifr.ifr_ifru.ifru_hwaddr.sa_data };
167        Ok(sa_data)
168    }
169    // TODO: get_mac -> String
170
171    fn get_ctl_fd() -> Result<c_int> {
172        let fd = unsafe { socket(libc::PF_INET, libc::SOCK_DGRAM, 0) };
173        if fd >= 0 {
174            return Ok(fd);
175        }
176        let error = std::io::Error::last_os_error();
177        let fd = unsafe { socket(libc::PF_PACKET, libc::SOCK_DGRAM, 0) };
178        if fd >= 0 {
179            return Ok(fd);
180        }
181        let fd = unsafe { socket(libc::PF_INET6, libc::SOCK_DGRAM, 0) };
182        if fd >= 0 {
183            return Ok(fd);
184        }
185        Err(error)
186    }
187}
188
189impl Drop for IpTool {
190    fn drop(&mut self) {
191        unsafe { close(self.fd) };
192    }
193}
194
195#[repr(C)]
196union IfrIfru {
197    ifru_addr: sockaddr,
198    ifru_hwaddr: sockaddr,
199    ifru_addr_v4: sockaddr_in,
200    ifru_addr_v6: sockaddr_in6,
201    ifru_dstaddr: sockaddr,
202    ifru_broadaddr: sockaddr,
203    ifru_flags: c_short,
204    ifru_metric: c_int,
205    ifru_ivalue: c_int,
206    ifru_mtu: c_int,
207    ifru_phys: c_int,
208    ifru_media: c_int,
209    ifru_intval: c_int,
210    //ifru_data: caddr_t,
211    //ifru_devmtu: ifdevmtu,
212    //ifru_kpi: ifkpi,
213    ifru_wake_flags: u32,
214    ifru_route_refcnt: u32,
215    ifru_cap: [c_int; 2],
216    ifru_functional_type: u32,
217}
218
219#[repr(C)]
220pub struct Ifreq {
221    ifr_name: [c_uchar; libc::IFNAMSIZ],
222    ifr_ifru: IfrIfru,
223}
224
225impl Ifreq {
226    pub fn new(dev: &str) -> Self {
227        //let mut ifr_name = [0; libc::IF_NAMESIZE];
228
229        //ifr_name[..dev.len()].copy_from_slice(dev.as_bytes().as_ref());
230
231        let s: [u8; core::mem::size_of::<Self>()] = [0; core::mem::size_of::<Self>()];
232        let mut s: Self = unsafe { core::mem::transmute(s) };
233
234        copy_slice(&mut s.ifr_name, dev.as_bytes());
235
236        s
237        /*Self {
238            ifr_name,
239            ifr_ifru: IfrIfru { ifru_flags: 0 },
240        }*/
241    }
242}
243
244#[repr(C)]
245pub struct Ifreq6 {
246    addr: in6_addr,
247    prefix_length: u32,
248    ifindex: libc::c_uint,
249}
250
251#[cfg(test)]
252mod test {
253    use super::IpTool;
254    #[test]
255    #[ignore]
256    fn down() {
257        let ip_tool = IpTool::new().unwrap();
258
259        ip_tool.set_up("loop1", false).unwrap();
260    }
261
262    #[test]
263    #[ignore]
264    fn up() {
265        let ip_tool = IpTool::new().unwrap();
266
267        ip_tool.set_up("loop1", true).unwrap();
268    }
269
270    #[test]
271    #[ignore]
272    fn sleep_down_and_up() {
273        let ip_tool = IpTool::new().unwrap();
274
275        ip_tool.set_up("loop1", false).unwrap();
276
277        std::thread::sleep(std::time::Duration::from_secs(5));
278
279        ip_tool.set_up("loop1", true).unwrap();
280    }
281
282    #[test]
283    #[ignore]
284    fn mtu() {
285        let ip_tool = IpTool::new().unwrap();
286
287        ip_tool.set_mtu("loop1", 1420).unwrap();
288
289        assert_eq!(ip_tool.get_mtu("loop1").unwrap(), 1420);
290    }
291
292    #[test]
293    #[ignore]
294    fn mac() {
295        let ip_tool = IpTool::new().unwrap();
296        let mac = "5A:E6:60:8F:5F:DE";
297
298        ip_tool.set_mac("loop1", mac).unwrap();
299
300        let sa_data = ip_tool.get_mac_sa_data("loop1").unwrap();
301        assert_eq!(sa_data, super::parse_mac_addr(mac).unwrap());
302    }
303}