-
Notifications
You must be signed in to change notification settings - Fork 680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add getifaddrs/ifaddrs wrapper #764
Changes from 9 commits
986ebb0
4021a36
71e508e
5457cca
87b93a1
9dc2fb0
ee5d4b5
0751bda
fc4a0f2
c8cb083
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use libc; | ||
use libc::c_uint; | ||
|
||
libc_bitflags! { | ||
pub struct IffFlags: c_uint { | ||
/// Interface is running. | ||
IFF_UP as c_uint; | ||
/// Valid broadcast address set. | ||
IFF_BROADCAST as c_uint; | ||
/// Internal debugging flag. | ||
IFF_DEBUG as c_uint; | ||
/// Interface is a loopback interface. | ||
IFF_LOOPBACK as c_uint; | ||
/// Interface is a point-to-point link. | ||
IFF_POINTOPOINT as c_uint; | ||
/// Resources allocated. | ||
IFF_NOTRAILERS as c_uint; | ||
/// No arp protocol, L2 destination address not set. | ||
IFF_RUNNING as c_uint; | ||
/// Interface is in promiscuous mode. | ||
IFF_NOARP as c_uint; | ||
/// Avoid use of trailers. | ||
IFF_PROMISC as c_uint; | ||
/// Receive all multicast packets. | ||
IFF_ALLMULTI as c_uint; | ||
/// Master of a load balancing bundle. | ||
IFF_MASTER as c_uint; | ||
/// Slave of a load balancing bundle. | ||
IFF_SLAVE as c_uint; | ||
/// Supports multicast | ||
IFF_MULTICAST as c_uint; | ||
/// Is able to select media type via ifmap. | ||
IFF_PORTSEL as c_uint; | ||
/// Auto media selection active. | ||
IFF_AUTOMEDIA as c_uint; | ||
/// The addresses are lost when the interface goes down. | ||
IFF_DYNAMIC as c_uint; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this blank line. |
||
// These flags are available on modern Linuxes | ||
#[cfg(any(target_os = "linux", target_os = "android"))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The OSes here should be listed alphabetically. |
||
/// Driver signals L1 up (since Linux 2.6.17) | ||
IFF_LOWER_UP as c_uint; | ||
#[cfg(any(target_os = "linux", target_os = "android"))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
/// Driver signals dormant (since Linux 2.6.17) | ||
IFF_DORMANT as c_uint; | ||
#[cfg(any(target_os = "linux", target_os = "android"))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
/// Echo sent packets (since Linux 2.6.25) | ||
IFF_ECHO as c_uint; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
//! `ifaddrs` provides a safe interface for the system's network interface data. | ||
//! | ||
//! The `InterfaceAddrs` struct provides access to the system's network | ||
//! interface data. You can either iterate over it or consume it and convert | ||
//! it into an `InterfaceMap` (a `HashMap<String, Vec<InterfaceAddr>>`) for | ||
//! more convenient access by interface name. | ||
//! | ||
//! # Examples | ||
//! | ||
//! You can access the basic information of the system in a few lines. | ||
//! The following program prints all the known addresses. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's get a colon at the end of this line. |
||
//! | ||
//! ``` | ||
//! use nix::net::ifaddrs::InterfaceAddrs; | ||
//! | ||
//! let addrs = InterfaceAddrs::getifaddrs() | ||
//! .expect("Failed to enumerate network interfaces."); | ||
//! | ||
//! for addr in addrs { | ||
//! println!("{}: {:?}", addr.name, addr.address); | ||
//! } | ||
//! ``` | ||
//! | ||
//! The `IffFlags` struct provides access to info about the | ||
//! state of an interface. This program prints the addresses of only | ||
//! interfaces which are up. | ||
//! | ||
//! ``` | ||
//! use nix::net::ifaddrs::{InterfaceAddrs, iff_flags}; | ||
//! | ||
//! let addrs = InterfaceAddrs::getifaddrs() | ||
//! .expect("Failed to eunmerate network interfaces."); | ||
//! | ||
//! for addr in addrs { | ||
//! if addr.flags.contains(iff_flags::IFF_UP) { | ||
//! println!("{}: {:?}", addr.name, addr.address); | ||
//! } | ||
//! } | ||
//! ``` | ||
//! | ||
//! You can convert the `InterfaceAddrs` struct into a `HashMap` easily. | ||
//! `InterfaceMap` is an alias for `HashMap<String, Vec<InterfaceAddr>>` for | ||
//! easier reference. | ||
//! | ||
//! ``` | ||
//! use nix::net::ifaddrs::{InterfaceAddrs, InterfaceAddr, InterfaceMap}; | ||
//! use std::collections::HashMap; | ||
//! | ||
//! let interfaces: InterfaceMap = | ||
//! InterfaceAddrs::getifaddrs() | ||
//! .expect("Failed to enumerate network interfaces.") | ||
//! .into(); // Convert to a hash map | ||
//! | ||
//! // Print all the addresses of the loopback interface | ||
//! if let Some(addrs) = interfaces.get("lo") { | ||
//! println!("Loopback addresses:"); | ||
//! for addr in addrs { | ||
//! println!("\t{:?}", addr); | ||
//! } | ||
//! } | ||
//! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this blank line. |
||
//! ``` | ||
//! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And this one. |
||
|
||
use libc; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The grouping here should be |
||
use std::ptr::null_mut; | ||
use std::ffi::CStr; | ||
use std::collections::HashMap; | ||
use errno::{Errno, errno}; | ||
use Error; | ||
use Result; | ||
|
||
pub mod iff_flags; | ||
use self::iff_flags::IffFlags; | ||
|
||
mod sockaddr; | ||
use self::sockaddr::{IfAddrValue, sockaddr_to_ifaddrvalue}; | ||
|
||
mod tests; | ||
|
||
pub type InterfaceMap<'a> = HashMap<String, Vec<InterfaceAddr<'a>>>; | ||
|
||
/// Represents a handle into the operating system's knowledge about network | ||
/// interfaces present on the system. Allows the user to iterate over | ||
/// interface configurations. | ||
pub struct InterfaceAddrs<'a> { | ||
inner: *mut libc::ifaddrs, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even those these inner members are not public, it's useful for developers to get some docs on these. Can you provide doc comments for each element here? |
||
current: Option<&'a libc::ifaddrs>, | ||
do_free: bool, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like |
||
} | ||
|
||
impl<'a> InterfaceAddrs<'a> { | ||
/// Creates an `InterfaceAddrs` from a raw pointer, without calling into | ||
/// the `libc`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need the "the" here. |
||
/// | ||
/// The destructor will not attempt to free memory on an InterfaceAddrs | ||
/// created in this way. | ||
/// | ||
/// # Unsafety | ||
/// The caller is responsible for making sure the given pointer is not | ||
/// in invalid memory. | ||
/// | ||
/// # Errors | ||
/// `Err(())` will be returned if `p` was void. | ||
pub unsafe fn from_raw(p: *mut libc::ifaddrs) -> ::std::result::Result<Self, ()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this function is only used by test code, it should not be |
||
match p.as_ref() { | ||
Some(r) => Ok(Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fails to compile. You should change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It compiles for me? Something must be screwy with my Rustup, I'll change it. Thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You fixed the wrong line. Just check the Travis build log to see the error I'm talking about. If it works for you, that's probably because you're using a newer version of Rust than Travis. We're using 1.13.0 in Travis. |
||
inner: p, | ||
current: Some(r), | ||
do_free: false, | ||
}), | ||
None => Err(()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't an Invalid Argument error be provided here instead which would be a bit clearer? |
||
} | ||
} | ||
|
||
/// Produce an `InterfaceAddrs` from the system's information. | ||
pub fn getifaddrs() -> Result<Self> { | ||
let mut p = null_mut(); | ||
|
||
unsafe { | ||
libc::getifaddrs(&mut p); | ||
} | ||
|
||
// UNSAFETY: *mut -> &'static mut. This is known to be either in valid | ||
// memory or null based on the guarantees of getifaddrs() | ||
match unsafe { p.as_ref() } { | ||
Some(r) => Ok(Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once you eliminate |
||
inner: p, | ||
current: Some(r), | ||
do_free: true, | ||
}), | ||
|
||
None => Err(Error::from(Errno::from_i32(errno()))), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> From<InterfaceAddrs<'a>> for HashMap<String, Vec<InterfaceAddr<'a>>> { | ||
/// Collect an `InterfaceAddrs` into a `HashMap<String, InterfaceAddr>`. | ||
fn from(ia: InterfaceAddrs<'a>) -> HashMap<String, Vec<InterfaceAddr<'a>>> { | ||
let mut m = HashMap::new(); | ||
for i in ia { | ||
if !m.contains_key(&i.name) { | ||
m.insert(i.name.clone(), Vec::new()); | ||
} | ||
// Unwrap here because contains is checked above | ||
m.get_mut(&i.name).unwrap().push(i); | ||
} | ||
|
||
m | ||
} | ||
} | ||
|
||
impl<'a> Drop for InterfaceAddrs<'a> { | ||
fn drop(&mut self) { | ||
if self.do_free { | ||
// UNSAFETY: Calling libc FFI function which frees previously allocated | ||
// memory. | ||
unsafe { | ||
// Ask the libc to drop free the memory it allocated when | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "to free the memory" I think is what this should read. |
||
// the struct was created. | ||
libc::freeifaddrs(self.inner as *mut libc::ifaddrs); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this blank line. Should only be 1 between things in a file. |
||
/// Represents the configuration and state of a network interface. | ||
/// Interfaces are uniquely identified by name, and each interface is likely | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs a blank line above for the generated docs to look pretty (run rustdoc to see what I'm talking about). |
||
/// to be referred to multiple times, e.g. one for IPv4 and one for IPv6. | ||
#[derive(Debug, Clone)] | ||
pub struct InterfaceAddr<'a> { | ||
/// The name of the interface | ||
pub name: String, | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove these blank lines from between the elements in this struct. |
||
/// The address assigned to the interface for this protocol. | ||
/// A value of `None` means the libc reported a type of address that | ||
/// `std::net` doesn't understand. | ||
pub address: Option<IfAddrValue<'a>>, | ||
|
||
/// The netmasks assigned to the interface for this protocol. | ||
/// A value of `None` means the libc reported a type of address that | ||
/// `std::net` doesn't understand. | ||
pub netmask: Option<IfAddrValue<'a>>, | ||
|
||
/// The ifu assigned to the interface for this protocol. | ||
/// A value of `{Broadcast, Destination}Addr(None)` means the libc reported | ||
/// a type of address that `std::net` doesn't understand, while a value of | ||
/// `Neither` means that the interface has neither a valid broadcast address | ||
/// nor a point-to-point destination address. | ||
pub ifu: InterfaceIfu<'a>, | ||
|
||
/// Flags regarding the interface's behaviour and state | ||
pub flags: IffFlags, | ||
} | ||
|
||
/// Represents the ifu of an interface: either its broadcast address or | ||
/// point-to-point destination address. | ||
#[derive(Debug, Clone)] | ||
pub enum InterfaceIfu<'a> { | ||
BroadcastAddr(Option<IfAddrValue<'a>>), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All variants here need doc comments. |
||
DestinationAddr(Option<IfAddrValue<'a>>), | ||
Neither, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, that comment on 153 refers to a |
||
} | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the extra blank line here. |
||
impl<'a> Iterator for InterfaceAddrs<'a> { | ||
type Item = InterfaceAddr<'a>; | ||
fn next(&mut self) -> Option<InterfaceAddr<'a>> { | ||
// If the current ifaddrs is None, there are no more ifaddrs to inspect | ||
if self.current.is_none() { | ||
return None; | ||
} | ||
|
||
// Workaround for the borrow checker being overzealous | ||
// (without ptr_temp, self.current would technically | ||
// "still be in use" when the loop ends, meaning we | ||
// couldn't advance to the next struct) | ||
let ptr_temp = self.current.clone(); | ||
let p = ptr_temp.as_ref().unwrap(); | ||
|
||
// Get a pointer to the interface's name | ||
let name_ptr = p.ifa_name; | ||
// Check that name_ptr isn't null. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this check might be more semantically clear if you used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, |
||
if name_ptr.is_null() { | ||
panic!("getifaddrs() gave an ifaddrs struct with a null ifa_name"); | ||
} | ||
|
||
// UNSAFETY: Constructing CStr from pointer. If this pointer is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the pointer being null is checked above with a panic if it's NULL at this point, it's a compiler error or something's super wrong with the CPU. I think this comment can be cleaned up a big to clarify that. Same for the one below. |
||
// null it's a libc bug; it's checked above. | ||
let name = unsafe { CStr::from_ptr(name_ptr) } | ||
.to_string_lossy() | ||
.into_owned(); | ||
|
||
// Interpret the flags field into a typed version of those flags | ||
let flags = IffFlags::from_bits_truncate(p.ifa_flags); | ||
|
||
// Get IfAddrValue representations of the address and netmask | ||
// UNSAFETY: sockaddr_to_ifaddrvalue requires valid pointer. | ||
let address = unsafe { sockaddr_to_ifaddrvalue(p.ifa_addr) }; | ||
// UNSAFETY: sockaddr_to_ifaddrvalue requires valid pointer. | ||
let netmask = unsafe { sockaddr_to_ifaddrvalue(p.ifa_netmask) }; | ||
|
||
// Figure out which ifu type is needed and create it | ||
let ifu = if flags.contains(iff_flags::IFF_POINTOPOINT) { | ||
// Point to point destination address | ||
// UNSAFETY: sockaddr_to_ifaddrvalue requires valid pointer. | ||
let ifu_addr = unsafe { sockaddr_to_ifaddrvalue(p.ifa_ifu) }; | ||
InterfaceIfu::DestinationAddr(ifu_addr) | ||
} else if flags.contains(iff_flags::IFF_BROADCAST) { | ||
// Broadcast address | ||
// UNSAFETY: sockaddr_to_ifaddrvalue requires valid pointer. | ||
let ifu_addr = unsafe { sockaddr_to_ifaddrvalue(p.ifa_ifu) }; | ||
InterfaceIfu::BroadcastAddr(ifu_addr) | ||
} else { | ||
InterfaceIfu::Neither | ||
}; | ||
|
||
// Move along the list to the next ifaddrs struct | ||
// UNSAFETY: *mut -> Option<&'static mut>. | ||
// This is known to be in valid memory or null. | ||
self.current = unsafe { p.ifa_next.as_ref() }; | ||
|
||
Some(InterfaceAddr { | ||
name: name, | ||
address: address, | ||
netmask: netmask, | ||
ifu: ifu, | ||
flags: flags, | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; | ||
use std::mem::transmute; | ||
use std::fmt; | ||
use libc; | ||
|
||
/// Represents the actual data of an address in use by an interface. | ||
#[derive(Clone)] | ||
pub enum IfAddrValue<'a> { | ||
IpAddr(IpAddr), | ||
Other(&'a libc::sockaddr), | ||
} | ||
|
||
impl<'a> fmt::Debug for IfAddrValue<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match *self { | ||
IfAddrValue::IpAddr(ref addr) => write!(f, "IfAddrValue({:?})", addr), | ||
IfAddrValue::Other(_) => write!(f, "IfAddrValue(<Unknown Address Type>)"), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> From<IpAddr> for IfAddrValue<'a> { | ||
fn from(ip: IpAddr) -> IfAddrValue<'a> { | ||
IfAddrValue::IpAddr(ip) | ||
} | ||
} | ||
|
||
impl<'a> From<&'a libc::sockaddr> for IfAddrValue<'a> { | ||
fn from(addr: &'a libc::sockaddr) -> IfAddrValue<'a> { | ||
IfAddrValue::Other(addr) | ||
} | ||
} | ||
|
||
/// Converts a `libc::sockaddr` into an `Option<IfAddrValue>`. | ||
/// | ||
/// It returns `None` if the libc reports a type of address other than | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've been chewing on this, and I've concluded that we're doing it wrong. There are two problems:
Both of these problems share a common solution. For unknown address families, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that solution a lot, actually. I'll go ahead and implement that. |
||
/// IPv4, or IPv6, or if the given `sockaddr_input` was null. | ||
/// | ||
/// # Unsafety | ||
/// | ||
/// The caller is responsible for guaranteeing that the provided reference | ||
/// refers to valid memory. | ||
pub unsafe fn sockaddr_to_ifaddrvalue<'a>( | ||
sockaddr_input: *mut libc::sockaddr, | ||
) -> Option<IfAddrValue<'a>> { | ||
if let Some(sa) = sockaddr_input.as_ref() { | ||
// Only IPv4 and IPv6 are supported. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have |
||
match sa.sa_family as i32 { | ||
libc::AF_INET => { | ||
let data_v4: &libc::sockaddr_in = transmute(sa); | ||
// Transmuting a u32 into a [u8; 4] because | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be wrapped to the same number of columns as the code. We should be wrapping everything to 99 characters (which is the Rust standard). |
||
// the address is in network byte order. | ||
let s_addr_v4: [u8; 4] = transmute(data_v4.sin_addr.s_addr); | ||
Some(IpAddr::V4(Ipv4Addr::from(s_addr_v4)).into()) | ||
} | ||
libc::AF_INET6 => { | ||
let data_v6: &libc::sockaddr_in6 = transmute(sa); | ||
Some(IpAddr::V6(Ipv6Addr::from(data_v6.sin6_addr.s6_addr)).into()) | ||
} | ||
_ => Some(sa.into()), | ||
} | ||
} else { | ||
None | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine these into
use libc::{self, c_uint};