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 crate::{Error, NixPath, Result}; 7use libc::c_uint; 8 9/// Resolve an interface into a interface number. 10pub 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 21libc_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())))] 339mod 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. 355 pub fn index(&self) -> c_uint { 356 self.0.if_index 357 } 358 359 /// Obtain the name of this interface. 360 pub fn name(&self) -> &CStr { 361 unsafe { CStr::from_ptr(self.0.if_name) } 362 } 363 } 364 365 impl fmt::Debug for Interface { 366 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] 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. 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 { 397 fn drop(&mut self) { 398 unsafe { libc::if_freenameindex(self.ptr.as_ptr()) }; 399 } 400 } 401 402 impl fmt::Debug for Interfaces { 403 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] 412 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] 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 /// ``` 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))] 469pub use if_nameindex::*; 470