Skip to content

Commit f1e5db7

Browse files
Markus Wanner (mwa)mwanner
Markus Wanner (mwa)
authored andcommitted
Add support for getifaddrs. Closes: #650.
1 parent 63ce242 commit f1e5db7

File tree

4 files changed

+287
-0
lines changed

4 files changed

+287
-0
lines changed

src/ifaddrs.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//! Query network interface addresses
2+
//!
3+
//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
4+
//! of interfaces and their associated addresses.
5+
6+
use std::ffi;
7+
use std::fmt;
8+
use std::iter::Iterator;
9+
use std::mem;
10+
use std::option::Option;
11+
12+
use libc;
13+
14+
use {Result, Errno};
15+
use sys::socket::SockAddr;
16+
use net::if_::*;
17+
18+
/// Describes a single address for an interface as returned by `getifaddrs`.
19+
#[derive(Clone, Eq, Hash, PartialEq)]
20+
pub struct InterfaceAddress {
21+
/// Name of the network interface
22+
pub interface_name: String,
23+
/// Flags as from `SIOCGIFFLAGS` ioctl
24+
pub flags: InterfaceFlags,
25+
/// Network address of this interface
26+
pub address: Option<SockAddr>,
27+
/// Netmask of this interface
28+
pub netmask: Option<SockAddr>,
29+
/// Broadcast address of this interface, if applicable
30+
pub broadcast: Option<SockAddr>,
31+
/// Point-to-point destination address
32+
pub destination: Option<SockAddr>,
33+
}
34+
35+
impl fmt::Debug for InterfaceAddress {
36+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37+
write!(f, "InterfaceAddress ({:?})", self.interface_name)
38+
}
39+
}
40+
41+
cfg_if! {
42+
if #[cfg(any(target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
43+
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
44+
info.ifa_ifu
45+
}
46+
} else {
47+
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
48+
info.ifa_dstaddr
49+
}
50+
}
51+
}
52+
53+
impl InterfaceAddress {
54+
/// Create an `InterfaceAddress` from the libc struct.
55+
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
56+
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
57+
let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) };
58+
let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) };
59+
let mut addr = InterfaceAddress {
60+
interface_name: ifname.to_string_lossy().to_string(),
61+
flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
62+
address: address,
63+
netmask: netmask,
64+
broadcast: None,
65+
destination: None,
66+
};
67+
68+
let ifu = get_ifu_from_sockaddr(info);
69+
if addr.flags.contains(IFF_POINTOPOINT) {
70+
addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) };
71+
} else if addr.flags.contains(IFF_BROADCAST) {
72+
addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) };
73+
}
74+
75+
addr
76+
}
77+
}
78+
79+
/// Holds the results of `getifaddrs`.
80+
///
81+
/// Use the function `getifaddrs` to create this Iterator. Note that the
82+
/// actual list of interfaces can be iterated once and will be freed as
83+
/// soon as the Iterator goes out of scope.
84+
#[derive(Debug, Eq, Hash, PartialEq)]
85+
pub struct InterfaceAddressIterator {
86+
base: *mut libc::ifaddrs,
87+
next: *mut libc::ifaddrs,
88+
}
89+
90+
impl Drop for InterfaceAddressIterator {
91+
fn drop(&mut self) {
92+
unsafe { libc::freeifaddrs(self.base) };
93+
}
94+
}
95+
96+
impl Iterator for InterfaceAddressIterator {
97+
type Item = InterfaceAddress;
98+
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
99+
match unsafe { self.next.as_ref() } {
100+
Some(ifaddr) => {
101+
self.next = ifaddr.ifa_next;
102+
Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
103+
}
104+
None => None,
105+
}
106+
}
107+
}
108+
109+
/// Get interface addresses using libc's `getifaddrs`
110+
///
111+
/// Note that the underlying implementation differs between OSes. Only the
112+
/// most common address families are supported by the nix crate (due to
113+
/// lack of time and complexity of testing). The address familiy is encoded
114+
/// in the specific variant of `SockAddr` returned for the fields `address`,
115+
/// `netmask`, `broadcast`, and `destination`. For any entry not supported,
116+
/// the returned list will contain a `None` entry.
117+
///
118+
/// # Example
119+
/// ```
120+
/// match nix::ifaddrs::getifaddrs() {
121+
/// Ok(ifaddrs) => for ifaddr in ifaddrs {
122+
/// match ifaddr.address {
123+
/// Some(address) => {
124+
/// println!("interface {} address {}",
125+
/// ifaddr.interface_name, address);
126+
/// },
127+
/// None => {}
128+
/// }
129+
/// },
130+
/// Err(err) => {
131+
/// println!("error getting interface addresses: {}", err)
132+
/// }
133+
/// }
134+
/// ```
135+
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
136+
let mut addrs: *mut libc::ifaddrs = unsafe { mem::uninitialized() };
137+
Errno::result(unsafe { libc::getifaddrs(&mut addrs) }).map(|_| {
138+
InterfaceAddressIterator {
139+
base: addrs,
140+
next: addrs,
141+
}
142+
})
143+
}
144+
145+
#[cfg(test)]
146+
mod tests {
147+
use super::*;
148+
149+
// Only checks if `getifaddrs` can be invoked without panicking.
150+
#[test]
151+
fn test_getifaddrs() {
152+
let _ = getifaddrs();
153+
}
154+
}

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ pub mod poll;
4343

4444
pub mod net;
4545

46+
#[cfg(any(target_os = "dragonfly",
47+
target_os = "freebsd",
48+
target_os = "ios",
49+
target_os = "linux",
50+
target_os = "macos",
51+
target_os = "netbsd",
52+
target_os = "openbsd"))]
53+
pub mod ifaddrs;
54+
4655
#[cfg(any(target_os = "linux", target_os = "android"))]
4756
pub mod sched;
4857

src/net/if_.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,77 @@ pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
1717
Ok(if_index)
1818
}
1919
}
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)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
26+
IFF_UP;
27+
/// Valid broadcast address set. (see
28+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
29+
IFF_BROADCAST;
30+
/// Internal debugging flag. (see
31+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
32+
IFF_DEBUG;
33+
/// Interface is a loopback interface. (see
34+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
35+
IFF_LOOPBACK;
36+
/// Interface is a point-to-point link. (see
37+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
38+
IFF_POINTOPOINT;
39+
/// Avoid use of trailers. (see
40+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
41+
#[cfg(any(target_os = "android",
42+
target_os = "fuchsia",
43+
target_os = "ios",
44+
target_os = "linux",
45+
target_os = "macos",
46+
target_os = "netbsd",
47+
target_os = "openbsd"))]
48+
IFF_NOTRAILERS;
49+
/// Resources allocated. (see
50+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
51+
#[cfg(any(target_os = "android",
52+
target_os = "dragonfly",
53+
target_os = "fuchsia",
54+
target_os = "ios",
55+
target_os = "linux",
56+
target_os = "macos",
57+
target_os = "netbsd",
58+
target_os = "openbsd"))]
59+
IFF_RUNNING;
60+
/// No arp protocol, L2 destination address not set. (see
61+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
62+
IFF_NOARP;
63+
/// Interface is in promiscuous mode. (see
64+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
65+
IFF_PROMISC;
66+
/// Receive all multicast packets. (see
67+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
68+
IFF_ALLMULTI;
69+
/// Master of a load balancing bundle. (see
70+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
71+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
72+
IFF_MASTER;
73+
/// Slave of a load balancing bundle. (see
74+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
75+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
76+
IFF_SLAVE;
77+
/// Supports multicast. (see
78+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
79+
IFF_MULTICAST;
80+
/// Is able to select media type via ifmap. (see
81+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
82+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
83+
IFF_PORTSEL;
84+
/// Auto media selection active. (see
85+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
86+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
87+
IFF_AUTOMEDIA;
88+
/// The addresses are lost when the interface goes down. (see
89+
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
90+
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
91+
IFF_DYNAMIC;
92+
}
93+
);

src/sys/socket/addr.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,28 @@ pub enum AddressFamily {
201201
Natm = libc::AF_NATM,
202202
}
203203

204+
impl AddressFamily {
205+
/// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
206+
/// the `sa_family` field of a `sockaddr`.
207+
///
208+
/// Currently only supports these address families: Unix, Inet (v4 & v6), Packet, Netlink
209+
/// and System. Returns None for unsupported or unknown address families.
210+
pub fn from_i32(family: i32) -> Option<AddressFamily> {
211+
match family {
212+
libc::AF_UNIX => Some(AddressFamily::Unix),
213+
libc::AF_INET => Some(AddressFamily::Inet),
214+
libc::AF_INET6 => Some(AddressFamily::Inet6),
215+
#[cfg(any(target_os = "android", target_os = "linux"))]
216+
libc::AF_NETLINK => Some(AddressFamily::Netlink),
217+
#[cfg(any(target_os = "android", target_os = "linux"))]
218+
libc::AF_PACKET => Some(AddressFamily::Packet),
219+
#[cfg(any(target_os = "macos", target_os = "macos"))]
220+
libc::AF_SYSTEM => Some(AddressFamily::System),
221+
_ => None
222+
}
223+
}
224+
}
225+
204226
#[derive(Copy)]
205227
pub enum InetAddr {
206228
V4(libc::sockaddr_in),
@@ -707,6 +729,34 @@ impl SockAddr {
707729
format!("{}", self)
708730
}
709731

732+
/// Creates a `SockAddr` struct from libc's sockaddr.
733+
///
734+
/// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System.
735+
/// Returns None for unsupported families.
736+
pub unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> {
737+
if addr.is_null() {
738+
None
739+
} else {
740+
match AddressFamily::from_i32((*addr).sa_family as i32) {
741+
Some(AddressFamily::Unix) => None,
742+
Some(AddressFamily::Inet) => Some(SockAddr::Inet(
743+
InetAddr::V4(*(addr as *const libc::sockaddr_in)))),
744+
Some(AddressFamily::Inet6) => Some(SockAddr::Inet(
745+
InetAddr::V6(*(addr as *const libc::sockaddr_in6)))),
746+
#[cfg(any(target_os = "android", target_os = "linux"))]
747+
Some(AddressFamily::Netlink) => Some(SockAddr::Netlink(
748+
NetlinkAddr(*(addr as *const libc::sockaddr_nl)))),
749+
#[cfg(any(target_os = "ios", target_os = "macos"))]
750+
Some(AddressFamily::System) => Some(SockAddr::SysControl(
751+
SysControlAddr(*(addr as *const sys_control::sockaddr_ctl)))),
752+
// Other address families are currently not supported and simply yield a None
753+
// entry instead of a proper conversion to a `SockAddr`.
754+
Some(_) => None,
755+
None => None,
756+
}
757+
}
758+
}
759+
710760
pub unsafe fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) {
711761
match *self {
712762
SockAddr::Inet(InetAddr::V4(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in>() as libc::socklen_t),

0 commit comments

Comments
 (0)