1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
//! Network interface name resolution.
//!
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
//! or "socan1" into device numbers.

use std::{ffi::{CStr, CString}, fmt};
use crate::{errno::Errno, Error, NixPath, Result};
use libc::{c_uint, IF_NAMESIZE};

#[cfg(not(solarish))]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_int;
#[cfg(solarish)]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_longlong;

/// Resolve an interface into an interface number.
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
    let if_index = name
        .with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;

    if if_index == 0 {
        Err(Error::last())
    } else {
        Ok(if_index)
    }
}

/// Resolve an interface number into an interface.
pub fn if_indextoname(index: c_uint) -> Result<CString> {
    // We need to allocate this anyway, so doing it directly is faster.
    let mut buf = vec![0u8; IF_NAMESIZE];

    let return_buf = unsafe {
        libc::if_indextoname(index, buf.as_mut_ptr().cast())
    };

    Errno::result(return_buf.cast())?;
    Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
}

libc_bitflags!(
    /// Standard interface flags, used by `getifaddrs`
    pub struct InterfaceFlags: IflagsType {
    
        /// Interface is running. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_UP as IflagsType;
        /// Valid broadcast address set. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_BROADCAST as IflagsType;
        /// Internal debugging flag. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(not(target_os = "haiku"))]
        IFF_DEBUG as IflagsType;
        /// Interface is a loopback interface. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_LOOPBACK as IflagsType;
        /// Interface is a point-to-point link. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_POINTOPOINT as IflagsType;
        /// Avoid use of trailers. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(
                  linux_android,
                  solarish,
                  apple_targets,
                  target_os = "fuchsia",
                  target_os = "netbsd"))]
        IFF_NOTRAILERS as IflagsType;
        /// Interface manages own routes.
        #[cfg(any(target_os = "dragonfly"))]
        IFF_SMART as IflagsType;
        /// Resources allocated. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(
                  linux_android,
                  bsd,
                  solarish,
                  target_os = "fuchsia"))]
        IFF_RUNNING as IflagsType;
        /// No arp protocol, L2 destination address not set. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_NOARP as IflagsType;
        /// Interface is in promiscuous mode. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_PROMISC as IflagsType;
        /// Receive all multicast packets. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_ALLMULTI as IflagsType;
        /// Master of a load balancing bundle. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_MASTER;
        /// transmission in progress, tx hardware queue is full
        #[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
        IFF_OACTIVE;
        /// Protocol code on board.
        #[cfg(solarish)]
        IFF_INTELLIGENT as IflagsType;
        /// Slave of a load balancing bundle. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_SLAVE;
        /// Can't hear own transmissions.
        #[cfg(bsd)]
        IFF_SIMPLEX;
        /// Supports multicast. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        IFF_MULTICAST as IflagsType;
        /// Per link layer defined bit.
        #[cfg(bsd)]
        IFF_LINK0;
        /// Multicast using broadcast.
        #[cfg(solarish)]
        IFF_MULTI_BCAST as IflagsType;
        /// Is able to select media type via ifmap. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_PORTSEL;
        /// Per link layer defined bit.
        #[cfg(bsd)]
        IFF_LINK1;
        /// Non-unique address.
        #[cfg(solarish)]
        IFF_UNNUMBERED as IflagsType;
        /// Auto media selection active. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_AUTOMEDIA;
        /// Per link layer defined bit.
        #[cfg(bsd)]
        IFF_LINK2;
        /// Use alternate physical connection.
        #[cfg(any(freebsdlike, apple_targets))]
        IFF_ALTPHYS;
        /// DHCP controls interface.
        #[cfg(solarish)]
        IFF_DHCPRUNNING as IflagsType;
        /// The addresses are lost when the interface goes down. (see
        /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_DYNAMIC;
        /// Do not advertise.
        #[cfg(solarish)]
        IFF_PRIVATE as IflagsType;
        /// Driver signals L1 up. Volatile.
        #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
        IFF_LOWER_UP;
        /// Interface is in polling mode.
        #[cfg(any(target_os = "dragonfly"))]
        IFF_POLLING_COMPAT;
        /// Unconfigurable using ioctl(2).
        #[cfg(any(target_os = "freebsd"))]
        IFF_CANTCONFIG;
        /// Do not transmit packets.
        #[cfg(solarish)]
        IFF_NOXMIT as IflagsType;
        /// Driver signals dormant. Volatile.
        #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
        IFF_DORMANT;
        /// User-requested promisc mode.
        #[cfg(freebsdlike)]
        IFF_PPROMISC;
        /// Just on-link subnet.
        #[cfg(solarish)]
        IFF_NOLOCAL as IflagsType;
        /// Echo sent packets. Volatile.
        #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
        IFF_ECHO;
        /// User-requested monitor mode.
        #[cfg(freebsdlike)]
        IFF_MONITOR;
        /// Address is deprecated.
        #[cfg(solarish)]
        IFF_DEPRECATED as IflagsType;
        /// Static ARP.
        #[cfg(freebsdlike)]
        IFF_STATICARP;
        /// Address from stateless addrconf.
        #[cfg(solarish)]
        IFF_ADDRCONF as IflagsType;
        /// Interface is in polling mode.
        #[cfg(any(target_os = "dragonfly"))]
        IFF_NPOLLING;
        /// Router on interface.
        #[cfg(solarish)]
        IFF_ROUTER as IflagsType;
        /// Interface is in polling mode.
        #[cfg(any(target_os = "dragonfly"))]
        IFF_IDIRECT;
        /// Interface is winding down
        #[cfg(any(target_os = "freebsd"))]
        IFF_DYING;
        /// No NUD on interface.
        #[cfg(solarish)]
        IFF_NONUD as IflagsType;
        /// Interface is being renamed
        #[cfg(any(target_os = "freebsd"))]
        IFF_RENAMING;
        /// Anycast address.
        #[cfg(solarish)]
        IFF_ANYCAST as IflagsType;
        /// Don't exchange routing info.
        #[cfg(solarish)]
        IFF_NORTEXCH as IflagsType;
        /// Do not provide packet information
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_NO_PI as IflagsType;
        /// TUN device (no Ethernet headers)
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_TUN as IflagsType;
        /// TAP device
        #[cfg(any(linux_android, target_os = "fuchsia"))]
        IFF_TAP as IflagsType;
        /// IPv4 interface.
        #[cfg(solarish)]
        IFF_IPV4 as IflagsType;
        /// IPv6 interface.
        #[cfg(solarish)]
        IFF_IPV6 as IflagsType;
        /// in.mpathd test address
        #[cfg(solarish)]
        IFF_NOFAILOVER as IflagsType;
        /// Interface has failed
        #[cfg(solarish)]
        IFF_FAILED as IflagsType;
        /// Interface is a hot-spare
        #[cfg(solarish)]
        IFF_STANDBY as IflagsType;
        /// Functioning but not used
        #[cfg(solarish)]
        IFF_INACTIVE as IflagsType;
        /// Interface is offline
        #[cfg(solarish)]
        IFF_OFFLINE as IflagsType;
        /// Has CoS marking supported
        #[cfg(solarish)]
        IFF_COS_ENABLED as IflagsType;
        /// Prefer as source addr
        #[cfg(solarish)]
        IFF_PREFERRED as IflagsType;
        /// RFC3041
        #[cfg(solarish)]
        IFF_TEMPORARY as IflagsType;
        /// MTU set
        #[cfg(solarish)]
        IFF_FIXEDMTU as IflagsType;
        /// Cannot send/receive packets
        #[cfg(solarish)]
        IFF_VIRTUAL as IflagsType;
        /// Local address in use
        #[cfg(solarish)]
        IFF_DUPLICATE as IflagsType;
        /// IPMP IP interface
        #[cfg(solarish)]
        IFF_IPMP as IflagsType;
    }
);

impl fmt::Display for InterfaceFlags {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        bitflags::parser::to_writer(self, f)
    }
}


#[cfg(any(
    bsd,
    target_os = "fuchsia",
    target_os = "linux",
    solarish,
))]
mod if_nameindex {
    use super::*;

    use std::ffi::CStr;
    use std::fmt;
    use std::marker::PhantomData;
    use std::ptr::NonNull;

    /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
    /// (1, 2, 3, etc) that identifies it in the OS's networking stack.
    #[allow(missing_copy_implementations)]
    #[repr(transparent)]
    pub struct Interface(libc::if_nameindex);

    impl Interface {
        /// Obtain the index of this interface.
        pub fn index(&self) -> c_uint {
            self.0.if_index
        }

        /// Obtain the name of this interface.
        pub fn name(&self) -> &CStr {
            unsafe { CStr::from_ptr(self.0.if_name) }
        }
    }

    impl fmt::Debug for Interface {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.debug_struct("Interface")
                .field("index", &self.index())
                .field("name", &self.name())
                .finish()
        }
    }

    /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
    #[repr(transparent)]
    pub struct Interfaces {
        ptr: NonNull<libc::if_nameindex>,
    }

    impl Interfaces {
        /// Iterate over the interfaces in this list.
        #[inline]
        pub fn iter(&self) -> InterfacesIter<'_> {
            self.into_iter()
        }

        /// Convert this to a slice of interfaces. Note that the underlying interfaces list is
        /// null-terminated, so calling this calculates the length. If random access isn't needed,
        /// [`Interfaces::iter()`] should be used instead.
        pub fn to_slice(&self) -> &[Interface] {
            let ifs = self.ptr.as_ptr().cast();
            let len = self.iter().count();
            unsafe { std::slice::from_raw_parts(ifs, len) }
        }
    }

    impl Drop for Interfaces {
        fn drop(&mut self) {
            unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
        }
    }

    impl fmt::Debug for Interfaces {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            self.to_slice().fmt(f)
        }
    }

    impl<'a> IntoIterator for &'a Interfaces {
        type IntoIter = InterfacesIter<'a>;
        type Item = &'a Interface;
        #[inline]
        fn into_iter(self) -> Self::IntoIter {
            InterfacesIter {
                ptr: self.ptr.as_ptr(),
                _marker: PhantomData,
            }
        }
    }

    /// An iterator over the interfaces in an [`Interfaces`].
    #[derive(Debug)]
    pub struct InterfacesIter<'a> {
        ptr: *const libc::if_nameindex,
        _marker: PhantomData<&'a Interfaces>,
    }

    impl<'a> Iterator for InterfacesIter<'a> {
        type Item = &'a Interface;
        #[inline]
        fn next(&mut self) -> Option<Self::Item> {
            unsafe {
                if (*self.ptr).if_index == 0 {
                    None
                } else {
                    let ret = &*(self.ptr as *const Interface);
                    self.ptr = self.ptr.add(1);
                    Some(ret)
                }
            }
        }
    }

    /// Retrieve a list of the network interfaces available on the local system.
    ///
    /// ```
    /// let interfaces = nix::net::if_::if_nameindex().unwrap();
    /// for iface in &interfaces {
    ///     println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
    /// }
    /// ```
    pub fn if_nameindex() -> Result<Interfaces> {
        unsafe {
            let ifs = libc::if_nameindex();
            let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
            Ok(Interfaces { ptr })
        }
    }
}
#[cfg(any(
    bsd,
    target_os = "fuchsia",
    target_os = "linux",
    solarish,
))]
pub use if_nameindex::*;