From 169d88034059ba5e1db8c2f968eb0cefac8d88a8 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Sun, 13 Aug 2023 07:37:05 +0800 Subject: [PATCH] feat(shadowsocks): macOS if_nametoindex cache result --- .../shadowsocks/src/net/sys/unix/bsd/macos.rs | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs index a730bd9c35b1..dbc6e41a92db 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs @@ -1,4 +1,6 @@ use std::{ + cell::RefCell, + collections::HashMap, io::{self, ErrorKind}, mem, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream as StdTcpStream}, @@ -7,6 +9,7 @@ use std::{ ptr, sync::atomic::{AtomicBool, Ordering}, task::{self, Poll}, + time::{Duration, Instant}, }; use log::{debug, error, warn}; @@ -228,11 +231,24 @@ pub async fn create_inbound_tcp_socket(bind_addr: &SocketAddr, _accept_opts: &Ac } } -fn set_ip_bound_if(socket: &S, addr: &SocketAddr, iface: &str) -> io::Result<()> { - const IP_BOUND_IF: libc::c_int = 25; // bsd/netinet/in.h - const IPV6_BOUND_IF: libc::c_int = 125; // bsd/netinet6/in6.h +fn find_interface_index_cached(iface: &str) -> io::Result { + const INDEX_EXPIRE_DURATION: Duration = Duration::from_secs(5); - unsafe { + thread_local! { + static INTERFACE_INDEX_CACHE: RefCell> = + RefCell::new(HashMap::new()); + } + + let cache_index = INTERFACE_INDEX_CACHE.with(|cache| cache.borrow().get(iface).cloned()); + if let Some((idx, insert_time)) = cache_index { + // short-path, cache hit for most cases + let now = Instant::now(); + if now - insert_time < INDEX_EXPIRE_DURATION { + return Ok(idx); + } + } + + let index = unsafe { let mut ciface = [0u8; libc::IFNAMSIZ]; if iface.len() >= ciface.len() { return Err(ErrorKind::InvalidInput.into()); @@ -241,12 +257,28 @@ fn set_ip_bound_if(socket: &S, addr: &SocketAddr, iface: &str) -> io let iface_bytes = iface.as_bytes(); ptr::copy_nonoverlapping(iface_bytes.as_ptr(), ciface.as_mut_ptr(), iface_bytes.len()); - let index = libc::if_nametoindex(ciface.as_ptr() as *const libc::c_char); - if index == 0 { - let err = io::Error::last_os_error(); - error!("if_nametoindex ifname: {} error: {}", iface, err); - return Err(err); - } + libc::if_nametoindex(ciface.as_ptr() as *const libc::c_char) + }; + + if index == 0 { + let err = io::Error::last_os_error(); + error!("if_nametoindex ifname: {} error: {}", iface, err); + return Err(err); + } + + INTERFACE_INDEX_CACHE.with(|cache| { + cache.borrow_mut().insert(iface.to_owned(), (index, Instant::now())); + }); + + Ok(index) +} + +fn set_ip_bound_if(socket: &S, addr: &SocketAddr, iface: &str) -> io::Result<()> { + const IP_BOUND_IF: libc::c_int = 25; // bsd/netinet/in.h + const IPV6_BOUND_IF: libc::c_int = 125; // bsd/netinet6/in6.h + + unsafe { + let index = find_interface_index_cached(iface)?; let ret = match addr { SocketAddr::V4(..) => libc::setsockopt( @@ -359,6 +391,7 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, config: &ConnectOp /// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h #[repr(C)] +#[allow(non_camel_case_types)] struct msghdr_x { msg_name: *mut libc::c_void, //< optional address msg_namelen: libc::socklen_t, //< size of address