Skip to content

Instantly share code, notes, and snippets.

@ssrlive
Created October 2, 2023 16:08
Show Gist options
  • Save ssrlive/8d73616337bf984dced666d76bd5147f to your computer and use it in GitHub Desktop.
Save ssrlive/8d73616337bf984dced666d76bd5147f to your computer and use it in GitHub Desktop.
macos gateway
use libc::{
c_int, c_long, c_uint, c_void, freeifaddrs, getifaddrs, ifaddrs, sockaddr, sockaddr_in,
sockaddr_in6, AF_INET, AF_INET6, IFF_BROADCAST, IFF_LOOPBACK, IFF_MULTICAST, IFF_RUNNING,
IFF_UP, RTF_GATEWAY,
};
use std::{
ffi::CStr,
io, mem,
net::{IpAddr, Ipv4Addr},
ptr,
};
fn main() -> io::Result<()> {
let mut ifaddrs_ptr: *mut ifaddrs = ptr::null_mut();
unsafe {
if getifaddrs(&mut ifaddrs_ptr) != 0 {
return Err(io::Error::last_os_error());
}
let mut address: Option<IpAddr> = None;
let mut interface: Option<String> = None;
let mut ifa_ptr = ifaddrs_ptr;
while !ifa_ptr.is_null() {
let ifa = &*ifa_ptr;
let flags = ifa.ifa_flags as i32;
if flags & IFF_LOOPBACK == 0
&& flags & IFF_RUNNING != 0
&& flags & IFF_BROADCAST != 0
&& flags & IFF_MULTICAST != 0
&& flags & IFF_UP != 0
&& flags & RTF_GATEWAY != 0
{
let ip = extract_ip(ifa.ifa_addr);
if ip.is_some() && ip.unwrap().is_ipv4() {
let name = CStr::from_ptr(ifa.ifa_name).to_string_lossy().into_owned();
println!("Interface: {}", name);
let dst = extract_ip(ifa.ifa_dstaddr);
println!("Destination: {:?}", dst);
let mask = extract_ip(ifa.ifa_netmask);
println!("Mask: {:?}", mask);
address = ip;
interface = Some(name);
break;
}
}
ifa_ptr = ifa.ifa_next;
}
freeifaddrs(ifaddrs_ptr);
match (address, interface) {
(Some(gw), Some(iface)) => {
println!("Gateway: {}", gw);
println!("Interface: {}", iface);
}
_ => println!("Failed to retrieve gateway and interface information"),
}
}
let v = get_active_gateway()?;
println!("Active gateway: {:?}", v);
let _v = main4()?;
Ok(())
}
fn extract_ip(addr: *const sockaddr) -> Option<IpAddr> {
unsafe {
match (*addr).sa_family as c_int {
AF_INET => {
let sin = *(addr as *const sockaddr_in);
let ip = &sin.sin_addr.s_addr as *const c_uint as *const u8;
let ip: [u8; 4] = std::slice::from_raw_parts(ip, 4).try_into().ok()?;
Some(IpAddr::from(ip))
}
AF_INET6 => {
let sin6 = *(addr as *const sockaddr_in6);
let ip = sin6.sin6_addr.s6_addr;
Some(IpAddr::from(ip))
}
_ => None,
}
}
}
fn get_active_gateway() -> std::io::Result<Ipv4Addr> {
// Command: `netstat -rn | grep default | grep -E -o "[0-9\.]+" | head -n 1`
let output = std::process::Command::new("netstat").arg("-rn").output()?;
let output_str = String::from_utf8_lossy(&output.stdout);
let gateway = output_str
.lines()
.filter(|line| line.contains("default"))
.filter_map(|line| {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
Some(parts[1])
} else {
None
}
})
.filter_map(|ip| ip.parse::<Ipv4Addr>().ok())
.next();
use std::io::ErrorKind::Other;
let err = "Failed to parse default gateway from \"netstat\" output";
Ok(gateway.ok_or(std::io::Error::new(Other, err))?)
}
// use std::io;
// use std::mem;
// use std::net::{IpAddr, Ipv4Addr};
// use std::process::Command;
// use std::ptr;
#[repr(C)]
#[allow(non_camel_case_types)]
struct rt_msghdr {
rtm_msglen: u16,
rtm_version: u8,
rtm_type: u8,
rtm_index: i16,
rtm_flags: i32,
rtm_addrs: i32,
rtm_pid: i32,
rtm_seq: i32,
rtm_errno: i32,
rtm_use: i32,
rtm_inits: u32,
rtm_rmx: rt_metrics,
}
#[repr(C)]
#[allow(non_camel_case_types)]
struct rt_metrics {
rmx_locks: u32,
rmx_mtu: u32,
rmx_hopcount: u32,
rmx_expire: u32,
rmx_recvpipe: u32,
rmx_sendpipe: u32,
rmx_ssthresh: u32,
rmx_rtt: u32,
rmx_rttvar: u32,
rmx_pksent: u32,
rmx_filler: [u32; 4],
}
fn sa_size(sa: *const libc::sockaddr) -> usize {
if sa.is_null() {
return mem::size_of::<c_long>();
}
let sa_len = unsafe { (*sa).sa_len } as usize;
if sa_len == 0 {
mem::size_of::<c_long>()
} else {
1 + ((sa_len - 1) | (mem::size_of::<c_long>() - 1))
}
}
fn main4() -> io::Result<()> {
let mut mib: [i32; 6] = [0; 6];
let mut needed: usize = 0;
mib[0] = libc::CTL_NET;
mib[1] = libc::PF_ROUTE;
mib[4] = libc::NET_RT_DUMP;
let null = ptr::null_mut::<c_void>();
if unsafe { libc::sysctl(mib.as_mut_ptr(), 6, null, &mut needed, null, 0) } < 0 {
return Err(io::Error::last_os_error());
}
let buf = vec![0u8; needed];
let buf_ptr = buf.as_ptr() as *mut libc::c_void;
if unsafe { libc::sysctl(mib.as_mut_ptr(), 6, buf_ptr, &mut needed, null, 0) } < 0 {
return Err(io::Error::last_os_error());
}
let lim = unsafe { buf_ptr.offset(needed as isize) };
let mut next = buf_ptr;
let count = mem::size_of::<rt_msghdr>() as isize;
loop {
let rtm = unsafe { &mut *(next as *mut rt_msghdr) };
let sa = unsafe { &mut *(next.offset(count) as *mut sockaddr) };
let sa_size = sa_size(sa) as isize;
let sa = unsafe { (sa as *mut sockaddr as *mut c_void).offset(sa_size) };
let sockin = unsafe { &mut *(sa as *mut sockaddr_in) };
match sockin.sin_family as i32 {
libc::AF_INET => {
let ip = &sockin.sin_addr.s_addr as *const c_uint as *const u8;
let ip: [u8; 4] = unsafe { std::slice::from_raw_parts(ip, 4).try_into().unwrap() };
let ip = IpAddr::from(ip);
println!("defaultrouter={}", ip);
break;
}
libc::AF_INET6 => {
let sockin6 = unsafe { &mut *(sa as *mut sockaddr_in6) };
let ip = sockin6.sin6_addr.s6_addr;
let ip = IpAddr::from(ip);
println!("defaultrouter ipv6={}", ip);
}
_ => {}
}
next = unsafe { next.offset(rtm.rtm_msglen as isize) };
if next >= lim {
break;
}
}
Ok(())
}
@ssrlive
Copy link
Author

ssrlive commented Oct 4, 2023

set interface mtu

#[cfg(target_os = "macos")]
#[allow(dead_code)]
pub fn set_tun_mtu(tun: &str, mtu: usize) -> std::io::Result<()> {
    let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0) };
    if fd < 0 {
        return Err(std::io::Error::last_os_error());
    }
    let mut ifr: ifreq = unsafe { std::mem::zeroed() };
    tun.as_bytes()
        .iter()
        .zip(unsafe { ifr.ifrn.name }.as_mut_slice().iter_mut())
        .for_each(|(a, b)| *b = *a as libc::c_char);
    ifr.ifru.mtu = mtu as libc::c_int;

    let ret = unsafe { libc::ioctl(fd, SIOCSIFMTU as libc::c_ulong, &ifr) };
    unsafe {
        libc::close(fd);
    }
    if ret < 0 {
        // log::trace!("set_tun_mtu: {} {}", tun, mtu);
        return Err(std::io::Error::last_os_error());
    }
    Ok(())
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ifreq {
    pub ifrn: ifrn,
    pub ifru: ifru,
}

#[repr(C)]
#[derive(Copy, Clone)]
pub union ifrn {
    pub name: [libc::c_char; libc::IFNAMSIZ],
}

#[repr(C)]
#[derive(Copy, Clone)]
pub union ifru {
    pub addr: libc::sockaddr,
    pub dstaddr: libc::sockaddr,
    pub broadaddr: libc::sockaddr,

    pub flags: libc::c_short,
    pub metric: libc::c_int,
    pub mtu: libc::c_int,
    pub phys: libc::c_int,
    pub media: libc::c_int,
    pub intval: libc::c_int,
    pub data: *mut libc::c_void,
    pub devmtu: ifdevmtu,
    pub wake_flags: libc::c_uint,
    pub route_refcnt: libc::c_uint,
    pub cap: [libc::c_int; 2],
    pub functional_type: libc::c_uint,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ifdevmtu {
    pub current: libc::c_int,
    pub min: libc::c_int,
    pub max: libc::c_int,
}

//
// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/sockio.h
//
const SIOCSIFMTU: u32 = _IOW(b'i' as u32, 52, std::mem::size_of::<ifreq>() as u32);

#[allow(non_snake_case)]
#[inline]
const fn _IOC(inout: u32, group: u32, num: u32, len: u32) -> u32 {
    inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)
}

#[allow(non_snake_case)]
#[inline]
const fn _IOW(g: u32, n: u32, t: u32) -> u32 {
    _IOC(IOC_IN, g, n, t)
}

const IOCPARM_MASK: u32 = 0x1fff;
const IOC_IN: u32 = 0x80000000;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment