nix/net/if_.rs
1//! Network interface name resolution.
2//!
3//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
4//! or "socan1" into device numbers.
5
6use std::{ffi::{CStr, CString}, fmt};
7use crate::{errno::Errno, Error, NixPath, Result};
8use libc::{c_uint, IF_NAMESIZE};
9
10#[cfg(not(solarish))]
11/// type alias for InterfaceFlags
12pub type IflagsType = libc::c_int;
13#[cfg(solarish)]
14/// type alias for InterfaceFlags
15pub type IflagsType = libc::c_longlong;
16
17/// Resolve an interface into an interface number.
18pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
19 let if_index = name
20 .with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
21
22 if if_index == 0 {
23 Err(Error::last())
24 } else {
25 Ok(if_index)
26 }
27}
28
29/// Resolve an interface number into an interface.
30pub fn if_indextoname(index: c_uint) -> Result<CString> {
31 // We need to allocate this anyway, so doing it directly is faster.
32 let mut buf = vec![0u8; IF_NAMESIZE];
33
34 let return_buf = unsafe {
35 libc::if_indextoname(index, buf.as_mut_ptr().cast())
36 };
37
38 Errno::result(return_buf.cast())?;
39 Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
40}
41
42libc_bitflags!(
43 /// Standard interface flags, used by `getifaddrs`
44 pub struct InterfaceFlags: IflagsType {
45
46 /// Interface is running. (see
47 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
48 IFF_UP as IflagsType;
49 /// Valid broadcast address set. (see
50 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
51 IFF_BROADCAST as IflagsType;
52 /// Internal debugging flag. (see
53 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
54 #[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
55 IFF_DEBUG as IflagsType;
56 /// Interface is a loopback interface. (see
57 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
58 IFF_LOOPBACK as IflagsType;
59 /// Interface is a point-to-point link. (see
60 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
61 IFF_POINTOPOINT as IflagsType;
62 /// Avoid use of trailers. (see
63 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
64 #[cfg(any(
65 linux_android,
66 solarish,
67 apple_targets,
68 target_os = "fuchsia",
69 target_os = "cygwin"))]
70 IFF_NOTRAILERS as IflagsType;
71 /// Interface manages own routes.
72 #[cfg(any(target_os = "dragonfly"))]
73 IFF_SMART as IflagsType;
74 /// Resources allocated. (see
75 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
76 #[cfg(any(
77 linux_android,
78 bsd,
79 solarish,
80 target_os = "fuchsia",
81 target_os = "cygwin"))]
82 IFF_RUNNING as IflagsType;
83 /// No arp protocol, L2 destination address not set. (see
84 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
85 IFF_NOARP as IflagsType;
86 /// Interface is in promiscuous mode. (see
87 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
88 IFF_PROMISC as IflagsType;
89 /// Receive all multicast packets. (see
90 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
91 #[cfg(not(target_os = "cygwin"))]
92 IFF_ALLMULTI as IflagsType;
93 /// Master of a load balancing bundle. (see
94 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
95 #[cfg(any(linux_android, target_os = "fuchsia"))]
96 IFF_MASTER;
97 /// transmission in progress, tx hardware queue is full
98 #[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
99 IFF_OACTIVE;
100 /// Protocol code on board.
101 #[cfg(solarish)]
102 IFF_INTELLIGENT as IflagsType;
103 /// Slave of a load balancing bundle. (see
104 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
105 #[cfg(any(linux_android, target_os = "fuchsia"))]
106 IFF_SLAVE;
107 /// Can't hear own transmissions.
108 #[cfg(bsd)]
109 IFF_SIMPLEX;
110 /// Supports multicast. (see
111 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
112 IFF_MULTICAST as IflagsType;
113 /// Per link layer defined bit.
114 #[cfg(bsd)]
115 IFF_LINK0;
116 /// Multicast using broadcast.
117 #[cfg(solarish)]
118 IFF_MULTI_BCAST as IflagsType;
119 /// Is able to select media type via ifmap. (see
120 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
121 #[cfg(any(linux_android, target_os = "fuchsia"))]
122 IFF_PORTSEL;
123 /// Per link layer defined bit.
124 #[cfg(bsd)]
125 IFF_LINK1;
126 /// Non-unique address.
127 #[cfg(solarish)]
128 IFF_UNNUMBERED as IflagsType;
129 /// Auto media selection active. (see
130 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
131 #[cfg(any(linux_android, target_os = "fuchsia"))]
132 IFF_AUTOMEDIA;
133 /// Per link layer defined bit.
134 #[cfg(bsd)]
135 IFF_LINK2;
136 /// Use alternate physical connection.
137 #[cfg(any(freebsdlike, apple_targets))]
138 IFF_ALTPHYS;
139 /// DHCP controls interface.
140 #[cfg(solarish)]
141 IFF_DHCPRUNNING as IflagsType;
142 /// The addresses are lost when the interface goes down. (see
143 /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
144 #[cfg(any(linux_android, target_os = "fuchsia"))]
145 IFF_DYNAMIC;
146 /// Do not advertise.
147 #[cfg(solarish)]
148 IFF_PRIVATE as IflagsType;
149 /// Driver signals L1 up. Volatile.
150 #[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
151 IFF_LOWER_UP;
152 /// Interface is in polling mode.
153 #[cfg(any(target_os = "dragonfly"))]
154 IFF_POLLING_COMPAT;
155 /// Unconfigurable using ioctl(2).
156 #[cfg(any(target_os = "freebsd"))]
157 IFF_CANTCONFIG;
158 /// Do not transmit packets.
159 #[cfg(solarish)]
160 IFF_NOXMIT as IflagsType;
161 /// Driver signals dormant. Volatile.
162 #[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
163 IFF_DORMANT;
164 /// User-requested promisc mode.
165 #[cfg(freebsdlike)]
166 IFF_PPROMISC;
167 /// Just on-link subnet.
168 #[cfg(solarish)]
169 IFF_NOLOCAL as IflagsType;
170 /// Echo sent packets. Volatile.
171 #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
172 IFF_ECHO;
173 /// User-requested monitor mode.
174 #[cfg(freebsdlike)]
175 IFF_MONITOR;
176 /// Address is deprecated.
177 #[cfg(solarish)]
178 IFF_DEPRECATED as IflagsType;
179 /// Static ARP.
180 #[cfg(freebsdlike)]
181 IFF_STATICARP;
182 /// Address from stateless addrconf.
183 #[cfg(solarish)]
184 IFF_ADDRCONF as IflagsType;
185 /// Interface is in polling mode.
186 #[cfg(any(target_os = "dragonfly"))]
187 IFF_NPOLLING;
188 /// Router on interface.
189 #[cfg(solarish)]
190 IFF_ROUTER as IflagsType;
191 /// Interface is in polling mode.
192 #[cfg(any(target_os = "dragonfly"))]
193 IFF_IDIRECT;
194 /// Interface is winding down
195 #[cfg(any(target_os = "freebsd"))]
196 IFF_DYING;
197 /// No NUD on interface.
198 #[cfg(solarish)]
199 IFF_NONUD as IflagsType;
200 /// Interface is being renamed
201 #[cfg(any(target_os = "freebsd"))]
202 IFF_RENAMING;
203 /// Anycast address.
204 #[cfg(solarish)]
205 IFF_ANYCAST as IflagsType;
206 /// Don't exchange routing info.
207 #[cfg(solarish)]
208 IFF_NORTEXCH as IflagsType;
209 /// Do not provide packet information
210 #[cfg(any(linux_android, target_os = "fuchsia"))]
211 IFF_NO_PI as IflagsType;
212 /// TUN device (no Ethernet headers)
213 #[cfg(any(linux_android, target_os = "fuchsia"))]
214 IFF_TUN as IflagsType;
215 /// TAP device
216 #[cfg(any(linux_android, target_os = "fuchsia"))]
217 IFF_TAP as IflagsType;
218 /// IPv4 interface.
219 #[cfg(solarish)]
220 IFF_IPV4 as IflagsType;
221 /// IPv6 interface.
222 #[cfg(solarish)]
223 IFF_IPV6 as IflagsType;
224 /// in.mpathd test address
225 #[cfg(solarish)]
226 IFF_NOFAILOVER as IflagsType;
227 /// Interface has failed
228 #[cfg(solarish)]
229 IFF_FAILED as IflagsType;
230 /// Interface is a hot-spare
231 #[cfg(solarish)]
232 IFF_STANDBY as IflagsType;
233 /// Functioning but not used
234 #[cfg(solarish)]
235 IFF_INACTIVE as IflagsType;
236 /// Interface is offline
237 #[cfg(solarish)]
238 IFF_OFFLINE as IflagsType;
239 /// Has CoS marking supported
240 #[cfg(solarish)]
241 IFF_COS_ENABLED as IflagsType;
242 /// Prefer as source addr
243 #[cfg(solarish)]
244 IFF_PREFERRED as IflagsType;
245 /// RFC3041
246 #[cfg(solarish)]
247 IFF_TEMPORARY as IflagsType;
248 /// MTU set
249 #[cfg(solarish)]
250 IFF_FIXEDMTU as IflagsType;
251 /// Cannot send/receive packets
252 #[cfg(solarish)]
253 IFF_VIRTUAL as IflagsType;
254 /// Local address in use
255 #[cfg(solarish)]
256 IFF_DUPLICATE as IflagsType;
257 /// IPMP IP interface
258 #[cfg(solarish)]
259 IFF_IPMP as IflagsType;
260 }
261);
262
263impl fmt::Display for InterfaceFlags {
264 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265 bitflags::parser::to_writer(self, f)
266 }
267}
268
269
270#[cfg(any(
271 bsd,
272 target_os = "fuchsia",
273 target_os = "linux",
274 solarish,
275))]
276mod if_nameindex {
277 use super::*;
278
279 use std::ffi::CStr;
280 use std::fmt;
281 use std::marker::PhantomData;
282 use std::ptr::NonNull;
283
284 /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
285 /// (1, 2, 3, etc) that identifies it in the OS's networking stack.
286 #[allow(missing_copy_implementations)]
287 #[repr(transparent)]
288 pub struct Interface(libc::if_nameindex);
289
290 impl Interface {
291 /// Obtain the index of this interface.
292 pub fn index(&self) -> c_uint {
293 self.0.if_index
294 }
295
296 /// Obtain the name of this interface.
297 pub fn name(&self) -> &CStr {
298 unsafe { CStr::from_ptr(self.0.if_name) }
299 }
300 }
301
302 impl fmt::Debug for Interface {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 f.debug_struct("Interface")
305 .field("index", &self.index())
306 .field("name", &self.name())
307 .finish()
308 }
309 }
310
311 /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
312 #[repr(transparent)]
313 pub struct Interfaces {
314 ptr: NonNull<libc::if_nameindex>,
315 }
316
317 impl Interfaces {
318 /// Iterate over the interfaces in this list.
319 #[inline]
320 pub fn iter(&self) -> InterfacesIter<'_> {
321 self.into_iter()
322 }
323
324 /// Convert this to a slice of interfaces. Note that the underlying interfaces list is
325 /// null-terminated, so calling this calculates the length. If random access isn't needed,
326 /// [`Interfaces::iter()`] should be used instead.
327 pub fn to_slice(&self) -> &[Interface] {
328 let ifs = self.ptr.as_ptr().cast();
329 let len = self.iter().count();
330 unsafe { std::slice::from_raw_parts(ifs, len) }
331 }
332 }
333
334 impl Drop for Interfaces {
335 fn drop(&mut self) {
336 unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
337 }
338 }
339
340 impl fmt::Debug for Interfaces {
341 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
342 self.to_slice().fmt(f)
343 }
344 }
345
346 impl<'a> IntoIterator for &'a Interfaces {
347 type IntoIter = InterfacesIter<'a>;
348 type Item = &'a Interface;
349 #[inline]
350 fn into_iter(self) -> Self::IntoIter {
351 InterfacesIter {
352 ptr: self.ptr.as_ptr(),
353 _marker: PhantomData,
354 }
355 }
356 }
357
358 /// An iterator over the interfaces in an [`Interfaces`].
359 #[derive(Debug)]
360 pub struct InterfacesIter<'a> {
361 ptr: *const libc::if_nameindex,
362 _marker: PhantomData<&'a Interfaces>,
363 }
364
365 impl<'a> Iterator for InterfacesIter<'a> {
366 type Item = &'a Interface;
367 #[inline]
368 fn next(&mut self) -> Option<Self::Item> {
369 unsafe {
370 if (*self.ptr).if_index == 0 {
371 None
372 } else {
373 let ret = &*(self.ptr as *const Interface);
374 self.ptr = self.ptr.add(1);
375 Some(ret)
376 }
377 }
378 }
379 }
380
381 /// Retrieve a list of the network interfaces available on the local system.
382 ///
383 /// ```
384 /// let interfaces = nix::net::if_::if_nameindex().unwrap();
385 /// for iface in &interfaces {
386 /// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
387 /// }
388 /// ```
389 pub fn if_nameindex() -> Result<Interfaces> {
390 unsafe {
391 let ifs = libc::if_nameindex();
392 let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
393 Ok(Interfaces { ptr })
394 }
395 }
396}
397#[cfg(any(
398 bsd,
399 target_os = "fuchsia",
400 target_os = "linux",
401 solarish,
402))]
403pub use if_nameindex::*;