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 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 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 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 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 }
135
136 pub fn get_address(&self, _dev: &str) -> Result<IpAddr> {
137 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 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_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 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 }
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}