From 986ebb0cf85f50a52371951e917a21e98fc52963 Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Sat, 9 Sep 2017 11:05:07 -0500 Subject: [PATCH 01/10] Add preliminary getifaddrs code --- src/net/ifaddrs/if_flags.rs | 46 +++++++ src/net/ifaddrs/mod.rs | 250 ++++++++++++++++++++++++++++++++++++ src/net/ifaddrs/sockaddr.rs | 47 +++++++ src/net/mod.rs | 2 + 4 files changed, 345 insertions(+) create mode 100644 src/net/ifaddrs/if_flags.rs create mode 100644 src/net/ifaddrs/mod.rs create mode 100644 src/net/ifaddrs/sockaddr.rs diff --git a/src/net/ifaddrs/if_flags.rs b/src/net/ifaddrs/if_flags.rs new file mode 100644 index 0000000000..0bb4cf7edd --- /dev/null +++ b/src/net/ifaddrs/if_flags.rs @@ -0,0 +1,46 @@ +use libc; +use libc::c_uint; + +bitflags! { + pub struct InterfaceFlags: c_uint { + /// Interface is running. + const IFF_UP = libc::IFF_UP as c_uint; + /// Valid broadcast address set. + const IFF_BROADCAST = libc::IFF_BROADCAST as c_uint; + /// Internal debugging flag. + const IFF_DEBUG = libc::IFF_DEBUG as c_uint; + /// Interface is a loopback interface. + const IFF_LOOPBACK = libc::IFF_LOOPBACK as c_uint; + /// Interface is a point-to-point link. + const IFF_POINTOPOINT = libc::IFF_POINTOPOINT as c_uint; + /// Resources allocated. + const IFF_NOTRAILERS = libc::IFF_NOTRAILERS as c_uint; + /// No arp protocol, L2 destination address not set. + const IFF_RUNNING = libc::IFF_RUNNING as c_uint; + /// Interface is in promiscuous mode. + const IFF_NOARP = libc::IFF_NOARP as c_uint; + /// Avoid use of trailers. + const IFF_PROMISC = libc::IFF_PROMISC as c_uint; + /// Receive all multicast packets. + const IFF_ALLMULTI = libc::IFF_ALLMULTI as c_uint; + /// Master of a load balancing bundle. + const IFF_MASTER = libc::IFF_MASTER as c_uint; + /// Slave of a load balancing bundle. + const IFF_SLAVE = libc::IFF_SLAVE as c_uint; + /// Supports multicast + const IFF_MULTICAST = libc::IFF_MULTICAST as c_uint; + /// Is able to select media type via ifmap. + const IFF_PORTSEL = libc::IFF_PORTSEL as c_uint; + /// Auto media selection active. + const IFF_AUTOMEDIA = libc::IFF_AUTOMEDIA as c_uint; + /// The addresses are lost when the interface goes down. + const IFF_DYNAMIC = libc::IFF_DYNAMIC as c_uint; + /// Driver signals L1 up (since Linux 2.6.17) + const IFF_LOWER_UP = 1<<16; + /// Driver signals dormant (since Linux 2.6.17) + const IFF_DORMANT = 1<<17; + /// Echo sent packets (since Linux 2.6.25) + const IFF_ECHO = 1<<18; + } +} + diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs new file mode 100644 index 0000000000..cda1264730 --- /dev/null +++ b/src/net/ifaddrs/mod.rs @@ -0,0 +1,250 @@ +//! `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>`) 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. +//! +//! ``` +//! use nix::net::ifaddrs::InterfaceAddrs; +//! +//! let addrs = InterfaceAddrs::query_system() +//! .expect("System has no network interfaces."); +//! +//! for addr in addrs { +//! println!("{}: {:?}", addr.name, addr.address); +//! } +//! ``` +//! +//! The `InterfaceFlags` 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, if_flags}; +//! +//! let addrs = InterfaceAddrs::query_system() +//! .expect("System has no network interfaces."); +//! +//! for addr in addrs { +//! if addr.flags.contains(if_flags::IFF_UP) { +//! println!("{}: {:?}", addr.name, addr.address); +//! } +//! } +//! ``` +//! +//! You can convert the `InterfaceAddrs` struct into a `HashMap` easily. +//! `InterfaceMap` is an alias for `HashMap>` for +//! easier reference. +//! +//! ``` +//! use nix::net::ifaddrs::{InterfaceAddrs, InterfaceAddr, InterfaceMap}; +//! use std::collections::HashMap; +//! +//! let interfaces: InterfaceMap = +//! InterfaceAddrs::query_system() +//! .expect("System has no 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); +//! } +//! } +//! +//! ``` +//! + +use libc; +use std::net::{IpAddr}; +use std::ptr::null_mut; +use std::ffi::CStr; +use std::collections::HashMap; + +pub mod if_flags; +use self::if_flags::InterfaceFlags; + +mod sockaddr; +use self::sockaddr::sockaddr_to_ipaddr; + +pub type InterfaceMap = HashMap>; + +/// 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 { + inner: *mut libc::ifaddrs, + current: Option<&'static libc::ifaddrs>, +} + +impl InterfaceAddrs { + /// Produce an `InterfaceAddrs` from the system's information. Returns `None` + /// if there are no interfaces to be inspected. + pub fn query_system() -> Option { + let mut p = null_mut(); + + // UNSAFETY: Calling libc FFI function, which allocates memory and + // fills it with info about interfaces. + 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() + return unsafe{ p.as_ref() } + .and_then(|r| Some(Self { inner: p, current: Some(r) })); + } + +} + +impl From for HashMap> { + /// Collect an `InterfaceAddrs` into a `HashMap`. + fn from(ia: InterfaceAddrs) -> HashMap> { + 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 Drop for InterfaceAddrs { + fn drop(&mut self) { + // UNSAFETY: Calling libc FFI function which frees previously allocated + // memory. + unsafe { + // Ask the libc to drop free the memory it allocated when the struct + // was created. + libc::freeifaddrs(self.inner as *mut libc::ifaddrs); + } + } +} + + +/// Represents the configuration and state of a network interface. +/// Interfaces are uniquely identified by name, and each interface is likely +/// to be referred to multiple times, e.g. one for IPv4 and one for IPv6. +#[derive(Debug, Clone)] +pub struct InterfaceAddr { + /// The name of the interface + pub name: String, + + /// The address assigned to the interface for this protocol. + /// A value of `None` means the libc reported a type of IP address that + /// `std::net` doesn't understand. + pub address: Option, + + /// The netmasks assigned to the interface for this protocol. + /// A value of `None` means the libc reported a type of IP address that + /// `std::net` doesn't understand. + pub netmask: Option, + + /// The ifu assigned to the interface for this protocol. + /// A value of `None` means the libc reported a type of IP address that + /// `std::net` doesn't understand. + pub ifu: InterfaceIfu, + + /// Flags regarding the interface's behaviour and state + pub flags: InterfaceFlags, +} + +/// Represents the ifu of an interface: either its broadcast address or +/// point-to-point destination address. +#[derive(Debug, Clone)] +pub enum InterfaceIfu { + BroadcastAddr(Option), + DestinationAddr(Option), + Neither, +} + + +impl Iterator for InterfaceAddrs { + type Item = InterfaceAddr; + fn next(&mut self) -> Option { + // 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, p 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. + 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 + // 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 = InterfaceFlags::from_bits_truncate(p.ifa_flags); + + // Get std::net::IpAddr representations of the address and netmask + // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. + let address = unsafe { sockaddr_to_ipaddr(p.ifa_addr) }; + // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. + let netmask = unsafe { sockaddr_to_ipaddr(p.ifa_netmask) }; + + // Figure out which ifu type is needed and create it + let ifu = if flags.contains(if_flags::IFF_POINTOPOINT) { + // Point to point destination address + // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. + let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; + InterfaceIfu::DestinationAddr(ifu_addr) + } else if flags.contains(if_flags::IFF_BROADCAST) { + // Broadcast address + // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. + let ifu_addr = unsafe { sockaddr_to_ipaddr(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, + }) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn tests_get_if_addrs() { + let ifs = super::InterfaceAddrs::query_system().unwrap(); + for i in ifs { + println!("{}:", i.name); + println!("\tADDR {:?}, MASK {:?}, IFU {:?}\n\t{:?}", + i.address, + i.netmask, + i.ifu, + i.flags); + } + } +} + diff --git a/src/net/ifaddrs/sockaddr.rs b/src/net/ifaddrs/sockaddr.rs new file mode 100644 index 0000000000..cf255e06f1 --- /dev/null +++ b/src/net/ifaddrs/sockaddr.rs @@ -0,0 +1,47 @@ +use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; +use std::mem::transmute; +use libc; +/// Converts a `libc::sockaddr` into an `Option`. +/// +/// It returns `None` if the libc reports a type of address that `std::net` +/// doesn't understand, or if the given `sockaddr_input` was null. +/// +/// # Unsafety +/// +/// The caller is responsible for guaranteeing that the provided reference +/// refers to valid memory. +// Allowing unsused_unsafe because I like to annotate unsafety +#[allow(unused_unsafe)] +pub unsafe fn sockaddr_to_ipaddr (sockaddr_input: *mut libc::sockaddr) -> Option { + // UNSAFETY: Deref'ing a pointer whose validity is an invariant of this function + if let Some(sa) = unsafe { sockaddr_input.as_ref() } { + // Only IPv4 and IPv6 are supported. + match sa.sa_family as i32 { + libc::AF_INET => { + // UNSAFETY: Transmuting a sockaddr into a sockaddr_in. + // They're the same thing. + let data_v4: &libc::sockaddr_in = unsafe { transmute(sa) }; + + // UNSAFETY: Transmuting a u32 into a [u8; 4] because + // the address is in network byte order. + let s_addr_v4: [u8; 4]; + unsafe { s_addr_v4 = transmute(data_v4.sin_addr.s_addr); } + Some(IpAddr::V4( + Ipv4Addr::from(s_addr_v4) + )) + }, + libc::AF_INET6 => { + let data_v6: &libc::sockaddr_in6; + // UNSAFETY: Transmuting a sockaddr into a sockaddr_in6. + // They're the same thing. + unsafe { data_v6 = transmute(sa); } + Some(IpAddr::V6( + Ipv6Addr::from(data_v6.sin6_addr.s6_addr) + )) + } + _ => None, + } + } else { + None + } +} diff --git a/src/net/mod.rs b/src/net/mod.rs index eca5cfbb7a..e5fe4ebcd9 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,3 +1,5 @@ // To avoid clashing with the keyword "if", we use "if_" as the module name. // The original header is called "net/if.h". pub mod if_; + +pub mod ifaddrs; From 4021a363972dd30f219b70289ac181bea1f662f5 Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Sat, 9 Sep 2017 11:17:22 -0500 Subject: [PATCH 02/10] Bring interface flags module into compliance with conventions --- src/net/ifaddrs/{if_flags.rs => iff_flags.rs} | 7 ++++++- src/net/ifaddrs/mod.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) rename src/net/ifaddrs/{if_flags.rs => iff_flags.rs} (87%) diff --git a/src/net/ifaddrs/if_flags.rs b/src/net/ifaddrs/iff_flags.rs similarity index 87% rename from src/net/ifaddrs/if_flags.rs rename to src/net/ifaddrs/iff_flags.rs index 0bb4cf7edd..2a69440062 100644 --- a/src/net/ifaddrs/if_flags.rs +++ b/src/net/ifaddrs/iff_flags.rs @@ -2,7 +2,7 @@ use libc; use libc::c_uint; bitflags! { - pub struct InterfaceFlags: c_uint { + pub struct IffFlags: c_uint { /// Interface is running. const IFF_UP = libc::IFF_UP as c_uint; /// Valid broadcast address set. @@ -35,10 +35,15 @@ bitflags! { const IFF_AUTOMEDIA = libc::IFF_AUTOMEDIA as c_uint; /// The addresses are lost when the interface goes down. const IFF_DYNAMIC = libc::IFF_DYNAMIC as c_uint; + + // These flags are available on modern Linuxes + #[cfg(any(target_os = "linux", target_os = "android"))] /// Driver signals L1 up (since Linux 2.6.17) const IFF_LOWER_UP = 1<<16; + #[cfg(any(target_os = "linux", target_os = "android"))] /// Driver signals dormant (since Linux 2.6.17) const IFF_DORMANT = 1<<17; + #[cfg(any(target_os = "linux", target_os = "android"))] /// Echo sent packets (since Linux 2.6.25) const IFF_ECHO = 1<<18; } diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index cda1264730..01e73f7d8f 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -21,18 +21,18 @@ //! } //! ``` //! -//! The `InterfaceFlags` struct provides access to info about the +//! 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, if_flags}; +//! use nix::net::ifaddrs::{InterfaceAddrs, iff_flags}; //! //! let addrs = InterfaceAddrs::query_system() //! .expect("System has no network interfaces."); //! //! for addr in addrs { -//! if addr.flags.contains(if_flags::IFF_UP) { +//! if addr.flags.contains(iff_flags::IFF_UP) { //! println!("{}: {:?}", addr.name, addr.address); //! } //! } @@ -68,8 +68,8 @@ use std::ptr::null_mut; use std::ffi::CStr; use std::collections::HashMap; -pub mod if_flags; -use self::if_flags::InterfaceFlags; +pub mod iff_flags; +use self::iff_flags::IffFlags; mod sockaddr; use self::sockaddr::sockaddr_to_ipaddr; @@ -155,7 +155,7 @@ pub struct InterfaceAddr { pub ifu: InterfaceIfu, /// Flags regarding the interface's behaviour and state - pub flags: InterfaceFlags, + pub flags: IffFlags, } /// Represents the ifu of an interface: either its broadcast address or @@ -196,7 +196,7 @@ impl Iterator for InterfaceAddrs { .into_owned()}; // Interpret the flags field into a typed version of those flags - let flags = InterfaceFlags::from_bits_truncate(p.ifa_flags); + let flags = IffFlags::from_bits_truncate(p.ifa_flags); // Get std::net::IpAddr representations of the address and netmask // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. @@ -205,12 +205,12 @@ impl Iterator for InterfaceAddrs { let netmask = unsafe { sockaddr_to_ipaddr(p.ifa_netmask) }; // Figure out which ifu type is needed and create it - let ifu = if flags.contains(if_flags::IFF_POINTOPOINT) { + let ifu = if flags.contains(iff_flags::IFF_POINTOPOINT) { // Point to point destination address // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; InterfaceIfu::DestinationAddr(ifu_addr) - } else if flags.contains(if_flags::IFF_BROADCAST) { + } else if flags.contains(iff_flags::IFF_BROADCAST) { // Broadcast address // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; From 71e508e488c5baabdc8cefe0a36deb7320113d1d Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Sat, 9 Sep 2017 15:24:16 -0500 Subject: [PATCH 03/10] Clean up ifaddrs in accordance with review --- src/net/ifaddrs/iff_flags.rs | 1 - src/net/ifaddrs/mod.rs | 109 ++++++++++++++++------------------- src/net/ifaddrs/sockaddr.rs | 36 +++++------- 3 files changed, 63 insertions(+), 83 deletions(-) diff --git a/src/net/ifaddrs/iff_flags.rs b/src/net/ifaddrs/iff_flags.rs index 2a69440062..bdc5959de0 100644 --- a/src/net/ifaddrs/iff_flags.rs +++ b/src/net/ifaddrs/iff_flags.rs @@ -48,4 +48,3 @@ bitflags! { const IFF_ECHO = 1<<18; } } - diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index 01e73f7d8f..76115834ac 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -1,8 +1,8 @@ //! `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>`) for +//! 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>`) for //! more convenient access by interface name. //! //! # Examples @@ -13,7 +13,7 @@ //! ``` //! use nix::net::ifaddrs::InterfaceAddrs; //! -//! let addrs = InterfaceAddrs::query_system() +//! let addrs = InterfaceAddrs::getifaddrs() //! .expect("System has no network interfaces."); //! //! for addr in addrs { @@ -21,14 +21,14 @@ //! } //! ``` //! -//! The `IffFlags` struct provides access to info about the +//! 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::query_system() +//! let addrs = InterfaceAddrs::getifaddrs() //! .expect("System has no network interfaces."); //! //! for addr in addrs { @@ -46,8 +46,8 @@ //! use nix::net::ifaddrs::{InterfaceAddrs, InterfaceAddr, InterfaceMap}; //! use std::collections::HashMap; //! -//! let interfaces: InterfaceMap = -//! InterfaceAddrs::query_system() +//! let interfaces: InterfaceMap = +//! InterfaceAddrs::getifaddrs() //! .expect("System has no network interfaces.") //! .into(); // Convert to a hash map //! @@ -58,12 +58,12 @@ //! println!("\t{:?}", addr); //! } //! } -//! +//! //! ``` //! use libc; -use std::net::{IpAddr}; +use std::net::IpAddr; use std::ptr::null_mut; use std::ffi::CStr; use std::collections::HashMap; @@ -76,8 +76,8 @@ use self::sockaddr::sockaddr_to_ipaddr; pub type InterfaceMap = HashMap>; -/// Represents a handle into the operating system's knowledge about network -/// interfaces present on the system. Allows the user to iterate over +/// 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 { inner: *mut libc::ifaddrs, @@ -87,19 +87,22 @@ pub struct InterfaceAddrs { impl InterfaceAddrs { /// Produce an `InterfaceAddrs` from the system's information. Returns `None` /// if there are no interfaces to be inspected. - pub fn query_system() -> Option { - let mut p = null_mut(); - - // UNSAFETY: Calling libc FFI function, which allocates memory and - // fills it with info about interfaces. - 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() - return unsafe{ p.as_ref() } - .and_then(|r| Some(Self { inner: p, current: Some(r) })); - } + pub fn getifaddrs() -> Option { + 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() + return unsafe { p.as_ref() }.and_then(|r| { + Some(Self { + inner: p, + current: Some(r), + }) + }); + } } impl From for HashMap> { @@ -108,10 +111,10 @@ impl From for HashMap> { let mut m = HashMap::new(); for i in ia { if !m.contains_key(&i.name) { - m.insert(i.name.clone(), Vec::new()); + m.insert(i.name.clone(), Vec::new()); } // Unwrap here because contains is checked above - m.get_mut(&i.name).unwrap().push(i); + m.get_mut(&i.name).unwrap().push(i); } m @@ -140,18 +143,20 @@ pub struct InterfaceAddr { pub name: String, /// The address assigned to the interface for this protocol. - /// A value of `None` means the libc reported a type of IP address that + /// A value of `None` means the libc reported a type of address that /// `std::net` doesn't understand. pub address: Option, - /// The netmasks assigned to the interface for this protocol. - /// A value of `None` means the libc reported a type of IP address that + /// 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, - /// The ifu assigned to the interface for this protocol. - /// A value of `None` means the libc reported a type of IP address that - /// `std::net` doesn't understand. + /// 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, /// Flags regarding the interface's behaviour and state @@ -159,7 +164,7 @@ pub struct InterfaceAddr { } /// Represents the ifu of an interface: either its broadcast address or -/// point-to-point destination address. +/// point-to-point destination address. #[derive(Debug, Clone)] pub enum InterfaceIfu { BroadcastAddr(Option), @@ -177,8 +182,9 @@ impl Iterator for InterfaceAddrs { } // Workaround for the borrow checker being overzealous - // (without ptr_temp, p would technically "still be in use" when the - // loop ends, meaning we couldn't advance to the next struct) + // (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(); @@ -188,16 +194,16 @@ impl Iterator for InterfaceAddrs { 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 // null it's a libc bug; it's checked above. - let name = unsafe { CStr::from_ptr(name_ptr) + let name = unsafe { CStr::from_ptr(name_ptr) } .to_string_lossy() - .into_owned()}; + .into_owned(); // Interpret the flags field into a typed version of those flags let flags = IffFlags::from_bits_truncate(p.ifa_flags); - + // Get std::net::IpAddr representations of the address and netmask // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let address = unsafe { sockaddr_to_ipaddr(p.ifa_addr) }; @@ -215,13 +221,15 @@ impl Iterator for InterfaceAddrs { // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; InterfaceIfu::BroadcastAddr(ifu_addr) - } else { InterfaceIfu::Neither }; + } else { + InterfaceIfu::Neither + }; // Move along the list to the next ifaddrs struct - // UNSAFETY: *mut -> Option<&'static mut>. + // 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, @@ -231,20 +239,3 @@ impl Iterator for InterfaceAddrs { }) } } - -#[cfg(test)] -mod tests { - #[test] - fn tests_get_if_addrs() { - let ifs = super::InterfaceAddrs::query_system().unwrap(); - for i in ifs { - println!("{}:", i.name); - println!("\tADDR {:?}, MASK {:?}, IFU {:?}\n\t{:?}", - i.address, - i.netmask, - i.ifu, - i.flags); - } - } -} - diff --git a/src/net/ifaddrs/sockaddr.rs b/src/net/ifaddrs/sockaddr.rs index cf255e06f1..dbfdb94219 100644 --- a/src/net/ifaddrs/sockaddr.rs +++ b/src/net/ifaddrs/sockaddr.rs @@ -1,43 +1,33 @@ use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; use std::mem::transmute; use libc; -/// Converts a `libc::sockaddr` into an `Option`. -/// -/// It returns `None` if the libc reports a type of address that `std::net` +/// Converts a `libc::sockaddr` into an `Option`. +/// +/// It returns `None` if the libc reports a type of address that `std::net` /// doesn't understand, or if the given `sockaddr_input` was null. /// /// # Unsafety /// /// The caller is responsible for guaranteeing that the provided reference /// refers to valid memory. -// Allowing unsused_unsafe because I like to annotate unsafety -#[allow(unused_unsafe)] -pub unsafe fn sockaddr_to_ipaddr (sockaddr_input: *mut libc::sockaddr) -> Option { - // UNSAFETY: Deref'ing a pointer whose validity is an invariant of this function - if let Some(sa) = unsafe { sockaddr_input.as_ref() } { +pub unsafe fn sockaddr_to_ipaddr(sockaddr_input: *mut libc::sockaddr) -> Option { + if let Some(sa) = sockaddr_input.as_ref() { // Only IPv4 and IPv6 are supported. match sa.sa_family as i32 { libc::AF_INET => { - // UNSAFETY: Transmuting a sockaddr into a sockaddr_in. - // They're the same thing. - let data_v4: &libc::sockaddr_in = unsafe { transmute(sa) }; + let data_v4: &libc::sockaddr_in = transmute(sa); - // UNSAFETY: Transmuting a u32 into a [u8; 4] because + // Transmuting a u32 into a [u8; 4] because // the address is in network byte order. - let s_addr_v4: [u8; 4]; - unsafe { s_addr_v4 = transmute(data_v4.sin_addr.s_addr); } - Some(IpAddr::V4( - Ipv4Addr::from(s_addr_v4) - )) - }, + let s_addr_v4: [u8; 4] = transmute(data_v4.sin_addr.s_addr); + Some(IpAddr::V4(Ipv4Addr::from(s_addr_v4))) + } libc::AF_INET6 => { let data_v6: &libc::sockaddr_in6; - // UNSAFETY: Transmuting a sockaddr into a sockaddr_in6. + // UNSAFETY: Transmuting a sockaddr into a sockaddr_in6. // They're the same thing. - unsafe { data_v6 = transmute(sa); } - Some(IpAddr::V6( - Ipv6Addr::from(data_v6.sin6_addr.s6_addr) - )) + data_v6 = transmute(sa); + Some(IpAddr::V6(Ipv6Addr::from(data_v6.sin6_addr.s6_addr))) } _ => None, } From 5457cca6ad53e7681f3d4bb0dc8a496fc4f97c6c Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Sat, 9 Sep 2017 15:34:54 -0500 Subject: [PATCH 04/10] Move ifaddrs to error handling with nix::Result --- src/net/ifaddrs/mod.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index 76115834ac..e5a33fd0bf 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -14,7 +14,7 @@ //! use nix::net::ifaddrs::InterfaceAddrs; //! //! let addrs = InterfaceAddrs::getifaddrs() -//! .expect("System has no network interfaces."); +//! .expect("Failed to enumerate network interfaces."); //! //! for addr in addrs { //! println!("{}: {:?}", addr.name, addr.address); @@ -29,7 +29,7 @@ //! use nix::net::ifaddrs::{InterfaceAddrs, iff_flags}; //! //! let addrs = InterfaceAddrs::getifaddrs() -//! .expect("System has no network interfaces."); +//! .expect("Failed to eunmerate network interfaces."); //! //! for addr in addrs { //! if addr.flags.contains(iff_flags::IFF_UP) { @@ -48,7 +48,7 @@ //! //! let interfaces: InterfaceMap = //! InterfaceAddrs::getifaddrs() -//! .expect("System has no network interfaces.") +//! .expect("Failed to enumerate network interfaces.") //! .into(); // Convert to a hash map //! //! // Print all the addresses of the loopback interface @@ -67,6 +67,9 @@ use std::net::IpAddr; 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; @@ -85,23 +88,24 @@ pub struct InterfaceAddrs { } impl InterfaceAddrs { - /// Produce an `InterfaceAddrs` from the system's information. Returns `None` - /// if there are no interfaces to be inspected. - pub fn getifaddrs() -> Option { + /// Produce an `InterfaceAddrs` from the system's information. + pub fn getifaddrs() -> Result { 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() - return unsafe { p.as_ref() }.and_then(|r| { - Some(Self { + // 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 { inner: p, current: Some(r), - }) - }); + }), + + None => Err(Error::from(Errno::from_i32(errno()))), + } } } From 87b93a1c98ba0b89c60e182a76d9750b270e7386 Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Sun, 10 Sep 2017 11:02:14 -0500 Subject: [PATCH 05/10] Add support for AF_PACKET (link-level) addresses in net::ifaddrs --- Cargo.toml | 1 + src/net/ifaddrs/mod.rs | 29 ++++++++++---------- src/net/ifaddrs/sockaddr.rs | 53 +++++++++++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 396a64a403..a59028d8f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ libc = { git = "https://github.com/rust-lang/libc" } bitflags = "0.9" cfg-if = "0.1.0" void = "1.0.2" +eui48 = "0.3" [dev-dependencies] lazy_static = "0.2" diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index e5a33fd0bf..f0481c9a2d 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -63,7 +63,6 @@ //! use libc; -use std::net::IpAddr; use std::ptr::null_mut; use std::ffi::CStr; use std::collections::HashMap; @@ -75,7 +74,7 @@ pub mod iff_flags; use self::iff_flags::IffFlags; mod sockaddr; -use self::sockaddr::sockaddr_to_ipaddr; +use self::sockaddr::{IfAddrValue, sockaddr_to_ifaddrvalue}; pub type InterfaceMap = HashMap>; @@ -149,12 +148,12 @@ pub struct InterfaceAddr { /// 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, + pub address: Option, /// 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, + pub netmask: Option, /// The ifu assigned to the interface for this protocol. /// A value of `{Broadcast, Destination}Addr(None)` means the libc reported @@ -171,8 +170,8 @@ pub struct InterfaceAddr { /// point-to-point destination address. #[derive(Debug, Clone)] pub enum InterfaceIfu { - BroadcastAddr(Option), - DestinationAddr(Option), + BroadcastAddr(Option), + DestinationAddr(Option), Neither, } @@ -208,22 +207,22 @@ impl Iterator for InterfaceAddrs { // Interpret the flags field into a typed version of those flags let flags = IffFlags::from_bits_truncate(p.ifa_flags); - // Get std::net::IpAddr representations of the address and netmask - // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. - let address = unsafe { sockaddr_to_ipaddr(p.ifa_addr) }; - // UNSAFETY: sockaddr_to_ipaddr requires valid pointer. - let netmask = unsafe { sockaddr_to_ipaddr(p.ifa_netmask) }; + // 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_ipaddr requires valid pointer. - let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; + // 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_ipaddr requires valid pointer. - let ifu_addr = unsafe { sockaddr_to_ipaddr(p.ifa_ifu) }; + // UNSAFETY: sockaddr_to_ifaddrvalue requires valid pointer. + let ifu_addr = unsafe { sockaddr_to_ifaddrvalue(p.ifa_ifu) }; InterfaceIfu::BroadcastAddr(ifu_addr) } else { InterfaceIfu::Neither diff --git a/src/net/ifaddrs/sockaddr.rs b/src/net/ifaddrs/sockaddr.rs index dbfdb94219..26f52903d9 100644 --- a/src/net/ifaddrs/sockaddr.rs +++ b/src/net/ifaddrs/sockaddr.rs @@ -1,33 +1,64 @@ use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; use std::mem::transmute; use libc; -/// Converts a `libc::sockaddr` into an `Option`. + +extern crate eui48; +use self::eui48::{MacAddress, Eui48}; + +/// Represents the actual data of an address in use by an interface. +#[derive(Debug, Clone, PartialEq)] +pub enum IfAddrValue { + IpAddr(IpAddr), + MacAddr(MacAddress), +} + +impl From for IfAddrValue { + fn from(ip: IpAddr) -> IfAddrValue { + IfAddrValue::IpAddr(ip) + } +} + +impl From for IfAddrValue { + fn from(mac: MacAddress) -> IfAddrValue { + IfAddrValue::MacAddr(mac) + } +} + +/// Converts a `libc::sockaddr` into an `Option`. /// -/// It returns `None` if the libc reports a type of address that `std::net` -/// doesn't understand, or if the given `sockaddr_input` was null. +/// It returns `None` if the libc reports a type of address other than +/// IPv4, IPv6, or EUI-84 MAC, 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_ipaddr(sockaddr_input: *mut libc::sockaddr) -> Option { +pub unsafe fn sockaddr_to_ifaddrvalue(sockaddr_input: *mut libc::sockaddr) + -> Option { if let Some(sa) = sockaddr_input.as_ref() { // Only IPv4 and IPv6 are supported. 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 // 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))) + Some(IpAddr::V4(Ipv4Addr::from(s_addr_v4)).into()) } libc::AF_INET6 => { - let data_v6: &libc::sockaddr_in6; - // UNSAFETY: Transmuting a sockaddr into a sockaddr_in6. - // They're the same thing. - data_v6 = transmute(sa); - Some(IpAddr::V6(Ipv6Addr::from(data_v6.sin6_addr.s6_addr))) + let data_v6: &libc::sockaddr_in6 = transmute(sa); + Some(IpAddr::V6(Ipv6Addr::from(data_v6.sin6_addr.s6_addr)).into()) + } + libc::AF_PACKET => { + let data_mac: &libc::sockaddr_ll = transmute(sa); + if data_mac.sll_halen != 6 { + None // If the length of the hardware address (halen) isn't + // 6, it's not EUI48 and we can't handle it. + } else { + let a = data_mac.sll_addr; + let eui: Eui48 = [a[0], a[1], a[2], a[3], a[4], a[5]]; + Some(MacAddress::new(eui).into()) + } } _ => None, } From 9dc2fb04f66c30e0fcf267f67d1dd59c71df2685 Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Tue, 12 Sep 2017 09:59:23 -0500 Subject: [PATCH 06/10] Return a reference to unrepresentable sockaddrs types instead of swallowing them. --- Cargo.toml | 1 - src/net/ifaddrs/mod.rs | 34 ++++++++++++------------- src/net/ifaddrs/sockaddr.rs | 50 ++++++++++++++++++------------------- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a59028d8f2..396a64a403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ libc = { git = "https://github.com/rust-lang/libc" } bitflags = "0.9" cfg-if = "0.1.0" void = "1.0.2" -eui48 = "0.3" [dev-dependencies] lazy_static = "0.2" diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index f0481c9a2d..ba8dc06158 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -76,17 +76,17 @@ use self::iff_flags::IffFlags; mod sockaddr; use self::sockaddr::{IfAddrValue, sockaddr_to_ifaddrvalue}; -pub type InterfaceMap = HashMap>; +pub type InterfaceMap<'a> = HashMap>>; /// 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 { +pub struct InterfaceAddrs<'a> { inner: *mut libc::ifaddrs, - current: Option<&'static libc::ifaddrs>, + current: Option<&'a libc::ifaddrs>, } -impl InterfaceAddrs { +impl<'a> InterfaceAddrs<'a> { /// Produce an `InterfaceAddrs` from the system's information. pub fn getifaddrs() -> Result { let mut p = null_mut(); @@ -108,9 +108,9 @@ impl InterfaceAddrs { } } -impl From for HashMap> { +impl<'a> From> for HashMap>> { /// Collect an `InterfaceAddrs` into a `HashMap`. - fn from(ia: InterfaceAddrs) -> HashMap> { + fn from(ia: InterfaceAddrs<'a>) -> HashMap>> { let mut m = HashMap::new(); for i in ia { if !m.contains_key(&i.name) { @@ -124,7 +124,7 @@ impl From for HashMap> { } } -impl Drop for InterfaceAddrs { +impl<'a> Drop for InterfaceAddrs<'a> { fn drop(&mut self) { // UNSAFETY: Calling libc FFI function which frees previously allocated // memory. @@ -141,26 +141,26 @@ impl Drop for InterfaceAddrs { /// Interfaces are uniquely identified by name, and each interface is likely /// to be referred to multiple times, e.g. one for IPv4 and one for IPv6. #[derive(Debug, Clone)] -pub struct InterfaceAddr { +pub struct InterfaceAddr<'a> { /// The name of the interface pub name: String, /// 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, + pub address: Option>, /// 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, + pub netmask: Option>, /// 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, + pub ifu: InterfaceIfu<'a>, /// Flags regarding the interface's behaviour and state pub flags: IffFlags, @@ -169,16 +169,16 @@ pub struct InterfaceAddr { /// Represents the ifu of an interface: either its broadcast address or /// point-to-point destination address. #[derive(Debug, Clone)] -pub enum InterfaceIfu { - BroadcastAddr(Option), - DestinationAddr(Option), +pub enum InterfaceIfu<'a> { + BroadcastAddr(Option>), + DestinationAddr(Option>), Neither, } -impl Iterator for InterfaceAddrs { - type Item = InterfaceAddr; - fn next(&mut self) -> Option { +impl<'a> Iterator for InterfaceAddrs<'a> { + type Item = InterfaceAddr<'a>; + fn next(&mut self) -> Option> { // If the current ifaddrs is None, there are no more ifaddrs to inspect if self.current.is_none() { return None; diff --git a/src/net/ifaddrs/sockaddr.rs b/src/net/ifaddrs/sockaddr.rs index 26f52903d9..bfa6291429 100644 --- a/src/net/ifaddrs/sockaddr.rs +++ b/src/net/ifaddrs/sockaddr.rs @@ -1,40 +1,47 @@ use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; use std::mem::transmute; +use std::fmt; use libc; -extern crate eui48; -use self::eui48::{MacAddress, Eui48}; - /// Represents the actual data of an address in use by an interface. -#[derive(Debug, Clone, PartialEq)] -pub enum IfAddrValue { +#[derive(Clone)] +pub enum IfAddrValue<'a> { IpAddr(IpAddr), - MacAddr(MacAddress), + 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()"), + } + } } -impl From for IfAddrValue { - fn from(ip: IpAddr) -> IfAddrValue { +impl<'a> From for IfAddrValue<'a> { + fn from(ip: IpAddr) -> IfAddrValue<'a> { IfAddrValue::IpAddr(ip) } } -impl From for IfAddrValue { - fn from(mac: MacAddress) -> IfAddrValue { - IfAddrValue::MacAddr(mac) +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`. /// /// It returns `None` if the libc reports a type of address other than -/// IPv4, IPv6, or EUI-84 MAC, or if the given `sockaddr_input` was null. +/// 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(sockaddr_input: *mut libc::sockaddr) - -> Option { +pub unsafe fn sockaddr_to_ifaddrvalue<'a>(sockaddr_input: *mut libc::sockaddr) + -> Option> { if let Some(sa) = sockaddr_input.as_ref() { // Only IPv4 and IPv6 are supported. match sa.sa_family as i32 { @@ -49,18 +56,9 @@ pub unsafe fn sockaddr_to_ifaddrvalue(sockaddr_input: *mut libc::sockaddr) let data_v6: &libc::sockaddr_in6 = transmute(sa); Some(IpAddr::V6(Ipv6Addr::from(data_v6.sin6_addr.s6_addr)).into()) } - libc::AF_PACKET => { - let data_mac: &libc::sockaddr_ll = transmute(sa); - if data_mac.sll_halen != 6 { - None // If the length of the hardware address (halen) isn't - // 6, it's not EUI48 and we can't handle it. - } else { - let a = data_mac.sll_addr; - let eui: Eui48 = [a[0], a[1], a[2], a[3], a[4], a[5]]; - Some(MacAddress::new(eui).into()) - } - } - _ => None, + _ => { + Some(sa.into()) + }, } } else { None From ee5d4b582831fa011f17a025e28f5486dee7b60d Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Tue, 12 Sep 2017 11:54:04 -0500 Subject: [PATCH 07/10] Add test for ifaddrs --- src/net/ifaddrs/mod.rs | 41 +++++++++++++++++--- src/net/ifaddrs/sockaddr.rs | 9 ++--- src/net/ifaddrs/tests.rs | 76 +++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 src/net/ifaddrs/tests.rs diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index ba8dc06158..9ebce2ce3e 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -76,6 +76,8 @@ use self::iff_flags::IffFlags; mod sockaddr; use self::sockaddr::{IfAddrValue, sockaddr_to_ifaddrvalue}; +mod tests; + pub type InterfaceMap<'a> = HashMap>>; /// Represents a handle into the operating system's knowledge about network @@ -84,9 +86,33 @@ pub type InterfaceMap<'a> = HashMap>>; pub struct InterfaceAddrs<'a> { inner: *mut libc::ifaddrs, current: Option<&'a libc::ifaddrs>, + do_free: bool, } impl<'a> InterfaceAddrs<'a> { + /// Creates an `InterfaceAddrs` from a raw pointer, without calling into + /// the `libc`. + /// + /// 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 { + match p.as_ref() { + Some(r) => Ok(Self { + inner: p, + current: Some(r), + do_free: false, + }), + None => Err(()), + } + } + /// Produce an `InterfaceAddrs` from the system's information. pub fn getifaddrs() -> Result { let mut p = null_mut(); @@ -101,6 +127,7 @@ impl<'a> InterfaceAddrs<'a> { Some(r) => Ok(Self { inner: p, current: Some(r), + do_free: true, }), None => Err(Error::from(Errno::from_i32(errno()))), @@ -126,12 +153,14 @@ impl<'a> From> for HashMap>> { impl<'a> Drop for InterfaceAddrs<'a> { fn drop(&mut self) { - // UNSAFETY: Calling libc FFI function which frees previously allocated - // memory. - unsafe { - // Ask the libc to drop free the memory it allocated when the struct - // was created. - libc::freeifaddrs(self.inner as *mut libc::ifaddrs); + 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 + // the struct was created. + libc::freeifaddrs(self.inner as *mut libc::ifaddrs); + } } } } diff --git a/src/net/ifaddrs/sockaddr.rs b/src/net/ifaddrs/sockaddr.rs index bfa6291429..48bf18d711 100644 --- a/src/net/ifaddrs/sockaddr.rs +++ b/src/net/ifaddrs/sockaddr.rs @@ -40,8 +40,9 @@ impl<'a> From<&'a libc::sockaddr> for IfAddrValue<'a> { /// /// 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> { +pub unsafe fn sockaddr_to_ifaddrvalue<'a>( + sockaddr_input: *mut libc::sockaddr, +) -> Option> { if let Some(sa) = sockaddr_input.as_ref() { // Only IPv4 and IPv6 are supported. match sa.sa_family as i32 { @@ -56,9 +57,7 @@ pub unsafe fn sockaddr_to_ifaddrvalue<'a>(sockaddr_input: *mut libc::sockaddr) let data_v6: &libc::sockaddr_in6 = transmute(sa); Some(IpAddr::V6(Ipv6Addr::from(data_v6.sin6_addr.s6_addr)).into()) } - _ => { - Some(sa.into()) - }, + _ => Some(sa.into()), } } else { None diff --git a/src/net/ifaddrs/tests.rs b/src/net/ifaddrs/tests.rs new file mode 100644 index 0000000000..6ac4b6d2e8 --- /dev/null +++ b/src/net/ifaddrs/tests.rs @@ -0,0 +1,76 @@ +#![cfg(test)] + +use std::ptr::null_mut; +use std::mem::transmute; + +use std::ffi::CString; + +use libc; +use libc::{ifaddrs, sockaddr, sockaddr_in}; +use libc::in_addr; + +use super::iff_flags::*; +use super::InterfaceAddrs; +use super::InterfaceMap; + +// Utility function to turn some bytes into a u32 in the right order because +// endianness is a pain +fn convert_v4_addr(b0: u8, b1: u8, b2: u8, b3: u8) -> in_addr { + unsafe { in_addr { s_addr: transmute([b0, b1, b2, b3]) } } +} + +// Utility function to turn some bytes into a sockaddr with the v4 addr type +fn sockaddr_for_addr(b0: u8, b1: u8, b2: u8, b3: u8) -> sockaddr { + let sin = sockaddr_in { + sin_family: libc::AF_INET as u16, + sin_port: 0, + sin_addr: convert_v4_addr(b0, b1, b2, b3), + sin_zero: [0; 8], + }; + + unsafe { transmute(sin) } +} + +#[test] +fn test_ifaddrs() { + + // Create an "external" interface + let mut ext_address = sockaddr_for_addr(192, 168, 0, 1); + let mut ext_netmask = sockaddr_for_addr(255, 255, 255, 0); + let mut ext_brdcast = sockaddr_for_addr(192, 168, 0, 255); + + let mut test_ext_ipv4 = ifaddrs { + ifa_next: null_mut(), + ifa_name: CString::new("test_ext").unwrap().into_raw(), + ifa_flags: (IFF_BROADCAST | IFF_UP | IFF_RUNNING).bits(), + ifa_addr: &mut ext_address, + ifa_netmask: &mut ext_netmask, + ifa_ifu: &mut ext_brdcast, + ifa_data: null_mut(), + }; + + + // Create a "loopback" interface, and link it to the "external" one + let mut lo_address = sockaddr_for_addr(127, 0, 0, 1); + let mut lo_netmask = sockaddr_for_addr(255, 255, 255, 0); + let mut lo_brdcast = sockaddr_for_addr(127, 0, 0, 255); + + let mut test_lo_ipv4 = ifaddrs { + ifa_next: &mut test_ext_ipv4, + ifa_name: CString::new("test_lo").unwrap().into_raw(), + ifa_flags: (IFF_BROADCAST | IFF_LOOPBACK | IFF_UP | IFF_RUNNING).bits(), + ifa_addr: &mut lo_address, + ifa_netmask: &mut lo_netmask, + ifa_ifu: &mut lo_brdcast, + ifa_data: null_mut(), + }; + + let created = unsafe { InterfaceAddrs::from_raw(&mut test_lo_ipv4) }.unwrap(); + + let hm: InterfaceMap = created.into(); + + assert_eq!(hm.len(), 2, "Expected 2 interfaces, found {}.", hm.len()); + + assert!(hm.contains_key("test_lo")); + assert!(hm.contains_key("test_ext")); +} From 0751bda12a7796484623d120d491cc3b91718d16 Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Tue, 12 Sep 2017 12:29:03 -0500 Subject: [PATCH 08/10] Add changelog entry for getifaddrs --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8be1905cc..eae0809019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Added socket option variant that enables the timestamp socket control message: `nix::sys::socket::sockopt::ReceiveTimestamp` ([#663](https://github.com/nix-rust/nix/pull/663)) +- Added API for `getifaddrs` with associated wrapper type. + ([#764](https://github.com/nix-rust/nix/pull/764)) ### Changed - Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692)) From fc4a0f2f22c6cccfcfa4c8082459ae6271e355dd Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Tue, 12 Sep 2017 15:36:11 -0500 Subject: [PATCH 09/10] Convert iff_flags to using libc_bitflags macro --- src/net/ifaddrs/iff_flags.rs | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/net/ifaddrs/iff_flags.rs b/src/net/ifaddrs/iff_flags.rs index bdc5959de0..90622cc98b 100644 --- a/src/net/ifaddrs/iff_flags.rs +++ b/src/net/ifaddrs/iff_flags.rs @@ -1,50 +1,50 @@ use libc; use libc::c_uint; -bitflags! { +libc_bitflags! { pub struct IffFlags: c_uint { /// Interface is running. - const IFF_UP = libc::IFF_UP as c_uint; + IFF_UP as c_uint; /// Valid broadcast address set. - const IFF_BROADCAST = libc::IFF_BROADCAST as c_uint; + IFF_BROADCAST as c_uint; /// Internal debugging flag. - const IFF_DEBUG = libc::IFF_DEBUG as c_uint; + IFF_DEBUG as c_uint; /// Interface is a loopback interface. - const IFF_LOOPBACK = libc::IFF_LOOPBACK as c_uint; + IFF_LOOPBACK as c_uint; /// Interface is a point-to-point link. - const IFF_POINTOPOINT = libc::IFF_POINTOPOINT as c_uint; + IFF_POINTOPOINT as c_uint; /// Resources allocated. - const IFF_NOTRAILERS = libc::IFF_NOTRAILERS as c_uint; + IFF_NOTRAILERS as c_uint; /// No arp protocol, L2 destination address not set. - const IFF_RUNNING = libc::IFF_RUNNING as c_uint; + IFF_RUNNING as c_uint; /// Interface is in promiscuous mode. - const IFF_NOARP = libc::IFF_NOARP as c_uint; + IFF_NOARP as c_uint; /// Avoid use of trailers. - const IFF_PROMISC = libc::IFF_PROMISC as c_uint; + IFF_PROMISC as c_uint; /// Receive all multicast packets. - const IFF_ALLMULTI = libc::IFF_ALLMULTI as c_uint; + IFF_ALLMULTI as c_uint; /// Master of a load balancing bundle. - const IFF_MASTER = libc::IFF_MASTER as c_uint; + IFF_MASTER as c_uint; /// Slave of a load balancing bundle. - const IFF_SLAVE = libc::IFF_SLAVE as c_uint; + IFF_SLAVE as c_uint; /// Supports multicast - const IFF_MULTICAST = libc::IFF_MULTICAST as c_uint; + IFF_MULTICAST as c_uint; /// Is able to select media type via ifmap. - const IFF_PORTSEL = libc::IFF_PORTSEL as c_uint; + IFF_PORTSEL as c_uint; /// Auto media selection active. - const IFF_AUTOMEDIA = libc::IFF_AUTOMEDIA as c_uint; + IFF_AUTOMEDIA as c_uint; /// The addresses are lost when the interface goes down. - const IFF_DYNAMIC = libc::IFF_DYNAMIC as c_uint; + IFF_DYNAMIC as c_uint; // These flags are available on modern Linuxes #[cfg(any(target_os = "linux", target_os = "android"))] /// Driver signals L1 up (since Linux 2.6.17) - const IFF_LOWER_UP = 1<<16; + IFF_LOWER_UP as c_uint; #[cfg(any(target_os = "linux", target_os = "android"))] /// Driver signals dormant (since Linux 2.6.17) - const IFF_DORMANT = 1<<17; + IFF_DORMANT as c_uint; #[cfg(any(target_os = "linux", target_os = "android"))] /// Echo sent packets (since Linux 2.6.25) - const IFF_ECHO = 1<<18; + IFF_ECHO as c_uint; } } From c8cb083cad3f52bb7fc8e62c0990b3c62de55fff Mon Sep 17 00:00:00 2001 From: SilverWingedSeraph Date: Tue, 12 Sep 2017 19:30:35 -0500 Subject: [PATCH 10/10] Fix incompatible use of Self --- src/net/ifaddrs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/ifaddrs/mod.rs b/src/net/ifaddrs/mod.rs index 9ebce2ce3e..01613934ce 100644 --- a/src/net/ifaddrs/mod.rs +++ b/src/net/ifaddrs/mod.rs @@ -102,7 +102,7 @@ impl<'a> InterfaceAddrs<'a> { /// /// # Errors /// `Err(())` will be returned if `p` was void. - pub unsafe fn from_raw(p: *mut libc::ifaddrs) -> ::std::result::Result { + pub unsafe fn from_raw(p: *mut libc::ifaddrs) -> ::std::result::Result, ()> { match p.as_ref() { Some(r) => Ok(Self { inner: p,