Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions luomu-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ rust-version.workspace = true
homepage.workspace = true
repository.workspace = true

[features]
default = []

[dependencies]
libc = { version = "0.2", optional = true }

[dev-dependencies]
quickcheck.workspace = true

Expand Down
3 changes: 3 additions & 0 deletions luomu-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ pub use tagged_macaddr::TaggedMacAddr;
/// forwardable
pub mod ipaddr;

#[cfg(feature = "libc")]
pub mod sockaddr;

/// Invalid address error
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InvalidAddress;
Expand Down
118 changes: 118 additions & 0 deletions luomu-common/src/sockaddr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#![allow(unsafe_code)]
//! Utilities to handle [libc::sockaddr] and related structs.

use std::net::{Ipv4Addr, Ipv6Addr};

use crate::Address;

/// Length of MAC address in bytes
const MAC_ADDR_LEN: usize = 6;

/// Reads address information from given socket address pointer.
///
/// Address may contain IP address or Link address, the returned [Address]
/// reflects which one was read. [None] is returned if no address was available,
/// or it could not be read.
// We do our best to validate that the pointer given in `ifa_addr` is valid.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn from_sockaddr(ifa_addr: *const libc::sockaddr) -> Option<Address> {
if ifa_addr.is_null() {
return None;
}

let family = i32::from(unsafe { (*ifa_addr).sa_family });

#[cfg(target_os = "macos")]
let sa_len = usize::from(unsafe { (*ifa_addr).sa_len });

match family {
#[cfg(target_os = "macos")]
libc::AF_INET if sa_len < 16 => {
debug_assert!((5..=8).contains(&sa_len), "invalid sa_len {sa_len} for AF_INET");
// We keep ifa_addr as sockaddr, but read it like it's sockaddr_in:
//
// pub struct sockaddr_in {
// pub sin_len: u8,
// pub sin_family: u8,
// pub sin_port: u16, \ These two are in sockaddr.sa_data
// pub sin_addr: u32, /
// pub sin_zero: [c_char; 8],
// }
//
// We want to read the partial bytes from sin_addr, the length is
// dictated by sa_len. sockaddr.sa_data contains first sin_port
// which we skip and then sin_addr.
let len = sa_len.saturating_sub(2); // remove length of sin_len and sin_family
let mut iret = [0i8; 4];
let sa_data: &[i8] = unsafe { std::slice::from_raw_parts((*ifa_addr).sa_data.as_ptr(), len) };
iret[0..len.saturating_sub(2)].copy_from_slice(&sa_data[2..len]);
let uret: [u8; 4] = unsafe { std::mem::transmute(iret) };
Some(Address::from(Ipv4Addr::from_octets(uret)))
}

libc::AF_INET => {
#[cfg(target_os = "macos")]
debug_assert_eq!(sa_len, 16, "invalid sa_len {sa_len} for AF_INET");
let a: libc::sockaddr_in = unsafe { *(ifa_addr.cast::<libc::sockaddr_in>()) };
Some(Address::from(Ipv4Addr::from_bits(u32::from_be(
a.sin_addr.s_addr,
))))
}

libc::AF_INET6 => {
#[cfg(target_os = "macos")]
debug_assert_eq!(sa_len, 28, "invalid sa_len {sa_len} for AF_INET6");
let a: libc::sockaddr_in6 = unsafe { *(ifa_addr.cast::<libc::sockaddr_in6>()) };
Some(Address::from(Ipv6Addr::from_octets(a.sin6_addr.s6_addr)))
}

#[cfg(target_os = "macos")]
libc::AF_LINK => {
debug_assert!(
sa_len == 20 || sa_len == 24,
"invalid sa_len {sa_len} for AF_LINK"
);
// MAC address for this interface
let a: libc::sockaddr_dl = unsafe { *(ifa_addr.cast::<libc::sockaddr_dl>()) };
// length of the address
let a_len = usize::from(a.sdl_alen);
// length of the name
let n_len = usize::from(a.sdl_nlen);
// If seems that name is stored to sdl_data before the mac
// address of the interface. However, libc::sockaddr_dl::sdl_data has been
// defined to contain 12 bytes. Thus, if name of the interface
// is longer than 6 bytes (characters), we can not read the MAC
// address of that interface.
if a_len != MAC_ADDR_LEN || n_len + a_len > a.sdl_data.len() {
return None;
}
// also, sdl_data has been defined as i8 for whatever reason,
// we need bytes for mac address, thus a bit of unsafery
let data = &a.sdl_data;
let sdl_data_as_u8: &[u8] =
unsafe { std::slice::from_raw_parts(data.as_ptr().cast::<u8>(), data.len()) };
let mut address = [0u8; MAC_ADDR_LEN];
// mac address stored after name
// You may want to look into LLADDR() macro somewhere on Mac OS headers
let offset = usize::from(a.sdl_nlen);
address[..MAC_ADDR_LEN].copy_from_slice(&sdl_data_as_u8[offset..offset + MAC_ADDR_LEN]);
Some(Address::from(address))
}

#[cfg(target_os = "linux")]
libc::AF_PACKET => {
// Mac address of the interface
let a: libc::sockaddr_ll = unsafe { *(ifa_addr.cast::<libc::sockaddr_ll>()) };
let a_len = usize::from(a.sll_halen);
debug_assert!(a_len == MAC_ADDR_LEN);
if a_len != MAC_ADDR_LEN {
return None;
}
let mut address = [0u8; MAC_ADDR_LEN];
address[..MAC_ADDR_LEN].copy_from_slice(&a.sll_addr[..MAC_ADDR_LEN]);
Some(Address::from(address))
}

_ => None,
}
}
2 changes: 1 addition & 1 deletion luomu-getifaddrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repository.workspace = true
bitflags.workspace = true
cfg-if.workspace = true
libc.workspace = true
luomu-common.workspace = true
luomu-common = { workspace = true, features = [ "libc" ] }

[lints]
workspace = true
106 changes: 7 additions & 99 deletions luomu-getifaddrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,17 @@
use std::ffi::CStr;
use std::io;
use std::mem::MaybeUninit;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::IpAddr;

use luomu_common::sockaddr::from_sockaddr;
use luomu_common::{Address, MacAddr};

#[cfg(target_os = "macos")]
use std::slice;

mod flags;
pub use flags::Flags;

mod stats;
pub use stats::IfStats;

/// Length of MAC address in bytes
const MAC_ADDR_LEN: usize = 6;

/// Returns a linked list describing the network interfaces of
/// the local system.
pub fn getifaddrs() -> io::Result<IfAddrs> {
Expand Down Expand Up @@ -117,27 +112,6 @@ impl Iterator for IfAddrs {
}
}

/// Returns address family value from [libc::sockaddr]
const fn sa_get_family(sa: *const libc::sockaddr) -> i32 {
unsafe { *sa }.sa_family as libc::c_int
}

/// Returns given [libc::sockaddr] pointer as a pointer to [libc::sockaddr_in]
const fn sa_as_sockaddr_in(sa: *const libc::sockaddr) -> libc::sockaddr_in {
unsafe { *(sa.cast::<libc::sockaddr_in>()) }
}

/// Returns given [libc::sockaddr] pointer as a pointer to [libc::sockaddr_in6]
const fn sa_as_sockaddr_in6(sa: *const libc::sockaddr) -> libc::sockaddr_in6 {
unsafe { *(sa.cast::<libc::sockaddr_in6>()) }
}

#[cfg(target_os = "macos")]
/// Returns given [libc::sockaddr] pointer as a pointer to [libc::sockaddr_dl]
const fn sa_as_sockaddr_dl(sa: *const libc::sockaddr) -> libc::sockaddr_dl {
unsafe { *(sa.cast::<libc::sockaddr_dl>()) }
}

/// This struct provides access to information about network interface.
pub struct IfAddr {
/// pointer for the interface data
Expand Down Expand Up @@ -179,21 +153,21 @@ impl IfAddr {

/// Interface address
pub fn addr(&self) -> Option<Address> {
IfAddr::read_addr(self.ifa().ifa_addr)
from_sockaddr(self.ifa().ifa_addr)
}

/// Interface netmask
pub fn netmask(&self) -> Option<IpAddr> {
IfAddr::read_addr(self.ifa().ifa_netmask).and_then(|a| a.as_ip())
from_sockaddr(self.ifa().ifa_netmask).and_then(|a| a.as_ip())
}

/// Destination address for Point-to-Point link
pub fn dstaddr(&self) -> Option<IpAddr> {
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
IfAddr::read_addr(self.ifa().ifa_dstaddr).and_then(|a| a.as_ip())
from_sockaddr(self.ifa().ifa_dstaddr).and_then(|a| a.as_ip())
} else if #[cfg(target_os = "linux")] {
IfAddr::read_addr(self.ifa().ifa_ifu).and_then(|a| a.as_ip())
from_sockaddr(self.ifa().ifa_ifu).and_then(|a| a.as_ip())
}
}
}
Expand All @@ -204,7 +178,7 @@ impl IfAddr {
return None;
}

let family = sa_get_family(self.ifa().ifa_addr);
let family = i32::from(unsafe { *self.ifa().ifa_addr }.sa_family);

#[cfg(target_os = "linux")]
if family != libc::AF_PACKET {
Expand All @@ -220,72 +194,6 @@ impl IfAddr {
let link_stats = unsafe { &*(ifa_data as *const stats::LinkStats) };
Some(*link_stats)
}

/// Reads address information from given socket address pointer.
///
/// Address may contain IP address or Link address, the returned
/// [Addr] reflects which one was read. [None] is returned if no address
/// was available, or it could not be read.
fn read_addr(ifa_addr: *const libc::sockaddr) -> Option<Address> {
if ifa_addr.is_null() {
return None;
}

let family = sa_get_family(ifa_addr);

match family {
libc::AF_INET => {
let a: libc::sockaddr_in = sa_as_sockaddr_in(ifa_addr);
Some(Address::from(Ipv4Addr::from(u32::from_be(a.sin_addr.s_addr))))
}
libc::AF_INET6 => {
let a: libc::sockaddr_in6 = sa_as_sockaddr_in6(ifa_addr);
Some(Address::from(Ipv6Addr::from(a.sin6_addr.s6_addr)))
}
#[cfg(target_os = "macos")]
libc::AF_LINK => {
// MAC address for this interface
let a: libc::sockaddr_dl = sa_as_sockaddr_dl(ifa_addr);
// length of the address
let a_len = usize::from(a.sdl_alen);
// length of the name
let n_len = usize::from(a.sdl_nlen);
// If seems that name is stored to sdl_data before the mac
// address of the interface. However, libc::sockaddr_dl::sdl_data has been
// defined to contain 12 bytes. Thus, if name of the interface
// is longer than 6 bytes (characters), we can not read the MAC
// address of that interface.
if a_len != MAC_ADDR_LEN || n_len + a_len > a.sdl_data.len() {
return None;
}
// also, sdl_data has been defined as i8 for whatever reason,
// we need bytes for mac address, thus a bit of unsafery
let data = &a.sdl_data;
let sdl_data_as_u8: &[u8] =
unsafe { slice::from_raw_parts(data.as_ptr().cast::<u8>(), data.len()) };
let mut address = [0u8; MAC_ADDR_LEN];
// mac address stored after name
// You may want to look into LLADDR() macro somewhere on Mac OS headers
let offset = usize::from(a.sdl_nlen);
address[..MAC_ADDR_LEN].copy_from_slice(&sdl_data_as_u8[offset..offset + MAC_ADDR_LEN]);
Some(Address::from(address))
}
#[cfg(target_os = "linux")]
libc::AF_PACKET => {
// Mac address of the interface
let a: libc::sockaddr_ll = unsafe { *(ifa_addr.cast::<libc::sockaddr_ll>()) };
let a_len = usize::from(a.sll_halen);
debug_assert!(a_len == MAC_ADDR_LEN);
if a_len != MAC_ADDR_LEN {
return None;
}
let mut address = [0u8; MAC_ADDR_LEN];
address[..MAC_ADDR_LEN].copy_from_slice(&a.sll_addr[..MAC_ADDR_LEN]);
Some(Address::from(address))
}
_ => None,
}
}
}

/// A view into interface data
Expand Down
2 changes: 1 addition & 1 deletion luomu-libpcap-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::process::Command;

use tar::Archive;

static VERSION: &str = "1.10.5";
static VERSION: &str = "1.10.6";

fn main() -> io::Result<()> {
let out_dir = env::var("OUT_DIR").expect("environment variable OUT_DIR");
Expand Down
2 changes: 1 addition & 1 deletion luomu-libpcap-sys/generate.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh -e

LIBPCAP='libpcap-1.10.5'
LIBPCAP='libpcap-1.10.6'

./verify.sh "${LIBPCAP}.tar.xz"

Expand Down
Binary file removed luomu-libpcap-sys/libpcap-1.10.5.tar.xz
Binary file not shown.
Binary file removed luomu-libpcap-sys/libpcap-1.10.5.tar.xz.sig
Binary file not shown.
Binary file added luomu-libpcap-sys/libpcap-1.10.6.tar.xz
Binary file not shown.
Binary file added luomu-libpcap-sys/libpcap-1.10.6.tar.xz.sig
Binary file not shown.
Loading
Loading