Skip to content
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 support for getifaddrs. #667

Merged
merged 1 commit into from
Dec 4, 2017
Merged

Conversation

mwanner
Copy link
Contributor

@mwanner mwanner commented Jul 10, 2017

Here's a first attempt at a rustian interface for getifaddrs, please review.

Changes for the changelog:

  • Added nix::ifaddrs::{getifaddrs, InterfaceAddressIterator, InterfaceAddress and InterfaceFlags}

Closes #650.
Closes #764.

@mwanner
Copy link
Contributor Author

mwanner commented Jul 10, 2017

One area I'm not quite sure about is the use of the nix specific InetAddr struct. Due to it supporting only IPv4 and IPv6, but not AF_PACKET, for example, lots of entries returned from getifaddrs remain empty.

This works for me, ATM. However, I think nix should better provide some way to represent those entries as well.

src/ifaddrs.rs Outdated

use sys::socket::InetAddr;

#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "fuchsia"))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to use a module here. Please use a single libc_bitflags instance and annotate each entry with a cfg attribute with their platform support.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think Android supports all these as well (you can find this out easily by searching the libc repo).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Android doesn't support getifaddrs, that's probably why I excluded it. But you're right, the flags themselves are known to Android as well.

Maybe I should move InterfaceFlags to net/if_.rs, as those constants are defined in /usr/include/net/if.h and not in ifaddrs.h.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Android does, but libc does need to have it added.

I think it's fine having them here for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and: I used the module to reduce the amount of #[cfg()] conditions. I only used the module as I didn't find any good way to write this without repeating the conditions many times. I'm fine if you prefer the latter and changed things accordingly.

src/ifaddrs.rs Outdated
mod flags {
use libc;
libc_bitflags!(
/// Flags for `InterfaceAddress` entries returned by `getifaddrs`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That comment doesn't really provide any detail. Can you summarize what these values actually refer to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I duplicated the comment from the C header, now.

src/ifaddrs.rs Outdated

/// Describes a single address for an interface as returned by `getifaddrs`.
pub struct InterfaceAddress {
/// name of the interface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please capitalize the first character of all of these comments

src/ifaddrs.rs Outdated

/// Get interface addresses using libc's `getifaddrs`
///
/// Note that the underlying implementation differs between implementations and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the underlying implementation differs between implementations", that's pretty redundant. Can you rephrase this to be a little more clear?

src/ifaddrs.rs Outdated
/// }
/// }
/// ```
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check that this isn't on Android? According to this it should.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the BSDs, too. They all have it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, it's already defined in libc for all BSDs and Solaris as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked against rust's libc bindings, where src/unix/notbsd/android/mod.rs doesn't define getifaddrs (at least not in 3f903a1d from master as of Jul 11).

And: Huh? not(any(target_os = "android", target_os = "haiku")) already includes the BSDs.

/// Creates an `IpAddr` struct from libc's sockaddr.
///
/// Note that this supports only IPv4 and IPv6 address families.
pub unsafe fn from_libc_sockaddr(addr: *mut libc::sockaddr) -> Option<InetAddr> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this unsafe and the other from_libc isn't? What's the reason for providing this API?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I marked this one unsafe because it takes a raw pointer, which could point anywhere and yield a segfault. Again, this is not a public function, but only used internally (by that other InterfaceAddress::from_libc_ifaddrs function)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not a public function, you should remove the pub

@Susurrus
Copy link
Contributor

Thanks for the PR! There are a few things to address from this, notably a lack of tests besides the doctest that you added (which is great BTW).

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like the start of a pretty good API. I like that you used Iterator. In addition to the inline comments I made, I think that this could really use some better tests. I think you'll need to find a way to mock libc::getifaddrs and return some canned data. Does Rust have a mocking library yet?

@@ -64,6 +64,24 @@ impl InetAddr {
}
}

/// Creates an `IpAddr` struct from libc's sockaddr.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/IpAddr/InetAddr/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that's a last-minute typo. I didn't notice SockAddr before and currently think the getifaddrs API shoud rather use SockAddr.

None
} else {
let family = (*addr).sa_family as u32;
match mem::transmute::<u32, AddressFamily>(family) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using transmute here, it's better to provide an AddressFamily::from_u32 method.

src/ifaddrs.rs Outdated

use sys::socket::InetAddr;

#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "fuchsia"))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nix doesn't support emscripten or fuschia, so you don't need to mention them here. It doesn't hurt, though, so you can leave it in if you want to. But why not enable it for the BSDs? They support all this stuff. If there are a few Linux specific flags, you can #[cfg()] individual flags instead of the whole thing.

src/ifaddrs.rs Outdated
target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
target_os = "bitrig", target_os = "solaris"))]
{
addr.destination = unsafe { InetAddr::from_libc_sockaddr(info.ifa_dstaddr) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you forgot to set the broadcast for non-Linux platforms. You can do it the same way as on Linux. Depending on the flags, either ifa_dstaddr or ifa_broadaddr is set. The only difference is that on BSD, one of those is a macro for the other, whereas on Linux they're a union.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it's the same field for BSDs as well? I did it that way due to rust's libc bindings, where there is not ifa_broadaddr or ifa_broadcastaddr defined. I'll check the C headers from FreeBSD and adjust.

Given the BSDs also lack IFF_BROADCAST and IFF_POINTOPOINT, how do they distinguish between broadcast vs destination address?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked, and libc does define both IFF_BROADCAST and IFF_POINTOPOINT for most, if not all, platforms.

src/ifaddrs.rs Outdated
///
/// # Example
/// ```
/// match nix::ifaddrs::getifaddrs() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the example code, it would probably be clearer to use unwrap instead of a match block

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An unwrap is very likely to fail due to the many address = None cases you'll encounter. It certainly makes cargo test fail on my box (Debian, with many AF_PACKET entries that cannot be represented ATM).

src/ifaddrs.rs Outdated
/// }
/// }
/// ```
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the BSDs, too. They all have it.

src/ifaddrs.rs Outdated
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
let mut addrs: *mut libc::ifaddrs = unsafe { mem::zeroed() };
match unsafe { libc::getifaddrs(&mut addrs) } {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably simplify this logic to something like:
Errno::result(unsafe { libc::getifaddrs(&mut addrs) }).map(|_| Ok(InterfaceAddressIterator{base: addrs, next:addrs}))

@Susurrus
Copy link
Contributor

@asomers mockers looks promising for a general-purpose mocking library.

@mwanner
Copy link
Contributor Author

mwanner commented Jul 11, 2017

@Susurrus and @asomers: Thanks a lot for your review. I adjusted some things and commented on others.

I still need to check how destination vs broadcast addresses work for BSDs. And I'd like to change InetAddr for SockAddr, maybe adding better support for AF_PACKET entries.

@mwanner
Copy link
Contributor Author

mwanner commented Jul 27, 2017

Proper support for BSDs depends on corrections in libc, see rust-lang/libc#682.

I'm currently in favour of disabling getifaddrs for those OSes for which libc is buggy and at least add the getifaddrs support for the others (including Linux and OS X, IIRC). We can later enable full support when libc gets corrected. (BTW, can rust handle code dependent on versions of external crates?)

@Susurrus Susurrus changed the title Add support for getifaddrs. Closes: #650. Add support for getifaddrs. Jul 27, 2017
src/net/if_.rs Outdated
libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub flags InterfaceFlags: libc::c_int {
#[cfg(any(target_os = "linux", target_os = "android"))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The target_os list should be alphabetized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc defines most of these constants for most platforms, not just Linux. You should expand the #[cfg()] directives appropriately.

@Susurrus
Copy link
Contributor

I'd prefer to wait on this until rust-lang/libc#682 gets addressed assuming you're willing to take that on. Were you planning on doing that?

@mwanner
Copy link
Contributor Author

mwanner commented Jul 27, 2017

Were you planning on doing that?

Not really, sorry. The itch I was trying to scratch originally was getifaddrs on Linux. I fear I have neither enough BSDish boxes to test this nor enough spare time to hack on rust-lang/libc#682. Therefore my suggestion to at least provide support for OSes (correctly) supported by libc.

@Susurrus
Copy link
Contributor

@mwanner I'm fine with that as long as the code documents why we aren't exposing it everywhere and it references a bug filed with libc.

@posborne
Copy link
Member

@mwanner @asomers I happened to be working on a problem today where I needed just this functionality. What are the next step on this one at this point? I'm assuming the libc issue and related CI hole is still the gate at this point?

@Susurrus
Copy link
Contributor

@posborne I think there are two options, one is deliver this just for Linux or the second is get someone working on resolving the libc blocker and then enable it on all supported platforms. Since we don't have anyone stepping up to do the latter, I'd suggest going with the former. Strip out anything non-Linux, and put a big TODO in there along with a reference to the libc bug.

@asomers
Copy link
Member

asomers commented Nov 16, 2017

Actually, the libc definition is only wrong for NetBSD. FreeBSD is fine.

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is really close. You shouldn't need access to a BSD machine in order to fix the IFF_* definitions. It's simply a matter of going through libc and checking which operating systems define which flags.

src/ifaddrs.rs Outdated
target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd",
target_os = "bitrig", target_os = "solaris"))]
{
addr.destination = unsafe { InetAddr::from_libc_sockaddr(info.ifa_dstaddr) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked, and libc does define both IFF_BROADCAST and IFF_POINTOPOINT for most, if not all, platforms.

src/net/if_.rs Outdated
libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub flags InterfaceFlags: libc::c_int {
#[cfg(any(target_os = "linux", target_os = "android"))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc defines most of these constants for most platforms, not just Linux. You should expand the #[cfg()] directives appropriately.

/// Creates an `IpAddr` struct from libc's sockaddr.
///
/// Note that this supports only IPv4 and IPv6 address families.
pub unsafe fn from_libc_sockaddr(addr: *mut libc::sockaddr) -> Option<InetAddr> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not a public function, you should remove the pub

@mwanner
Copy link
Contributor Author

mwanner commented Nov 16, 2017

Thanks for the heads up, @asomers. Looks like libc has been updated, because the former cfg conditions were based on reading its source, too.

I made an attempt to update this PR to match current libc sources.

@mwanner
Copy link
Contributor Author

mwanner commented Nov 16, 2017

@asomers wrote:

If it's not a public function, you should remove the pub

Given libc::sockaddr is public, users of nix might well want to obtain a nix::sys::socket::SockAddr from a libc::sockaddr. At least I found myself missing that constructor, before.

@posborne
Copy link
Member

I can take a look at Android support as well. I happen to need to target Android aarch64 myself and bionic has had getifaddrs for some time but the libc bindings appear to be missing that on the rust side (explicitly). No need to gate this on that work though, as it will definitely require libc changes.

@Susurrus
Copy link
Contributor

@posborne It looks like getifaddrs() just needs to be moved up into src/notbsd/mod.rs and removed from linux/mod.rs and emscripten/mod.rs. If you want to do this and help run any tests that come out of this on Android, then I think it wouldn't be hard to include that support as part of this PR.

@nix-rust nix-rust deleted a comment from mwanner Nov 17, 2017
Copy link
Contributor

@Susurrus Susurrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good! I just noticed a few style/convention issues that would be nice to cleanup before merging. Good work!

src/ifaddrs.rs Outdated
/// }
/// ```
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
let mut addrs: *mut libc::ifaddrs = unsafe { mem::zeroed() };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this not be mem::uninitialized() instead?

src/net/if_.rs Outdated
libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub struct InterfaceFlags: libc::c_int {
IFF_UP;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add doccomments for each variant.

src/net/if_.rs Outdated
IFF_DEBUG;
IFF_LOOPBACK;
IFF_POINTOPOINT;
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "haiku")))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetize all OSes listed in #[cfgs. Additionally please use a whitelist instead of a blacklist for targets for consistency.

@@ -201,6 +201,23 @@ pub enum AddressFamily {
Natm = libc::AF_NATM,
}

impl AddressFamily {
pub fn from_i32(family: i32) -> Option<AddressFamily> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a doccomment for this function as it's public.

libc::AF_UNIX => Some(AddressFamily::Unix),
libc::AF_INET => Some(AddressFamily::Inet),
libc::AF_INET6 => Some(AddressFamily::Inet6),
#[cfg(any(target_os = "linux", target_os = "android"))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetize the OSes.

src/lib.rs Outdated
@@ -43,6 +43,9 @@ pub mod poll;

pub mod net;

#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer to avoid blocklists with #cfgs, can you instead whitelist all appropriate platforms?

src/ifaddrs.rs Outdated

let ifu : *mut libc::sockaddr;

#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "fuchsia"))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably want to use cfg_if! here instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't manage to use a cfg_if! for just these two statements, but extracted a small private function, now. Seems cleaner to me that way, but YMMV.

src/ifaddrs.rs Outdated
destination: None,
};

let ifu : *mut libc::sockaddr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the space before the colon.

src/ifaddrs.rs Outdated
@@ -0,0 +1,137 @@
//! Query network interface addresses
//!
//! Uses the Linux and/or BSD specific function getifaddrs to query the list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put backticks around getifaddrs so it renders nicely with rustdoc.

#[cfg(any(target_os = "ios", target_os = "macos"))]
Some(AddressFamily::System) => Some(SockAddr::SysControl(
SysControlAddr(*(addr as *mut sys_control::sockaddr_ctl)))),
// No other address families are supported, including netlink and syscontrol.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a TODO or FIXME or just impossible? Please clarify this comment further.

@mwanner
Copy link
Contributor Author

mwanner commented Nov 17, 2017

@Susurrus thanks for your review. I think I covered all points.

src/ifaddrs.rs Outdated
/// Get interface addresses using libc's `getifaddrs`
///
/// Note that the underlying implementation differs between OSes and the nix
/// crate doesn't support `AF_PACKET` entries. Therefore, the returned list
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be a nice change in the future to support AF_PACKET/AF_LINK in order to be able to build something out for getting MAC addresses (useful for, among other things, getting a node id for UUID V1 generation). Not gating for the first pass.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment could be made more clear as to why this isn't implemented. Was it because we were lazy or because the OSes differed enough it'd be quite difficult to expose a nice API here?

src/ifaddrs.rs Outdated
use net::if_::*;

/// Describes a single address for an interface as returned by `getifaddrs`.
pub struct InterfaceAddress {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we implement or derive Debug/Display for this. Also, I think deriving Clone, PartialEq, and Hash would be good.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this as well, should definitely have Clone, Copy, Debug, Eq, Hash, and PartialEq. We shouldn't implement Display here and I don't think we should implement Default here either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit ef40c9c, I added derives for Clone, Eq, Hash, and PartialEq, but not Copy. The struct uses Strings, so I don't this that marker is applicable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and Debug. I wasn't quite sure how much or in what format to return from fn fmt. Also note that SockAddr doesn't implement Debug, but Display. Is this really consistent?

Copy link
Contributor

@Susurrus Susurrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few more style changes and some derives to get sorted, but definitely almost there!

src/net/if_.rs Outdated
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "ios",
target_os = "linux", target_os = "macos", target_os = "netbsd",
target_os = "openbsd"))]
/// Avoid use of trailers. (see
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put the doccomments for items above any #[cfgs for those items. Applies in several places here.

src/lib.rs Outdated
@@ -43,6 +43,10 @@ pub mod poll;

pub mod net;

#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linux is out of alphabetical order, and could you list these 1-per-line? When cfg's get longer than a line we like to wrap with each target on its own line.

src/ifaddrs.rs Outdated
/// Get interface addresses using libc's `getifaddrs`
///
/// Note that the underlying implementation differs between OSes and the nix
/// crate doesn't support `AF_PACKET` entries. Therefore, the returned list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment could be made more clear as to why this isn't implemented. Was it because we were lazy or because the OSes differed enough it'd be quite difficult to expose a nice API here?

src/ifaddrs.rs Outdated
use net::if_::*;

/// Describes a single address for an interface as returned by `getifaddrs`.
pub struct InterfaceAddress {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this as well, should definitely have Clone, Copy, Debug, Eq, Hash, and PartialEq. We shouldn't implement Display here and I don't think we should implement Default here either.

src/ifaddrs.rs Outdated
/// 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.
pub struct InterfaceAddressIterator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely derive Debug here, but does it make sense to derive any of the other standard ones (Clone, Copy, Eq, Hash, and PartialEq)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clone certainly doesnt make much sense, as the InterfaceAddressIteratoris used to govern the memory allocated bygetifaddrs. I added Debug, Eq, Hash, and PartialEq`.

src/net/if_.rs Outdated
/// Avoid use of trailers. (see
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_NOTRAILERS;
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "fuchsia",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please list targets one-per-line when wrapping these cfgs.

@nix-rust nix-rust deleted a comment from mwanner Nov 21, 2017
Copy link
Contributor

@Susurrus Susurrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor things left, then we're GTM.

src/ifaddrs.rs Outdated
}

cfg_if! {
if #[cfg(any(target_os = "emscripten", target_os = "fuchsia", target_os="linux"))] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs spacing around the = before linux.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, corrected.

src/ifaddrs.rs Outdated
///
/// 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). For any entry not supported,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I was looking at this as a user, my first thought would be oh, what are those specific address families? I think this information is embedded in InterfaceFlags, yes? If so, that should be mentioned in this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe some of the InterfaceFlags are specific per address family, yes. However, it's the returned SockAddr type(s) that directly map to an AddressFamily, see from_libc_sockaddr. I tried to improve the comment.

@Susurrus
Copy link
Contributor

Also, could you squash this down into 1 commit? Makes viewing the history of nix a lot easier and better supports git bisect.

src/net/if_.rs Outdated
IFF_RUNNING;
/// Resources allocated.
#[cfg(any(target_os = "freebsd"))]
IFF_DRV_RUNNING;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be IFF_RUNNING instead of IFF_DRV_RUNNING. The former is an alias for the latter. But you may need to add it to libc first. Ditto for IFF_OACTIVE below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc defines only IFF_DRV_RUNNING and IFF_DRV_OACTIVE for FreeBSD, so that's what I used. Given I don't really use - let alone know - FreeBSD, I don't quite feel in the position to open a PR on libc.

Especially since FreeBSD seems to prefer the DRV variant since version 8.1:
https://www.freebsd.org/cgi/man.cgi?query=ifnet

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The distinction between the DRV variants and the non-DRV variants only matters for kernel code. You can read the gory details at https://svnweb.freebsd.org/base?view=revision&revision=148886 . It's perfectly fine to use the non-DRV variants in userland code, especially since their values are identical. For a cross-platform library like nix, the non-DRV variants are better, because they're portable. The current definitions are at https://svnweb.freebsd.org/base/head/sys/net/if.h?view=markup#l170 . You can go ahead and submit a PR to libc based on those definitions, even if you don't have a FreeBSD VM to test on. If you like, CC me as a reviewer on the libc PR.

src/net/if_.rs Outdated
/// Avoid use of trailers. (see
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(target_os = "android",
target_os = "bitrig",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to worry about bitrig anymore. A future nix PR will probably remove all references. For now, it doesn't really matter whether or not you leave it in.
https://www.phoronix.com/scan.php?page=news_item&px=Bitrig-Fork-Dead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, thanks for the hint.

src/net/if_.rs Outdated
/// 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 = "freebsd", target_os = "fuchsia", target_os = "linux"))]
IFF_PORTSEL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what caused your build failure. IFF_PORTSEL is not defined on FreeBSD.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. That's clearly wrong. Corrected.

@nix-rust nix-rust deleted a comment from mwanner Nov 22, 2017
src/net/if_.rs Outdated
/// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_PROMISC;

// bitmask: 0x0200
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove all these "bitmask: 0xXXX" constants and the blank line between all enum entries here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those greatly simplified assembling the list of IFF_ constants and will help maintaining it in the future. Can you please suggest a viable alternative? Dropping that information is not a good option, IMO.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all listed in /usr/include/linux/if.h, so was this helpful for the other platforms? I don't know how this will help make things more maintainable except be incorrect or out of date possible since these values will never likely be updated. I think it's best we remove them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Susurrus Yes, because I could map to the other OSes' header files by bitmask and ensure I got the complete list covered (these flags aren't alphabetically ordered, but by value). If you want to take over and keep the list in sync with libc, I'm fine with having the bitmasks removed. However, I'm certainly not ever gonna do it again w/o the information these hints provide.

That being said, maybe there's a better way to "encode" them that better conforms to the style guide applicable for nix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand how it helped you to compile the list, but it won't be helpful moving forward in maintaining it as I mentioned above. That being said, I'm not going to throw too much more of a fight over it as I don't think this is that important. Please do, however, remove the blank lines in between the variants.

bors added a commit to rust-lang/libc that referenced this pull request Nov 29, 2017
Use IFF_OACTIVE and IFF_RUNNING even on FreeBSD. Deprecate the DRV ones.

According to @asomers, libc should propagate the use of the portable constants `IFF_OACTIVE` and `IFF_RUNNING` for user-space applications, instead of `IFF_DRV_OACTIVE` and `IFF_DRV_RUNNING`. It least that's my understanding of [his comment](nix-rust/nix#667 (comment)) in nix-rust/nix#667.
@lucab
Copy link
Contributor

lucab commented Nov 30, 2017

@Susurrus @asomers what's the status and differences between this PR and #764? I'm also in the need of support for ifaddrs features and willing to give it a try, but I'm not sure which of the two PRs to look at.

@Susurrus
Copy link
Contributor

Susurrus commented Dec 2, 2017

@lucab It's basically whichever gets there first at this point. I would recommend we focus our efforts on this PR since #764 hasn't been touched in months and I don't expect it to ever resolve.

@mwanner Please remove the blank lines from InterfaceFlags and squash all commits into 1, and then let's get @asomers and @lucab to take a pass at this and we'll be golden!

src/ifaddrs.rs Outdated
///
/// 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 familiy is encoded
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/familiy/family/

src/ifaddrs.rs Outdated
/// }
/// },
/// Err(err) => {
/// println!("error getting interface addresses: {}", err)
Copy link
Contributor

@lucab lucab Dec 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would simplify this example in order to show just the getifaddrs, by doing:

  • a try! / unwrap / expect on the getifaddrs
  • a while let on the interator

The resulting example would be less indented and more compact:

let addrs = getifaddrs()?;
while let Ok(a) = addrs.next() {
  println!(...)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I stripped the error handling.

Note however, that the iterator is not over a Result<..>, but over multiple InterfaceAddress structs, where always get an interface_name, but for which we do not (yet) support the address type. Therefore, neither while let Ok(..) nor while let Some(..) work and the simple for loop likely is the easiest. I tried to clarify the example in that regard.

@lucab
Copy link
Contributor

lucab commented Dec 3, 2017

I agree it would be nice to have AF_PACKET as it is pretty ubiquitous, but personally I don't need it right now. I'd be fine having a TODO line in place for that and a followup ticket detailing what's left to be done.

Except for typos and very verbose example, this PR looks nice at a first glance. Thanks for this!

@mwanner
Copy link
Contributor Author

mwanner commented Dec 3, 2017

I guess this branch currently fails due to d374a1e from the master branch. @Susurrus any visibility change I need to be aware of? Or why is this suddenly not finding IFF_BROADCAST, anymore.

@lucab
Copy link
Contributor

lucab commented Dec 3, 2017

@mwanner #801 changed the scope of bit-constants, see #801 (comment). Those are now InterfaceFlags::IFF_*. You'd see this with a cargo update.

@mwanner
Copy link
Contributor Author

mwanner commented Dec 3, 2017

@Susurrus, @asomers, @lucab: I think I covered all concerns and the result now passes CI tests as well.

As two of you voted against the bitmask comments and the corresponding grouping by empty lines, I removed those. Given the lack of that information, I wonder if alphabetical ordering of the IFF_* flags would ease future maintenance. Opinions?

To track progress on the requested support of AF_PACKET, I opened #809 (which I started hacking on, BTW, nothing ready to push, though.)

Given @lucab's simplified example wasn't correct, I fear the getifaddrs interfaces is less than obvious. IMO that justifies the verbosity of the example. You could even argue that the netmask, broadcast, and destination addresses are missing completely...

Compared to #764, this variant is closer to the libc interface, not providing a HashMap collecting all entries per interface, but (hopefully) working without any additional memory allocation. That might make the interface of getifaddrs look a bit clumsy, though.

@Susurrus
Copy link
Contributor

Susurrus commented Dec 4, 2017

As two of you voted against the bitmask comments and the corresponding grouping by empty lines, I removed those. Given the lack of that information, I wonder if alphabetical ordering of the IFF_* flags would ease future maintenance. Opinions?

The way maintenance of this will work is that someone will find a constant that's missing. So they'll just add them. There really isn't going to be someone doing a full audit of these per-platform, so I think we're fine here. I think you're overly concerned with the maintenance burden of that bitmask.

To track progress on the requested support of AF_PACKET, I opened #809 (which I started hacking on, BTW, nothing ready to push, though.)

Perfect, thanks.

Given @lucab's simplified example wasn't correct, I fear the getifaddrs interfaces is less than obvious. IMO that justifies the verbosity of the example. You could even argue that the netmask, broadcast, and destination addresses are missing completely...

I don't think we need a complete solution on the first pass, I think this one is pretty good. And I agree with keeping a verbose example. My only question here would be how to make sure that incorrect usage of this API fails at compile time. I think @lucab's example would have failed at compile time, so I think concerns about confusion about this API might be unfounded.

Compared to #764, this variant is closer to the libc interface, not providing a HashMap collecting all entries per interface, but (hopefully) working without any additional memory allocation. That might make the interface of getifaddrs look a bit clumsy, though.

That's a good thing. nix aims to be as thin of a wrapper as possible around libc, so it won't always deliver the most ergonomic APIs. Then again, allocating is really only a big problem if you're doing high-iterations of things. For example, my implementation of the Termios API in nix does have some overhead, but considering that is rarely dealt with at high frequency, it's not an issue. We use a relatively subjective measurement for how "close to the metal" nix APIs should be.

Overall, I think this looks pretty good. I suggest we merge it. I'll wait until @asomers responds (@lucab feel free to chime in here as well if you have anything to add).

@asomers
Copy link
Member

asomers commented Dec 4, 2017

Yep, LGTM.

bors r+

bors bot added a commit that referenced this pull request Dec 4, 2017
667: Add support for getifaddrs. r=asomers a=mwanner

Here's a first attempt at a rustian interface for `getifaddrs`, please review.

Changes for the changelog:

- Added `nix::ifaddrs::{getifaddrs, InterfaceAddressIterator,
  InterfaceAddress and InterfaceFlags}`

Closes #650.
Closes #764.
@bors
Copy link
Contributor

bors bot commented Dec 4, 2017

@bors bors bot merged commit 985ea01 into nix-rust:master Dec 4, 2017
@NoraCodes
Copy link

Thanks for doing this, and sorry my PR died. I over estimated my ability to do open source while in school, but I'm glad to see that the functionality has been added!

@posborne
Copy link
Member

posborne commented Dec 4, 2017

Great to see this land! 🎉 Thanks for the contribution @mwanner and @Susurrus / @asomers for the diligent reviews.

asomers added a commit to asomers/nix that referenced this pull request Apr 12, 2020
This function never should've been public, since it's basically
impossible to use directly.  It's only public due to an oversight from
PR nix-rust#667 .
bors bot added a commit that referenced this pull request May 1, 2020
1215: Remove sys::socket::addr::from_libc_sockaddr from the public API r=posborne a=asomers

This function never should've been public, since it's basically
impossible to use directly.  It's only public due to an oversight from
PR #667 .

Co-authored-by: Alan Somers <asomers@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants