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