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