mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-06 16:14:11 +00:00
feat(shadowsocks): macOS if_nametoindex cache result
This commit is contained in:
@@ -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<S: AsRawFd>(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<u32> {
|
||||
const INDEX_EXPIRE_DURATION: Duration = Duration::from_secs(5);
|
||||
|
||||
unsafe {
|
||||
thread_local! {
|
||||
static INTERFACE_INDEX_CACHE: RefCell<HashMap<String, (u32, Instant)>> =
|
||||
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<S: AsRawFd>(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<S: AsRawFd>(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
|
||||
|
||||
Reference in New Issue
Block a user