Impl routing table fn for macOS
main
shellrow 1 year ago
parent 943939c5c9
commit 8a77d9726e
  1. 2
      Cargo.toml
  2. 29
      src/bpf/binding.rs
  3. 41
      src/bpf/mod.rs
  4. 16
      src/bpf/shared.rs
  5. 464
      src/gateway/macos.rs
  6. 17
      src/gateway/mod.rs
  7. 4
      src/interface/unix.rs
  8. 4
      src/lib.rs
  9. 22
      src/socket/mod.rs

@ -30,4 +30,4 @@ version = "0.48.0"
features = ["Win32_Foundation","Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_NetworkManagement_Ndis"]
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
system-configuration = "0.5.0"
system-configuration = "0.5.1"

@ -4,7 +4,6 @@
use libc;
pub type SockAddr = libc::sockaddr;
pub const AF_LINK: libc::c_int = 18;
const IF_NAMESIZE: usize = 16;
const IFNAMSIZ: usize = IF_NAMESIZE;
@ -44,7 +43,7 @@ pub const DLT_NULL: libc::c_uint = 0;
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
const BPF_ALIGNMENT: libc::c_int = SIZEOF_C_LONG;
#[cfg(any(target_os = "openbsd", target_os = "macos", target_os = "ios", windows))]
#[cfg(any(target_os = "openbsd"))]
const BPF_ALIGNMENT: libc::c_int = 4;
pub fn BPF_WORDALIGN(x: isize) -> isize {
@ -57,24 +56,6 @@ pub struct ifreq {
pub ifru_addr: SockAddr,
}
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd",
target_os = "macos",
target_os = "ios"
))]
pub struct sockaddr_dl {
pub sdl_len: libc::c_uchar,
pub sdl_family: libc::c_uchar,
pub sdl_index: libc::c_ushort,
pub sdl_type: libc::c_uchar,
pub sdl_nlen: libc::c_uchar,
pub sdl_alen: libc::c_uchar,
pub sdl_slen: libc::c_uchar,
pub sdl_data: [libc::c_char; 46],
}
#[cfg(any(
target_os = "freebsd",
target_os = "netbsd",
@ -97,13 +78,6 @@ pub struct timeval32 {
pub tv_usec: i32,
}
#[cfg(any(
target_os = "openbsd",
all(
any(target_os = "macos", target_os = "ios"),
target_pointer_width = "64"
)
))]
#[repr(C)]
pub struct bpf_hdr {
pub bh_tstamp: timeval32,
@ -112,7 +86,6 @@ pub struct bpf_hdr {
pub bh_hdrlen: libc::c_ushort,
}
#[cfg(not(windows))]
extern "C" {
pub fn ioctl(d: libc::c_int, request: libc::c_ulong, ...) -> libc::c_int;
}

@ -1,9 +1,42 @@
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
mod binding;
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
pub use self::binding::*;
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
mod unix;
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
pub use self::unix::*;
#[cfg(any(
target_os = "macos",
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd",
target_os = "ios"
))]
mod shared;
#[cfg(any(
target_os = "macos",
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd",
target_os = "ios"
))]
pub use self::shared::*;

@ -0,0 +1,16 @@
#![allow(non_camel_case_types)]
use libc;
pub const AF_LINK: libc::c_int = 18;
pub struct sockaddr_dl {
pub sdl_len: libc::c_uchar,
pub sdl_family: libc::c_uchar,
pub sdl_index: libc::c_ushort,
pub sdl_type: libc::c_uchar,
pub sdl_nlen: libc::c_uchar,
pub sdl_alen: libc::c_uchar,
pub sdl_slen: libc::c_uchar,
pub sdl_data: [libc::c_char; 46],
}

@ -0,0 +1,464 @@
#![allow(non_camel_case_types)]
use super::Gateway;
use crate::interface::MacAddr;
use std::{
io,
net::{IpAddr, Ipv4Addr, Ipv6Addr}, collections::HashMap,
};
const CTL_NET: u32 = 4;
const AF_INET: u32 = 2;
const AF_ROUTE: u32 = 17;
const AF_LINK: u32 = 18;
const AF_INET6: u32 = 30;
const PF_ROUTE: u32 = 17;
const NET_RT_DUMP: u32 = 1;
const NET_RT_FLAGS: u32 = 2;
const RTM_VERSION: u32 = 5;
const RTF_LLINFO: u32 = 1024;
const RTF_WASCLONED: u32 = 131072;
const RTAX_DST: u32 = 0;
const RTAX_GATEWAY: u32 = 1;
const RTAX_NETMASK: u32 = 2;
type __int32_t = ::std::os::raw::c_int;
type __uint8_t = ::std::os::raw::c_uchar;
type __uint16_t = ::std::os::raw::c_ushort;
type __uint32_t = ::std::os::raw::c_uint;
type __darwin_size_t = ::std::os::raw::c_ulong;
type __darwin_pid_t = __int32_t;
type sa_family_t = __uint8_t;
type in_addr_t = __uint32_t;
type in_port_t = __uint16_t;
type u_int = ::std::os::raw::c_uint;
type u_short = ::std::os::raw::c_ushort;
type u_char = ::std::os::raw::c_uchar;
type u_int32_t = ::std::os::raw::c_uint;
type size_t = __darwin_size_t;
type pid_t = __darwin_pid_t;
extern "C" {
fn sysctl(
arg1: *mut ::std::os::raw::c_int,
arg2: u_int,
arg3: *mut ::std::os::raw::c_void,
arg4: *mut size_t,
arg5: *mut ::std::os::raw::c_void,
arg6: size_t,
) -> ::std::os::raw::c_int;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct rt_msghdr {
pub rtm_msglen: u_short,
pub rtm_version: u_char,
pub rtm_type: u_char,
pub rtm_index: u_short,
pub rtm_flags: ::std::os::raw::c_int,
pub rtm_addrs: ::std::os::raw::c_int,
pub rtm_pid: pid_t,
pub rtm_seq: ::std::os::raw::c_int,
pub rtm_errno: ::std::os::raw::c_int,
pub rtm_use: ::std::os::raw::c_int,
pub rtm_inits: u_int32_t,
pub rtm_rmx: rt_metrics,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct rt_metrics {
pub rmx_locks: u_int32_t,
pub rmx_mtu: u_int32_t,
pub rmx_hopcount: u_int32_t,
pub rmx_expire: i32,
pub rmx_recvpipe: u_int32_t,
pub rmx_sendpipe: u_int32_t,
pub rmx_ssthresh: u_int32_t,
pub rmx_rtt: u_int32_t,
pub rmx_rttvar: u_int32_t,
pub rmx_pksent: u_int32_t,
pub rmx_state: u_int32_t,
pub rmx_filler: [u_int32_t; 3usize],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct sockaddr {
pub sa_len: __uint8_t,
pub sa_family: sa_family_t,
pub sa_data: [::std::os::raw::c_char; 14usize],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct sockaddr_dl {
pub sdl_len: u_char,
pub sdl_family: u_char,
pub sdl_index: u_short,
pub sdl_type: u_char,
pub sdl_nlen: u_char,
pub sdl_alen: u_char,
pub sdl_slen: u_char,
pub sdl_data: [::std::os::raw::c_char; 12usize],
}
#[repr(C)]
#[derive(Copy, Clone)]
union in6_addr_bind {
pub __u6_addr8: [__uint8_t; 16usize],
pub __u6_addr16: [__uint16_t; 8usize],
pub __u6_addr32: [__uint32_t; 4usize],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct in_addr {
pub s_addr: in_addr_t,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct in6_addr {
pub __u6_addr: in6_addr_bind,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct sockaddr_in {
pub sin_len: __uint8_t,
pub sin_family: sa_family_t,
pub sin_port: in_port_t,
pub sin_addr: in_addr,
pub sin_zero: [::std::os::raw::c_char; 8usize],
}
#[repr(C)]
#[derive(Copy, Clone)]
struct sockaddr_in6 {
pub sin6_len: __uint8_t,
pub sin6_family: sa_family_t,
pub sin6_port: in_port_t,
pub sin6_flowinfo: __uint32_t,
pub sin6_addr: in6_addr,
pub sin6_scope_id: __uint32_t,
}
fn code_to_error(err: i32) -> io::Error {
let kind = match err {
17 => io::ErrorKind::AlreadyExists, // EEXIST
3 => io::ErrorKind::NotFound, // ESRCH
3436 => io::ErrorKind::OutOfMemory, // ENOBUFS
_ => io::ErrorKind::Other,
};
io::Error::new(kind, format!("rtm_errno {}", err))
}
unsafe fn sa_to_ip(sa: &sockaddr) -> Option<IpAddr> {
match sa.sa_family as u32 {
AF_INET=> {
let inet: &sockaddr_in = std::mem::transmute(sa);
let octets: [u8; 4] = inet.sin_addr.s_addr.to_ne_bytes();
Some(IpAddr::from(octets))
}
AF_INET6=> {
let inet6: &sockaddr_in6 = std::mem::transmute(sa);
let octets: [u8; 16] = inet6.sin6_addr.__u6_addr.__u6_addr8;
Some(IpAddr::from(octets))
}
AF_LINK => None,
_ => None,
}
}
unsafe fn sa_to_link(sa: &sockaddr) -> Option<(Option<[u8; 6]>, u16)> {
match sa.sa_family as u32 {
AF_LINK => {
let sa_dl = sa as *const _ as *const sockaddr_dl;
let ifindex = (*sa_dl).sdl_index;
let mac;
if (*sa_dl).sdl_alen >= 6 {
let i = (*sa_dl).sdl_nlen as usize;
let a = (*sa_dl).sdl_data[i + 0] as u8;
let b = (*sa_dl).sdl_data[i + 1] as u8;
let c = (*sa_dl).sdl_data[i + 2] as u8;
let d = (*sa_dl).sdl_data[i + 3] as u8;
let e = (*sa_dl).sdl_data[i + 4] as u8;
let f = (*sa_dl).sdl_data[i + 5] as u8;
mac = Some([a, b, c, d, e, f]);
} else {
mac = None;
}
Some((mac, ifindex))
}
_ => None,
}
}
fn message_to_route(hdr: &rt_msghdr, msg: *mut u8) -> Option<Route> {
let destination;
let mut gateway = None;
let mut ifindex = None;
if hdr.rtm_addrs & (1 << RTAX_DST) == 0 {
return None;
}
unsafe {
let dst_sa: &sockaddr = std::mem::transmute((msg as *mut sockaddr).add(RTAX_DST as usize));
destination = sa_to_ip(dst_sa)?;
}
let mut prefix = match destination {
IpAddr::V4(_) => 32,
IpAddr::V6(_) => 128,
};
if hdr.rtm_addrs & (1 << RTAX_GATEWAY) != 0 {
unsafe {
let gw_sa: &sockaddr =
std::mem::transmute((msg as *mut sockaddr).add(RTAX_GATEWAY as usize));
gateway = sa_to_ip(gw_sa);
if gateway.is_none() {
if let Some((_mac, ifidx)) = sa_to_link(gw_sa) {
ifindex = Some(ifidx as u32);
}
}
}
}
if hdr.rtm_addrs & (1 << RTAX_NETMASK) != 0 {
unsafe {
match destination {
IpAddr::V4(_) => {
let mask_sa: &sockaddr_in =
std::mem::transmute((msg as *mut sockaddr).add(RTAX_NETMASK as usize));
let octets: [u8; 4] = mask_sa.sin_addr.s_addr.to_ne_bytes();
prefix = u32::from_be_bytes(octets).leading_ones() as u8;
}
IpAddr::V6(_) => {
let mask_sa: &sockaddr_in6 =
std::mem::transmute((msg as *mut sockaddr).add(RTAX_NETMASK as usize));
let octets: [u8; 16] = mask_sa.sin6_addr.__u6_addr.__u6_addr8;
prefix = u128::from_be_bytes(octets).leading_ones() as u8;
}
}
}
}
Some(Route {
destination,
prefix,
gateway,
ifindex,
})
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Route {
pub destination: IpAddr,
pub prefix: u8,
pub gateway: Option<IpAddr>,
pub ifindex: Option<u32>,
}
fn list_routes() -> io::Result<Vec<Route>> {
let mut mib: [u32; 6] = [0; 6];
let mut len = 0;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = 0; // AddressFamily: IPv4 & IPv6
mib[4] = NET_RT_DUMP;
// mib[5] flags: 0
if unsafe {
sysctl(
&mut mib as *mut _ as *mut _,
6,
std::ptr::null_mut(),
&mut len,
std::ptr::null_mut(),
0,
)
} < 0
{
return Err(io::Error::last_os_error());
}
let mut msgs_buf: Vec<u8> = vec![0; len as usize];
if unsafe {
sysctl(
&mut mib as *mut _ as *mut _,
6,
msgs_buf.as_mut_ptr() as _,
&mut len,
std::ptr::null_mut(),
0,
)
} < 0
{
return Err(io::Error::last_os_error());
}
let mut routes = vec![];
let mut offset = 0;
loop {
let buf = &mut msgs_buf[offset..];
if buf.len() < std::mem::size_of::<rt_msghdr>() {
break;
}
let rt_hdr = unsafe { std::mem::transmute::<_, &rt_msghdr>(buf.as_ptr()) };
assert_eq!(rt_hdr.rtm_version as u32, RTM_VERSION);
if rt_hdr.rtm_errno != 0 {
return Err(code_to_error(rt_hdr.rtm_errno));
}
let msg_len = rt_hdr.rtm_msglen as usize;
offset += msg_len;
if rt_hdr.rtm_flags as u32 & RTF_WASCLONED != 0 {
continue;
}
let rt_msg = &mut buf[std::mem::size_of::<rt_msghdr>()..msg_len];
if let Some(route) = message_to_route(rt_hdr, rt_msg.as_mut_ptr()) {
routes.push(route);
}
}
Ok(routes)
}
fn message_to_arppair(msg_bytes: *mut u8) -> (IpAddr, MacAddr) {
const IP_START_INDEX: usize = 4;
const IP_END_INDEX: usize = 7;
const MAC_START_INDEX: usize = 24;
const MAC_END_INDEX: usize = 29;
let ip_bytes = unsafe { std::slice::from_raw_parts(msg_bytes.add(IP_START_INDEX), IP_END_INDEX + 1 - IP_START_INDEX) };
let mac_bytes = unsafe { std::slice::from_raw_parts(msg_bytes.add(MAC_START_INDEX), MAC_END_INDEX + 1 - MAC_START_INDEX) };
let ip_addr = IpAddr::V4(Ipv4Addr::new(ip_bytes[0], ip_bytes[1], ip_bytes[2], ip_bytes[3]));
let mac_addr = MacAddr::new([mac_bytes[0], mac_bytes[1], mac_bytes[2], mac_bytes[3], mac_bytes[4], mac_bytes[5]]);
(ip_addr, mac_addr)
}
fn get_arp_table() -> io::Result<HashMap<IpAddr, MacAddr>> {
let mut arp_map: HashMap<IpAddr, MacAddr> = HashMap::new();
let mut mib: [u32; 6] = [0; 6];
let mut len = 0;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
mib[5] = RTF_LLINFO;
if unsafe {
sysctl(
&mut mib as *mut _ as *mut _,
6,
std::ptr::null_mut(),
&mut len,
std::ptr::null_mut(),
0,
)
} < 0
{
return Err(io::Error::last_os_error());
}
let mut msgs_buf: Vec<u8> = vec![0; len as usize];
if unsafe {
sysctl(
&mut mib as *mut _ as *mut _,
6,
msgs_buf.as_mut_ptr() as _,
&mut len,
std::ptr::null_mut(),
0,
)
} < 0
{
return Err(io::Error::last_os_error());
}
let mut offset = 0;
loop {
let buf = &mut msgs_buf[offset..];
if buf.len() < std::mem::size_of::<rt_msghdr>() {
break;
}
let rt_hdr = unsafe { std::mem::transmute::<_, &rt_msghdr>(buf.as_ptr()) };
assert_eq!(rt_hdr.rtm_version as u32, RTM_VERSION);
if rt_hdr.rtm_errno != 0 {
return Err(code_to_error(rt_hdr.rtm_errno));
}
let msg_len = rt_hdr.rtm_msglen as usize;
offset += msg_len;
let rt_msg: &mut [u8] = &mut buf[std::mem::size_of::<rt_msghdr>()..msg_len];
let (ip, mac) = message_to_arppair(rt_msg.as_mut_ptr());
arp_map.insert(ip, mac);
}
Ok(arp_map)
}
fn get_default_route() -> Option<Route> {
match list_routes() {
Ok(routes) => {
for route in routes {
if (route.destination == Ipv4Addr::UNSPECIFIED
|| route.destination == Ipv6Addr::UNSPECIFIED)
&& route.prefix == 0
&& route.gateway != Some(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
&& route.gateway != Some(IpAddr::V6(Ipv6Addr::UNSPECIFIED))
{
return Some(route);
}
}
}
Err(_) => {},
}
None
}
pub fn get_default_gateway(_interface_name: String) -> Result<Gateway, String> {
if let Some(route) = get_default_route(){
if let Some(gw_ip) = route.gateway {
match get_arp_table() {
Ok(arp_map) => {
if let Some(mac_addr) = arp_map.get(&gw_ip) {
let gateway = Gateway {
mac_addr: mac_addr.clone(),
ip_addr: gw_ip,
};
return Ok(gateway);
}
}
Err(_) => {}
}
let gateway = Gateway {
mac_addr: MacAddr::zero(),
ip_addr: gw_ip,
};
return Ok(gateway);
}else {
return Err(format!("Failed to get gateway IP address"));
}
}else{
return Err(format!("Failed to get default route"));
}
}

@ -1,12 +1,16 @@
#[cfg(any(
target_os = "macos",
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd",
target_os = "ios"
target_os = "netbsd"
))]
pub(crate) mod unix;
#[cfg(any(
target_os = "macos",
target_os = "ios"
))]
pub(crate) mod macos;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) mod linux;
@ -60,7 +64,12 @@ pub fn get_default_gateway() -> Result<Gateway, String> {
Err(String::from("Default Gateway not found"))
}
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "linux",
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
fn send_udp_packet() -> Result<(), String> {
use std::net::UdpSocket;
let buf = [0u8; 0];

@ -60,7 +60,7 @@ pub fn interfaces() -> Vec<Interface> {
match local_ip {
IpAddr::V4(local_ipv4) => {
if iface.ipv4.iter().any(|x| x.addr == local_ipv4) {
match gateway::unix::get_default_gateway(iface.name.clone()) {
match gateway::macos::get_default_gateway(iface.name.clone()) {
Ok(gateway) => {
iface.gateway = Some(gateway);
}
@ -70,7 +70,7 @@ pub fn interfaces() -> Vec<Interface> {
}
IpAddr::V6(local_ipv6) => {
if iface.ipv6.iter().any(|x| x.addr == local_ipv6) {
match gateway::unix::get_default_gateway(iface.name.clone()) {
match gateway::macos::get_default_gateway(iface.name.clone()) {
Ok(gateway) => {
iface.gateway = Some(gateway);
}

@ -7,11 +7,9 @@
))]
mod bpf;
#[cfg(any(
target_os = "macos",
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd",
target_os = "ios"
target_os = "netbsd"
))]
mod socket;
#[cfg(not(target_os = "windows"))]

@ -1,10 +1,28 @@
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
pub mod packet;
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
mod unix;
#[cfg(not(target_os = "windows"))]
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
pub use self::unix::*;
#[cfg(any(
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))]
#[cfg(test)]
mod tests {
use super::*;

Loading…
Cancel
Save