302 lines
9.0 KiB
Diff
302 lines
9.0 KiB
Diff
diff --git a/src/header/ifaddrs/mod.rs b/src/header/ifaddrs/mod.rs
|
|
index bddb69b8..598beba3 100644
|
|
--- a/src/header/ifaddrs/mod.rs
|
|
+++ b/src/header/ifaddrs/mod.rs
|
|
@@ -3,7 +3,13 @@
|
|
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getifaddrs.3.html>.
|
|
|
|
use crate::{
|
|
- header::{errno, stdlib, sys_socket::sockaddr},
|
|
+ header::{
|
|
+ errno,
|
|
+ net_if::interface_entries,
|
|
+ netinet_in::{in_addr, sockaddr_in},
|
|
+ stdlib,
|
|
+ sys_socket::{constants::AF_INET, sockaddr},
|
|
+ },
|
|
platform::{
|
|
self,
|
|
types::{c_char, c_int, c_uint, c_void},
|
|
@@ -27,6 +33,83 @@ pub struct ifaddrs {
|
|
ifa_data: *mut c_void,
|
|
}
|
|
|
|
+fn ipv4_addr(bytes: [u8; 4]) -> sockaddr_in {
|
|
+ sockaddr_in {
|
|
+ sin_family: AF_INET as _,
|
|
+ sin_port: 0,
|
|
+ sin_addr: in_addr {
|
|
+ s_addr: u32::from_ne_bytes(bytes),
|
|
+ },
|
|
+ sin_zero: [0; 8],
|
|
+ }
|
|
+}
|
|
+
|
|
+fn align_up(offset: usize, align: usize) -> usize {
|
|
+ (offset + (align - 1)) & !(align - 1)
|
|
+}
|
|
+
|
|
+unsafe fn make_ifaddrs_node(entry: &crate::header::net_if::InterfaceEntry) -> *mut ifaddrs {
|
|
+ let name_len = entry.name.to_bytes_with_nul().len();
|
|
+ let addr_size = if entry.addr.is_some() { core::mem::size_of::<sockaddr_in>() } else { 0 };
|
|
+ let netmask_size = if entry.netmask.is_some() { core::mem::size_of::<sockaddr_in>() } else { 0 };
|
|
+ let addr_align = core::mem::align_of::<sockaddr_in>();
|
|
+ let mut total = core::mem::size_of::<ifaddrs>() + name_len;
|
|
+ if addr_size != 0 {
|
|
+ total = align_up(total, addr_align) + addr_size;
|
|
+ }
|
|
+ if netmask_size != 0 {
|
|
+ total = align_up(total, addr_align) + netmask_size;
|
|
+ }
|
|
+ let raw = unsafe { stdlib::malloc(total) } as *mut u8;
|
|
+ if raw.is_null() {
|
|
+ return core::ptr::null_mut();
|
|
+ }
|
|
+
|
|
+ unsafe { raw.write_bytes(0, total) };
|
|
+
|
|
+ let node = raw.cast::<ifaddrs>();
|
|
+ let mut cursor = unsafe { raw.add(core::mem::size_of::<ifaddrs>()) };
|
|
+
|
|
+ let name_ptr = cursor.cast::<c_char>();
|
|
+ unsafe { core::ptr::copy_nonoverlapping(entry.name.as_ptr(), name_ptr, name_len) };
|
|
+ cursor = unsafe { cursor.add(name_len) };
|
|
+
|
|
+ let addr_ptr = if let Some(addr) = entry.addr {
|
|
+ let aligned = align_up(cursor as usize, addr_align);
|
|
+ cursor = aligned as *mut u8;
|
|
+ let ptr = cursor.cast::<sockaddr_in>();
|
|
+ unsafe { ptr.write(ipv4_addr(addr)) };
|
|
+ cursor = unsafe { cursor.add(core::mem::size_of::<sockaddr_in>()) };
|
|
+ ptr.cast::<sockaddr>()
|
|
+ } else {
|
|
+ core::ptr::null_mut()
|
|
+ };
|
|
+
|
|
+ let netmask_ptr = if let Some(netmask) = entry.netmask {
|
|
+ let aligned = align_up(cursor as usize, addr_align);
|
|
+ cursor = aligned as *mut u8;
|
|
+ let ptr = cursor.cast::<sockaddr_in>();
|
|
+ unsafe { ptr.write(ipv4_addr(netmask)) };
|
|
+ ptr.cast::<sockaddr>()
|
|
+ } else {
|
|
+ core::ptr::null_mut()
|
|
+ };
|
|
+
|
|
+ unsafe {
|
|
+ node.write(ifaddrs {
|
|
+ ifa_next: core::ptr::null_mut(),
|
|
+ ifa_name: name_ptr,
|
|
+ ifa_flags: entry.flags,
|
|
+ ifa_addr: addr_ptr,
|
|
+ ifa_netmask: netmask_ptr,
|
|
+ ifa_ifu: ifaddrs_ifa_ifu { ifu_broadaddr: core::ptr::null_mut() },
|
|
+ ifa_data: core::ptr::null_mut(),
|
|
+ })
|
|
+ };
|
|
+
|
|
+ node
|
|
+}
|
|
+
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) {
|
|
while !ifa.is_null() {
|
|
@@ -38,7 +106,31 @@ pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) {
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int {
|
|
- //TODO: implement getifaddrs
|
|
- platform::ERRNO.set(errno::ENOSYS);
|
|
- -1
|
|
+ if ifap.is_null() {
|
|
+ platform::ERRNO.set(errno::EFAULT);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ let entries = interface_entries();
|
|
+ let mut head = core::ptr::null_mut();
|
|
+ let mut prev: *mut ifaddrs = core::ptr::null_mut();
|
|
+
|
|
+ for entry in &entries {
|
|
+ let node = unsafe { make_ifaddrs_node(entry) };
|
|
+ if node.is_null() {
|
|
+ unsafe { freeifaddrs(head) };
|
|
+ platform::ERRNO.set(errno::ENOMEM);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if head.is_null() {
|
|
+ head = node;
|
|
+ } else {
|
|
+ unsafe { (*prev).ifa_next = node };
|
|
+ }
|
|
+ prev = node;
|
|
+ }
|
|
+
|
|
+ unsafe { *ifap = head };
|
|
+ 0
|
|
}
|
|
diff --git a/src/header/net_if/mod.rs b/src/header/net_if/mod.rs
|
|
index edbfedec..bd07e2c9 100644
|
|
--- a/src/header/net_if/mod.rs
|
|
+++ b/src/header/net_if/mod.rs
|
|
@@ -21,21 +21,51 @@ pub struct if_nameindex {
|
|
if_name: *const c_char,
|
|
}
|
|
|
|
+#[derive(Clone)]
|
|
+pub(crate) struct InterfaceEntry {
|
|
+ pub index: c_uint,
|
|
+ pub name: CStr<'static>,
|
|
+ pub flags: c_uint,
|
|
+ pub addr: Option<[u8; 4]>,
|
|
+ pub netmask: Option<[u8; 4]>,
|
|
+}
|
|
+
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/net_if.h.html>.
|
|
pub const IF_NAMESIZE: usize = 16;
|
|
|
|
-const IF_STUB_INTERFACE: *const c_char = (c"stub").as_ptr();
|
|
+pub(crate) fn interface_entries() -> [InterfaceEntry; 2] {
|
|
+ [
|
|
+ InterfaceEntry {
|
|
+ index: 1,
|
|
+ name: c"loopback".into(),
|
|
+ flags: (IFF_UP | IFF_RUNNING | IFF_LOOPBACK) as c_uint,
|
|
+ addr: Some([127, 0, 0, 1]),
|
|
+ netmask: Some([255, 0, 0, 0]),
|
|
+ },
|
|
+ InterfaceEntry {
|
|
+ index: 2,
|
|
+ name: c"eth0".into(),
|
|
+ flags: (IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST) as c_uint,
|
|
+ addr: None,
|
|
+ netmask: None,
|
|
+ },
|
|
+ ]
|
|
+}
|
|
|
|
-const INTERFACES: &[if_nameindex] = &[
|
|
- if_nameindex {
|
|
- if_index: 1,
|
|
- if_name: IF_STUB_INTERFACE,
|
|
- },
|
|
- if_nameindex {
|
|
- if_index: 0,
|
|
- if_name: null::<c_char>(),
|
|
- },
|
|
-];
|
|
+const INTERFACES: [if_nameindex; 3] = [
|
|
+ if_nameindex {
|
|
+ if_index: 1,
|
|
+ if_name: (c"loopback").as_ptr(),
|
|
+ },
|
|
+ if_nameindex {
|
|
+ if_index: 2,
|
|
+ if_name: (c"eth0").as_ptr(),
|
|
+ },
|
|
+ if_nameindex {
|
|
+ if_index: 0,
|
|
+ if_name: null::<c_char>(),
|
|
+ },
|
|
+];
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/if_freenameindex.html>.
|
|
///
|
|
@@ -52,8 +85,21 @@ pub unsafe extern "C" fn if_freenameindex(s: *mut if_nameindex) {}
|
|
/// Currently only checks against inteface index 1.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn if_indextoname(idx: c_uint, buf: *mut c_char) -> *const c_char {
|
|
- if idx == 1 {
|
|
- return IF_STUB_INTERFACE;
|
|
+ let entries = interface_entries();
|
|
+ for entry in &entries {
|
|
+ if entry.index == idx {
|
|
+ if !buf.is_null() {
|
|
+ unsafe {
|
|
+ core::ptr::copy_nonoverlapping(
|
|
+ entry.name.as_ptr(),
|
|
+ buf,
|
|
+ entry.name.to_bytes_with_nul().len(),
|
|
+ );
|
|
+ }
|
|
+ return buf;
|
|
+ }
|
|
+ return entry.name.as_ptr();
|
|
+ }
|
|
}
|
|
ERRNO.set(ENXIO);
|
|
null::<c_char>()
|
|
@@ -78,9 +136,12 @@ pub unsafe extern "C" fn if_nametoindex(name: *const c_char) -> c_uint {
|
|
if name.is_null() {
|
|
return 0;
|
|
}
|
|
let name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("") };
|
|
- if name.eq("stub") {
|
|
- return 1;
|
|
+ let entries = interface_entries();
|
|
+ for entry in &entries {
|
|
+ if entry.name.to_str().map(|entry_name| entry_name == name).unwrap_or(false) {
|
|
+ return entry.index;
|
|
+ }
|
|
}
|
|
0
|
|
}
|
|
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
|
--- a/tests/Makefile.tests.mk
|
|
+++ b/tests/Makefile.tests.mk
|
|
@@ -63,6 +63,7 @@ VARIED_NAMES=\
|
|
features \
|
|
fnmatch \
|
|
glob \
|
|
+ ifaddrs/getifaddrs \
|
|
iso646 \
|
|
libgen \
|
|
locale/duplocale \
|
|
diff --git a/tests/ifaddrs/getifaddrs.c b/tests/ifaddrs/getifaddrs.c
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/tests/ifaddrs/getifaddrs.c
|
|
@@ -0,0 +1,41 @@
|
|
+#include <assert.h>
|
|
+#include <ifaddrs.h>
|
|
+#include <net/if.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+
|
|
+int main(void) {
|
|
+ struct ifaddrs *list = NULL;
|
|
+ struct ifaddrs *it;
|
|
+ const struct if_nameindex *names;
|
|
+ char name[IF_NAMESIZE] = {0};
|
|
+ int saw_loopback = 0;
|
|
+ int saw_eth0 = 0;
|
|
+
|
|
+ assert(getifaddrs(&list) == 0);
|
|
+ for (it = list; it != NULL; it = it->ifa_next) {
|
|
+ assert(it->ifa_name != NULL);
|
|
+ if (strcmp(it->ifa_name, "loopback") == 0) {
|
|
+ saw_loopback = 1;
|
|
+ assert((it->ifa_flags & IFF_LOOPBACK) != 0);
|
|
+ } else if (strcmp(it->ifa_name, "eth0") == 0) {
|
|
+ saw_eth0 = 1;
|
|
+ assert(if_nametoindex("eth0") == 2);
|
|
+ assert(if_indextoname(2, name) == name);
|
|
+ assert(strcmp(name, "eth0") == 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ names = if_nameindex();
|
|
+ assert(names != NULL);
|
|
+ assert(names[0].if_index == 1 && strcmp(names[0].if_name, "loopback") == 0);
|
|
+ assert(names[1].if_index == 2 && strcmp(names[1].if_name, "eth0") == 0);
|
|
+ assert(names[2].if_index == 0 && names[2].if_name == NULL);
|
|
+
|
|
+ assert(saw_loopback != 0);
|
|
+ assert(saw_eth0 != 0);
|
|
+ if_freenameindex((struct if_nameindex *)names);
|
|
+ freeifaddrs(list);
|
|
+ puts("getifaddrs ok");
|
|
+ return 0;
|
|
+}
|