Skip to content

Commit 2c762b3

Browse files
authored
ifaces: Use nix::ifaddrs::getifaddrs() 100% (#519)
By relying on nix to handle the kernel/libc call complexities, this removes all unsafe code blocks and just makes the code pretty readable and straight forward.
1 parent aba5218 commit 2c762b3

File tree

1 file changed

+69
-176
lines changed
  • util/src/ifaces/ffi/unix

1 file changed

+69
-176
lines changed

util/src/ifaces/ffi/unix/mod.rs

Lines changed: 69 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,79 @@
1-
use std::ffi::CStr;
2-
use std::net::IpAddr;
3-
use std::{net, ptr};
4-
5-
use ::std::io::{Error, ErrorKind};
6-
71
use crate::ifaces::{Interface, Kind, NextHop};
82

9-
// https://github.com/Exa-Networks/exaproxy/blob/master/lib/exaproxy/util/interfaces.py
10-
11-
pub const AF_INET: i32 = nix::sys::socket::AddressFamily::Inet as i32;
12-
pub const AF_INET6: i32 = nix::sys::socket::AddressFamily::Inet6 as i32;
13-
14-
#[cfg(any(
15-
target_os = "macos",
16-
target_os = "ios",
17-
target_os = "freebsd",
18-
target_os = "openbsd",
19-
target_os = "netbsd"
20-
))]
21-
pub const AF_LINK: i32 = nix::libc::AF_LINK;
22-
#[cfg(any(
23-
target_os = "macos",
24-
target_os = "ios",
25-
target_os = "freebsd",
26-
target_os = "openbsd",
27-
target_os = "netbsd"
28-
))]
29-
pub const AF_PACKET: i32 = -1;
30-
31-
#[cfg(any(target_os = "linux", target_os = "android"))]
32-
pub const AF_LINK: i32 = -1;
33-
#[cfg(any(target_os = "linux", target_os = "android"))]
34-
pub const AF_PACKET: i32 = nix::libc::AF_PACKET;
35-
36-
#[allow(dead_code, non_camel_case_types)]
37-
#[repr(C)]
38-
pub enum SiocgifFlags {
39-
Up = 0x1, /* Interface is up. */
40-
Broadcast = 0x2, /* Broadcast address valid. */
41-
Debug = 0x4, /* Turn on debugging. */
42-
Loopback = 0x8, /* Is a loopback net. */
43-
Pointopoint = 0x10, /* Interface is point-to-point link. */
44-
Notrailers = 0x20, /* Avoid use of trailers. */
45-
Running = 0x40, /* Resources allocated. */
46-
Noarp = 0x80, /* No address resolution protocol. */
47-
Promisc = 0x100, /* Receive all packets. */
48-
49-
/* Not supported */
50-
Allmulti = 0x200, /* Receive all multicast packets. */
51-
52-
Master = 0x400, /* Master of a load balancer. */
53-
Slave = 0x800, /* Slave of a load balancer. */
54-
55-
Multicast = 0x1000, /* Supports multicast. */
56-
57-
Portsel = 0x2000, /* Can set media type. */
58-
Automedia = 0x4000, /* Auto media select active. */
59-
Dynamic = 0x8000, /* Dialup device with changing addresses. */
60-
}
61-
62-
#[repr(C)]
63-
pub struct union_ifa_ifu {
64-
pub data: *mut ::std::os::raw::c_void,
65-
}
66-
impl union_ifa_ifu {
67-
pub fn ifu_broadaddr(&mut self) -> *mut nix::sys::socket::sockaddr {
68-
self.data as *mut nix::sys::socket::sockaddr
69-
}
70-
pub fn ifu_dstaddr(&mut self) -> *mut nix::sys::socket::sockaddr {
71-
self.data as *mut nix::sys::socket::sockaddr
72-
}
73-
}
74-
75-
#[repr(C)]
76-
pub struct ifaddrs {
77-
pub ifa_next: *mut ifaddrs,
78-
pub ifa_name: *mut ::std::os::raw::c_char,
79-
pub ifa_flags: ::std::os::raw::c_uint,
80-
pub ifa_addr: *mut nix::sys::socket::sockaddr,
81-
pub ifa_netmask: *mut nix::sys::socket::sockaddr,
82-
pub ifa_ifu: union_ifa_ifu,
83-
pub ifa_data: *mut ::std::os::raw::c_void,
84-
}
85-
86-
extern "C" {
87-
pub fn getifaddrs(ifap: *mut *mut ifaddrs) -> ::std::os::raw::c_int;
88-
pub fn freeifaddrs(ifa: *mut ifaddrs) -> ::std::os::raw::c_void;
89-
#[allow(dead_code)]
90-
pub fn if_nametoindex(ifname: *const ::std::os::raw::c_char) -> ::std::os::raw::c_uint;
91-
}
92-
93-
pub fn nix_socketaddr_to_sockaddr(sa: *mut nix::sys::socket::sockaddr) -> Option<net::SocketAddr> {
94-
if sa.is_null() {
95-
return None;
3+
use nix::sys::socket::{AddressFamily, SockaddrLike, SockaddrStorage};
4+
use std::io::Error;
5+
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
6+
7+
fn ss_to_netsa(ss: &SockaddrStorage) -> Option<SocketAddr> {
8+
match ss.family() {
9+
Some(AddressFamily::Inet) => ss.as_sockaddr_in().map(|sin| {
10+
SocketAddr::V4(SocketAddrV4::new(
11+
std::net::Ipv4Addr::from(sin.ip()),
12+
sin.port(),
13+
))
14+
}),
15+
Some(AddressFamily::Inet6) => ss.as_sockaddr_in6().map(|sin6| {
16+
SocketAddr::V6(SocketAddrV6::new(
17+
sin6.ip(),
18+
sin6.port(),
19+
sin6.flowinfo(),
20+
sin6.scope_id(),
21+
))
22+
}),
23+
_ => None,
9624
}
97-
98-
let (addr, port) = match unsafe { *sa }.sa_family as i32 {
99-
AF_INET => {
100-
let sa: *const nix::sys::socket::sockaddr_in = sa as *const nix::libc::sockaddr_in;
101-
let sa = &unsafe { *sa };
102-
let (addr, port) = (sa.sin_addr.s_addr, sa.sin_port);
103-
(IpAddr::V4(Into::into(u32::from_be(addr))), port)
104-
}
105-
AF_INET6 => {
106-
let sa: *const nix::sys::socket::sockaddr_in6 = sa as *const nix::libc::sockaddr_in6;
107-
let sa = &unsafe { *sa };
108-
let (addr, port) = (sa.sin6_addr.s6_addr, sa.sin6_port);
109-
(Into::into(addr), port)
110-
}
111-
_ => return None,
112-
};
113-
Some(net::SocketAddr::new(addr, port))
11425
}
11526

11627
/// Query the local system for all interface addresses.
11728
pub fn ifaces() -> Result<Vec<Interface>, Error> {
118-
let mut ifaddrs_ptr: *mut ifaddrs = ptr::null_mut();
119-
match unsafe { getifaddrs(&mut ifaddrs_ptr as *mut _) } {
120-
0 => {
121-
let mut ret = Vec::new();
122-
let mut item: *mut ifaddrs = ifaddrs_ptr;
123-
loop {
124-
if item.is_null() {
125-
break;
126-
}
127-
let name = String::from_utf8(
128-
unsafe { CStr::from_ptr((*item).ifa_name) }
129-
.to_bytes()
130-
.to_vec(),
131-
);
132-
unsafe {
133-
if name.is_err() || (*item).ifa_addr.is_null() {
134-
item = (*item).ifa_next;
135-
continue;
136-
}
137-
}
138-
let kind = match unsafe { (*(*item).ifa_addr).sa_family as i32 } {
139-
AF_INET => Some(Kind::Ipv4),
140-
AF_INET6 => Some(Kind::Ipv6),
141-
AF_PACKET => Some(Kind::Packet),
142-
AF_LINK => Some(Kind::Link),
143-
code => Some(Kind::Unknow(code)),
144-
};
145-
if kind.is_none() {
146-
item = unsafe { (*item).ifa_next };
147-
continue;
148-
}
149-
150-
let addr = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_addr });
151-
let mask = nix_socketaddr_to_sockaddr(unsafe { (*item).ifa_netmask });
152-
let hop = unsafe {
153-
if (*item).ifa_flags & SiocgifFlags::Broadcast as ::std::os::raw::c_uint
154-
== SiocgifFlags::Broadcast as ::std::os::raw::c_uint
155-
{
156-
nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_broadaddr())
157-
.map(NextHop::Broadcast)
158-
} else {
159-
nix_socketaddr_to_sockaddr((*item).ifa_ifu.ifu_dstaddr())
160-
.map(NextHop::Destination)
161-
}
162-
};
163-
164-
if let Some(kind) = kind {
165-
match kind {
166-
Kind::Unknow(_) => {}
167-
_ => {
168-
ret.push(Interface {
169-
name: name.unwrap(),
170-
kind,
171-
addr,
172-
mask,
173-
hop,
174-
});
175-
}
176-
};
177-
};
178-
179-
item = unsafe { (*item).ifa_next };
180-
}
181-
unsafe { freeifaddrs(ifaddrs_ptr) };
182-
Ok(ret)
29+
let mut ret = Vec::new();
30+
for ifa in nix::ifaddrs::getifaddrs()? {
31+
if let Some(kind) = ifa
32+
.address
33+
.as_ref()
34+
.and_then(SockaddrStorage::family)
35+
.and_then(|af| match af {
36+
AddressFamily::Inet => Some(Kind::Ipv4),
37+
AddressFamily::Inet6 => Some(Kind::Ipv6),
38+
#[cfg(any(
39+
target_os = "android",
40+
target_os = "linux",
41+
target_os = "illumos",
42+
target_os = "fuchsia",
43+
target_os = "solaris"
44+
))]
45+
AddressFamily::Packet => Some(Kind::Packet),
46+
#[cfg(any(
47+
target_os = "dragonfly",
48+
target_os = "freebsd",
49+
target_os = "ios",
50+
target_os = "macos",
51+
target_os = "illumos",
52+
target_os = "netbsd",
53+
target_os = "openbsd"
54+
))]
55+
AddressFamily::Link => Some(Kind::Link),
56+
_ => None,
57+
})
58+
{
59+
let name = ifa.interface_name;
60+
let dst = ifa.destination.as_ref().and_then(ss_to_netsa);
61+
let broadcast = ifa.broadcast.as_ref().and_then(ss_to_netsa);
62+
let hop = dst
63+
.map(NextHop::Destination)
64+
.or(broadcast.map(NextHop::Broadcast));
65+
let addr = ifa.address.as_ref().and_then(ss_to_netsa);
66+
let mask = ifa.netmask.as_ref().and_then(ss_to_netsa);
67+
68+
ret.push(Interface {
69+
name,
70+
kind,
71+
addr,
72+
mask,
73+
hop,
74+
});
18375
}
184-
_ => Err(Error::new(ErrorKind::Other, "Oh, no ...")), // Err(nix::errno::Errno::last());
18576
}
77+
78+
Ok(ret)
18679
}

0 commit comments

Comments
 (0)