Skip to content

Commit f629ab3

Browse files
feat: use netlink as a fallback on android
1 parent a8bed97 commit f629ab3

File tree

3 files changed

+224
-38
lines changed

3 files changed

+224
-38
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ license = "MIT"
1414
libc = "0.2"
1515

1616
[target.'cfg(target_os = "android")'.dependencies]
17+
# DL Open
1718
dlopen = "0.1.8"
1819
once_cell = "1.17.1"
20+
# netlink
21+
netlink-packet-core = "0.5"
22+
netlink-packet-route = "0.15"
23+
netlink-sys = "0.8"
1924

2025
[target.'cfg(windows)'.dependencies]
2126
memalloc = "0.1.0"

src/interface/android.rs

Lines changed: 190 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
use once_cell::sync::OnceCell;
22

3+
pub fn get_libc_ifaddrs() -> Option<(
4+
unsafe extern "C" fn(*mut *mut libc::ifaddrs) -> libc::c_int,
5+
unsafe extern "C" fn(*mut libc::ifaddrs),
6+
)> {
7+
match (get_getifaddrs(), get_freeifaddrs()) {
8+
(Some(a), Some(b)) => Some((a, b)),
9+
_ => None,
10+
}
11+
}
12+
313
fn load_symbol<T>(sym: &'static str) -> Option<T> {
414
const LIB_NAME: &str = "libc.so";
515

@@ -33,32 +43,191 @@ fn get_freeifaddrs() -> Option<unsafe extern "C" fn(*mut libc::ifaddrs)> {
3343
*INSTANCE.get_or_init(|| load_symbol("freeifaddrs"))
3444
}
3545

36-
pub unsafe fn getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int {
37-
// Android is complicated
46+
mod netlink {
47+
//! Netlink based getifaddrs.
48+
//!
49+
//! Based on the logic found in https://git.musl-libc.org/cgit/musl/tree/src/network/getifaddrs.c
3850
39-
// API 24+ contains the getifaddrs and freeifaddrs functions but the NDK doesn't
40-
// expose those functions in ifaddrs.h when the minimum supported SDK is lower than 24
41-
// and therefore we need to load them manually.
42-
if let Some(dyn_getifaddrs) = get_getifaddrs() {
43-
return dyn_getifaddrs(ifap);
44-
}
51+
use netlink_packet_core::{
52+
NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST,
53+
};
54+
use netlink_packet_route::{
55+
rtnl::address::nlas::Nla as AddressNla, rtnl::link::nlas::Nla as LinkNla, AddressMessage,
56+
LinkMessage, RtnlMessage,
57+
};
58+
use netlink_sys::{protocols::NETLINK_ROUTE, Socket};
59+
use std::net::{Ipv4Addr, Ipv6Addr};
4560

46-
// If API < 24 (or we can't load libc for some other reason), we fallback to using netlink
47-
netlink_getifaddrs(ifap)
48-
}
61+
use crate::interface::{Interface, InterfaceType, Ipv4Net, Ipv6Net, MacAddr};
62+
63+
pub fn unix_interfaces() -> Vec<Interface> {
64+
let socket = Socket::new(NETLINK_ROUTE).unwrap();
4965

50-
pub unsafe fn freeifaddrs(ifa: *mut libc::ifaddrs) {
51-
if let Some(dyn_freeifaddrs) = get_freeifaddrs() {
52-
return dyn_freeifaddrs(ifa);
66+
let mut ifaces = Vec::new();
67+
enumerate_netlink(
68+
&socket,
69+
RtnlMessage::GetLink(LinkMessage::default()),
70+
&mut ifaces,
71+
);
72+
enumerate_netlink(
73+
&socket,
74+
RtnlMessage::GetAddress(AddressMessage::default()),
75+
&mut ifaces,
76+
);
77+
78+
ifaces
5379
}
5480

55-
netlink_freeifaddrs(ifa)
56-
}
81+
fn enumerate_netlink(socket: &Socket, msg: RtnlMessage, ifaces: &mut Vec<Interface>) {
82+
let mut packet = NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg));
83+
packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST;
84+
packet.header.sequence_number = 1;
85+
packet.finalize();
5786

58-
unsafe fn netlink_getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int {
59-
todo!()
60-
}
87+
let mut buf = vec![0; packet.header.length as usize];
88+
89+
// TODO: gracefully handle error
90+
assert!(buf.len() == packet.buffer_len());
91+
packet.serialize(&mut buf[..]);
92+
93+
socket.send(&buf[..], 0).unwrap();
6194

62-
unsafe fn netlink_freeifaddrs(ifa: *mut libc::ifaddrs) {
63-
todo!()
95+
let mut receive_buffer = vec![0; 4096];
96+
let mut offset = 0;
97+
98+
loop {
99+
let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap();
100+
101+
loop {
102+
let bytes = &receive_buffer[offset..];
103+
let rx_packet: NetlinkMessage<RtnlMessage> =
104+
NetlinkMessage::deserialize(bytes).unwrap();
105+
106+
match rx_packet.payload {
107+
NetlinkPayload::Done => {
108+
return;
109+
}
110+
NetlinkPayload::Error(err) => {
111+
eprintln!("Error: {:?}", err);
112+
return;
113+
}
114+
NetlinkPayload::InnerMessage(msg) => {
115+
match msg {
116+
RtnlMessage::NewLink(link_msg) => {
117+
let mut interface: Interface = Interface {
118+
index: link_msg.header.index,
119+
name: String::new(),
120+
friendly_name: None,
121+
description: None,
122+
if_type: InterfaceType::try_from(
123+
link_msg.header.link_layer_type as u32,
124+
)
125+
.unwrap_or(InterfaceType::Unknown),
126+
mac_addr: None,
127+
ipv4: Vec::new(),
128+
ipv6: Vec::new(),
129+
flags: link_msg.header.flags,
130+
transmit_speed: None,
131+
receive_speed: None,
132+
gateway: None,
133+
};
134+
135+
for nla in link_msg.nlas {
136+
match nla {
137+
LinkNla::IfName(name) => {
138+
interface.name = name;
139+
}
140+
LinkNla::Address(addr) => {
141+
match addr.len() {
142+
6 => {
143+
interface.mac_addr = Some(MacAddr::new(
144+
addr.try_into().unwrap(),
145+
));
146+
}
147+
4 => {
148+
let ip = Ipv4Addr::from(
149+
<[u8; 4]>::try_from(addr).unwrap(),
150+
);
151+
interface.ipv4.push(Ipv4Net::new_with_netmask(
152+
ip,
153+
Ipv4Addr::UNSPECIFIED,
154+
));
155+
}
156+
_ => {
157+
// unclear what these would be
158+
}
159+
}
160+
}
161+
_ => {}
162+
}
163+
}
164+
ifaces.push(interface);
165+
}
166+
RtnlMessage::NewAddress(addr_msg) => {
167+
println!("NewAddress: {:?}", addr_msg);
168+
if let Some(interface) =
169+
ifaces.iter_mut().find(|i| i.index == addr_msg.header.index)
170+
{
171+
for nla in addr_msg.nlas {
172+
match nla {
173+
AddressNla::Address(addr) => match addr.len() {
174+
4 => {
175+
let ip = Ipv4Addr::from(
176+
<[u8; 4]>::try_from(addr).unwrap(),
177+
);
178+
interface.ipv4.push(Ipv4Net::new(
179+
ip,
180+
addr_msg.header.prefix_len,
181+
));
182+
}
183+
16 => {
184+
let ip = Ipv6Addr::from(
185+
<[u8; 16]>::try_from(addr).unwrap(),
186+
);
187+
interface.ipv6.push(Ipv6Net::new(
188+
ip,
189+
addr_msg.header.prefix_len,
190+
));
191+
}
192+
_ => {
193+
// what else?
194+
}
195+
},
196+
_ => {}
197+
}
198+
}
199+
} else {
200+
eprintln!(
201+
"found unknown interface with index: {}",
202+
addr_msg.header.index
203+
);
204+
}
205+
}
206+
_ => {
207+
// not expecting other messages
208+
}
209+
}
210+
}
211+
_ => {}
212+
}
213+
offset += rx_packet.header.length as usize;
214+
if offset == size || rx_packet.header.length == 0 {
215+
offset = 0;
216+
break;
217+
}
218+
}
219+
}
220+
}
221+
222+
#[cfg(test)]
223+
mod tests {
224+
use super::*;
225+
226+
#[test]
227+
fn test_netlink_ifaddrs() {
228+
let interfaces = unix_interfaces();
229+
dbg!(&interfaces);
230+
assert!(!interfaces.is_empty());
231+
}
232+
}
64233
}

src/interface/unix.rs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
use super::Interface;
22
use super::MacAddr;
33
use crate::gateway;
4+
use crate::interface::InterfaceType;
45
use crate::ip::{Ipv4Net, Ipv6Net};
56
use crate::sys;
67

7-
use crate::interface::InterfaceType;
88
use libc;
99
use std::ffi::{CStr, CString};
1010
use std::mem::{self, MaybeUninit};
1111
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1212
use std::os::raw::c_char;
1313
use std::str::from_utf8_unchecked;
1414

15-
#[cfg(target_os = "android")]
16-
use super::android::{freeifaddrs, getifaddrs};
17-
1815
#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd"))]
1916
pub fn interfaces() -> Vec<Interface> {
2017
let mut interfaces: Vec<Interface> = unix_interfaces();
@@ -128,7 +125,9 @@ pub fn interfaces() -> Vec<Interface> {
128125
}
129126

130127
#[cfg(any(target_os = "linux", target_os = "android"))]
131-
fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Option<IpAddr>) {
128+
pub(super) fn sockaddr_to_network_addr(
129+
sa: *const libc::sockaddr,
130+
) -> (Option<MacAddr>, Option<IpAddr>) {
132131
use std::net::SocketAddr;
133132

134133
unsafe {
@@ -150,7 +149,7 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Opti
150149
let addr =
151150
sys::sockaddr_to_addr(mem::transmute(sa), mem::size_of::<libc::sockaddr_storage>());
152151

153-
match addr {
152+
match dbg!(addr) {
154153
Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
155154
Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
156155
Err(_) => (None, None),
@@ -199,7 +198,30 @@ fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> (Option<MacAddr>, Opti
199198
}
200199
}
201200

201+
#[cfg(not(target_os = "android"))]
202+
pub fn unix_interfaces() -> Vec<Interface> {
203+
unix_interfaces_inner(libc::getifaddrs, libc::freeifaddrs)
204+
}
205+
206+
#[cfg(target_os = "android")]
202207
pub fn unix_interfaces() -> Vec<Interface> {
208+
// Android is complicated
209+
210+
// API 24+ contains the getifaddrs and freeifaddrs functions but the NDK doesn't
211+
// expose those functions in ifaddrs.h when the minimum supported SDK is lower than 24
212+
// and therefore we need to load them manually.
213+
if let Some((getifaddrs, freeeifaddrs)) = android::get_libc_ifaddrs() {
214+
return unix_interfaces_inner(getifaddrs, freeifaddrs);
215+
}
216+
217+
// If API < 24 (or we can't load libc for some other reason), we fallback to using netlink
218+
android::netlink::unix_interfaces()
219+
}
220+
221+
pub fn unix_interfaces_inner(
222+
getifaddrs: unsafe extern "C" fn(ifap: *mut *mut libc::ifaddrs) -> libc::c_int,
223+
freeifaddrs: unsafe extern "C" fn(ifa: *mut libc::ifaddrs),
224+
) -> Vec<Interface> {
203225
let mut ifaces: Vec<Interface> = vec![];
204226
let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit();
205227
if unsafe { getifaddrs(addrs.as_mut_ptr()) } != 0 {
@@ -212,6 +234,7 @@ pub fn unix_interfaces() -> Vec<Interface> {
212234
let c_str = addr_ref.ifa_name as *const c_char;
213235
let bytes = unsafe { CStr::from_ptr(c_str).to_bytes() };
214236
let name = unsafe { from_utf8_unchecked(bytes).to_owned() };
237+
215238
let (mac, ip) = sockaddr_to_network_addr(addr_ref.ifa_addr as *const libc::sockaddr);
216239
let (_, netmask) = sockaddr_to_network_addr(addr_ref.ifa_netmask as *const libc::sockaddr);
217240
let mut ini_ipv4: Vec<Ipv4Net> = vec![];
@@ -308,17 +331,6 @@ pub fn unix_interfaces() -> Vec<Interface> {
308331
ifaces
309332
}
310333

311-
#[cfg(not(target_os = "android"))]
312-
unsafe fn getifaddrs(ifap: *mut *mut libc::ifaddrs) -> libc::c_int {
313-
// Not android, everything is easy
314-
libc::getifaddrs(ifap)
315-
}
316-
317-
#[cfg(not(target_os = "android"))]
318-
unsafe fn freeifaddrs(ifa: *mut libc::ifaddrs) {
319-
libc::freeifaddrs(ifa);
320-
}
321-
322334
#[cfg(test)]
323335
mod tests {
324336
use super::*;

0 commit comments

Comments
 (0)