summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarkus Wanner <markus@bluegap.ch>2017-07-10 20:43:44 +0200
committerMarkus Wanner <markus@bluegap.ch>2017-12-03 18:04:06 +0100
commit985ea01aa84811378eede881dad6f2f9020aa5d1 (patch)
treeb818216ed82cf901884e80ea0eaf627ac9a423af /src
parent86ebf7b0eac4cd0d092b816060042c55ca8871c5 (diff)
downloadnix-985ea01aa84811378eede881dad6f2f9020aa5d1.zip
Add support for getifaddrs. Closes: #650.
Diffstat (limited to 'src')
-rw-r--r--src/ifaddrs.rs153
-rw-r--r--src/lib.rs9
-rw-r--r--src/net/if_.rs241
-rw-r--r--src/sys/socket/addr.rs48
4 files changed, 451 insertions, 0 deletions
diff --git a/src/ifaddrs.rs b/src/ifaddrs.rs
new file mode 100644
index 00000000..2b3024b5
--- /dev/null
+++ b/src/ifaddrs.rs
@@ -0,0 +1,153 @@
+//! Query network interface addresses
+//!
+//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
+//! of interfaces and their associated addresses.
+
+use std::ffi;
+use std::fmt;
+use std::iter::Iterator;
+use std::mem;
+use std::option::Option;
+
+use libc;
+
+use {Result, Errno};
+use sys::socket::SockAddr;
+use net::if_::*;
+
+/// Describes a single address for an interface as returned by `getifaddrs`.
+#[derive(Clone, Eq, Hash, PartialEq)]
+pub struct InterfaceAddress {
+ /// Name of the network interface
+ pub interface_name: String,
+ /// Flags as from `SIOCGIFFLAGS` ioctl
+ pub flags: InterfaceFlags,
+ /// Network address of this interface
+ pub address: Option<SockAddr>,
+ /// Netmask of this interface
+ pub netmask: Option<SockAddr>,
+ /// Broadcast address of this interface, if applicable
+ pub broadcast: Option<SockAddr>,
+ /// Point-to-point destination address
+ pub destination: Option<SockAddr>,
+}
+
+impl fmt::Debug for InterfaceAddress {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "InterfaceAddress ({:?})", self.interface_name)
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
+ fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
+ info.ifa_ifu
+ }
+ } else {
+ fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
+ info.ifa_dstaddr
+ }
+ }
+}
+
+impl InterfaceAddress {
+ /// Create an `InterfaceAddress` from the libc struct.
+ fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
+ let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
+ let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) };
+ let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) };
+ let mut addr = InterfaceAddress {
+ interface_name: ifname.to_string_lossy().to_string(),
+ flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
+ address: address,
+ netmask: netmask,
+ broadcast: None,
+ destination: None,
+ };
+
+ let ifu = get_ifu_from_sockaddr(info);
+ if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
+ addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) };
+ } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
+ addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) };
+ }
+
+ addr
+ }
+}
+
+/// Holds the results of `getifaddrs`.
+///
+/// Use the function `getifaddrs` to create this Iterator. Note that the
+/// actual list of interfaces can be iterated once and will be freed as
+/// soon as the Iterator goes out of scope.
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct InterfaceAddressIterator {
+ base: *mut libc::ifaddrs,
+ next: *mut libc::ifaddrs,
+}
+
+impl Drop for InterfaceAddressIterator {
+ fn drop(&mut self) {
+ unsafe { libc::freeifaddrs(self.base) };
+ }
+}
+
+impl Iterator for InterfaceAddressIterator {
+ type Item = InterfaceAddress;
+ fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+ match unsafe { self.next.as_ref() } {
+ Some(ifaddr) => {
+ self.next = ifaddr.ifa_next;
+ Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
+ }
+ None => None,
+ }
+ }
+}
+
+/// Get interface addresses using libc's `getifaddrs`
+///
+/// Note that the underlying implementation differs between OSes. Only the
+/// most common address families are supported by the nix crate (due to
+/// lack of time and complexity of testing). The address family is encoded
+/// in the specific variant of `SockAddr` returned for the fields `address`,
+/// `netmask`, `broadcast`, and `destination`. For any entry not supported,
+/// the returned list will contain a `None` entry.
+///
+/// # Example
+/// ```
+/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
+/// for ifaddr in addrs {
+/// match ifaddr.address {
+/// Some(address) => {
+/// println!("interface {} address {}",
+/// ifaddr.interface_name, address);
+/// },
+/// None => {
+/// println!("interface {} with unsupported address family",
+/// ifaddr.interface_name);
+/// }
+/// }
+/// }
+/// ```
+pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
+ let mut addrs: *mut libc::ifaddrs = unsafe { mem::uninitialized() };
+ Errno::result(unsafe { libc::getifaddrs(&mut addrs) }).map(|_| {
+ InterfaceAddressIterator {
+ base: addrs,
+ next: addrs,
+ }
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ // Only checks if `getifaddrs` can be invoked without panicking.
+ #[test]
+ fn test_getifaddrs() {
+ let _ = getifaddrs();
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index cadc7fb4..ac39b3ed 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -43,6 +43,15 @@ pub mod poll;
pub mod net;
+#[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+pub mod ifaddrs;
+
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod sched;
diff --git a/src/net/if_.rs b/src/net/if_.rs
index 7edfc998..19b1ee73 100644
--- a/src/net/if_.rs
+++ b/src/net/if_.rs
@@ -17,3 +17,244 @@ pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
Ok(if_index)
}
}
+
+libc_bitflags!(
+ /// Standard interface flags, used by `getifaddrs`
+ pub struct InterfaceFlags: libc::c_int {
+ /// Interface is running. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_UP;
+ /// Valid broadcast address set. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_BROADCAST;
+ /// Internal debugging flag. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_DEBUG;
+ /// Interface is a loopback interface. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_LOOPBACK;
+ /// Interface is a point-to-point link. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_POINTOPOINT;
+ /// Avoid use of trailers. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ IFF_NOTRAILERS;
+ /// Interface manages own routes.
+ #[cfg(any(target_os = "dragonfly"))]
+ IFF_SMART;
+ /// Resources allocated. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ IFF_RUNNING;
+ /// No arp protocol, L2 destination address not set. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_NOARP;
+ /// Interface is in promiscuous mode. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_PROMISC;
+ /// Receive all multicast packets. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_ALLMULTI;
+ /// Master of a load balancing bundle. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ IFF_MASTER;
+ /// transmission in progress, tx hardware queue is full
+ #[cfg(any(target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ IFF_OACTIVE;
+ /// Protocol code on board.
+ #[cfg(target_os = "solaris")]
+ IFF_INTELLIGENT;
+ /// Slave of a load balancing bundle. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ IFF_SLAVE;
+ /// Can't hear own transmissions.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "osx"))]
+ IFF_SIMPLEX;
+ /// Supports multicast. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_MULTICAST;
+ /// Per link layer defined bit.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ IFF_LINK0;
+ /// Multicast using broadcast.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_MULTI_BCAST;
+ /// Is able to select media type via ifmap. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ IFF_PORTSEL;
+ /// Per link layer defined bit.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ IFF_LINK1;
+ /// Non-unique address.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_UNNUMBERED;
+ /// Auto media selection active. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ IFF_AUTOMEDIA;
+ /// Per link layer defined bit.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ IFF_LINK2;
+ /// Use alternate physical connection.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"))]
+ IFF_ALTPHYS;
+ /// DHCP controlls interface.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_DHCPRUNNING;
+ /// The addresses are lost when the interface goes down. (see
+ /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ IFF_DYNAMIC;
+ /// Do not advertise.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_PRIVATE;
+ /// 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(any(target_os = "solaris"))]
+ IFF_NOXMIT;
+ /// Driver signals dormant. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ IFF_DORMANT;
+ /// User-requested promisc mode.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ IFF_PPROMISC;
+ /// Just on-link subnet.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_NOLOCAL;
+ /// Echo sent packets. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ IFF_ECHO;
+ /// User-requested monitor mode.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ IFF_MONITOR;
+ /// Address is deprecated.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_DEPRECATED;
+ /// Static ARP.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ IFF_STATICARP;
+ /// Address from stateless addrconf.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_ADDRCONF;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ IFF_NPOLLING;
+ /// Router on interface.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_ROUTER;
+ /// 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(any(target_os = "solaris"))]
+ IFF_NONUD;
+ /// Interface is being renamed
+ #[cfg(any(target_os = "freebsd"))]
+ IFF_RENAMING;
+ /// Anycast address.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_ANYCAST;
+ /// Don't exchange routing info.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_NORTEXCH;
+ /// IPv4 interface.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_IPV4;
+ /// IPv6 interface.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_IPV6;
+ /// in.mpathd test address
+ #[cfg(any(target_os = "solaris"))]
+ IFF_NOFAILOVER;
+ /// Interface has failed
+ #[cfg(any(target_os = "solaris"))]
+ IFF_FAILED;
+ /// Interface is a hot-spare
+ #[cfg(any(target_os = "solaris"))]
+ IFF_STANDBY;
+ /// Functioning but not used
+ #[cfg(any(target_os = "solaris"))]
+ IFF_INACTIVE;
+ /// Interface is offline
+ #[cfg(any(target_os = "solaris"))]
+ IFF_OFFLINE;
+ #[cfg(any(target_os = "solaris"))]
+ IFF_COS_ENABLED;
+ /// Prefer as source addr.
+ #[cfg(any(target_os = "solaris"))]
+ IFF_PREFERRED;
+ /// RFC3041
+ #[cfg(any(target_os = "solaris"))]
+ IFF_TEMPORARY;
+ /// MTU set with SIOCSLIFMTU
+ #[cfg(any(target_os = "solaris"))]
+ IFF_FIXEDMTU;
+ /// Cannot send / receive packets
+ #[cfg(any(target_os = "solaris"))]
+ IFF_VIRTUAL;
+ /// Local address in use
+ #[cfg(any(target_os = "solaris"))]
+ IFF_DUPLICATE;
+ /// IPMP IP interface
+ #[cfg(any(target_os = "solaris"))]
+ IFF_IPMP;
+ }
+);
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs
index 29832b37..a1f86518 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -201,6 +201,26 @@ pub enum AddressFamily {
Natm = libc::AF_NATM,
}
+impl AddressFamily {
+ /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
+ /// the `sa_family` field of a `sockaddr`.
+ ///
+ /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink
+ /// and System. Returns None for unsupported or unknown address families.
+ pub fn from_i32(family: i32) -> Option<AddressFamily> {
+ match family {
+ libc::AF_UNIX => Some(AddressFamily::Unix),
+ libc::AF_INET => Some(AddressFamily::Inet),
+ libc::AF_INET6 => Some(AddressFamily::Inet6),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => Some(AddressFamily::Netlink),
+ #[cfg(any(target_os = "macos", target_os = "macos"))]
+ libc::AF_SYSTEM => Some(AddressFamily::System),
+ _ => None
+ }
+ }
+}
+
#[derive(Copy)]
pub enum InetAddr {
V4(libc::sockaddr_in),
@@ -707,6 +727,34 @@ impl SockAddr {
format!("{}", self)
}
+ /// Creates a `SockAddr` struct from libc's sockaddr.
+ ///
+ /// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System.
+ /// Returns None for unsupported families.
+ pub unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> {
+ if addr.is_null() {
+ None
+ } else {
+ match AddressFamily::from_i32((*addr).sa_family as i32) {
+ Some(AddressFamily::Unix) => None,
+ Some(AddressFamily::Inet) => Some(SockAddr::Inet(
+ InetAddr::V4(*(addr as *const libc::sockaddr_in)))),
+ Some(AddressFamily::Inet6) => Some(SockAddr::Inet(
+ InetAddr::V6(*(addr as *const libc::sockaddr_in6)))),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Some(AddressFamily::Netlink) => Some(SockAddr::Netlink(
+ NetlinkAddr(*(addr as *const libc::sockaddr_nl)))),
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ Some(AddressFamily::System) => Some(SockAddr::SysControl(
+ SysControlAddr(*(addr as *const sys_control::sockaddr_ctl)))),
+ // Other address families are currently not supported and simply yield a None
+ // entry instead of a proper conversion to a `SockAddr`.
+ Some(_) => None,
+ None => None,
+ }
+ }
+ }
+
pub unsafe fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) {
match *self {
SockAddr::Inet(InetAddr::V4(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in>() as libc::socklen_t),