11use std:: {
2+ cell:: RefCell ,
3+ collections:: HashMap ,
24 io:: { self , ErrorKind } ,
35 mem,
46 net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr , TcpStream as StdTcpStream } ,
@@ -7,6 +9,7 @@ use std::{
79 ptr,
810 sync:: atomic:: { AtomicBool , Ordering } ,
911 task:: { self , Poll } ,
12+ time:: { Duration , Instant } ,
1013} ;
1114
1215use log:: { debug, error, warn} ;
@@ -228,11 +231,24 @@ pub async fn create_inbound_tcp_socket(bind_addr: &SocketAddr, _accept_opts: &Ac
228231 }
229232}
230233
231- fn set_ip_bound_if < S : AsRawFd > ( socket : & S , addr : & SocketAddr , iface : & str ) -> io:: Result < ( ) > {
232- const IP_BOUND_IF : libc:: c_int = 25 ; // bsd/netinet/in.h
233- const IPV6_BOUND_IF : libc:: c_int = 125 ; // bsd/netinet6/in6.h
234+ fn find_interface_index_cached ( iface : & str ) -> io:: Result < u32 > {
235+ const INDEX_EXPIRE_DURATION : Duration = Duration :: from_secs ( 5 ) ;
234236
235- unsafe {
237+ thread_local ! {
238+ static INTERFACE_INDEX_CACHE : RefCell <HashMap <String , ( u32 , Instant ) >> =
239+ RefCell :: new( HashMap :: new( ) ) ;
240+ }
241+
242+ let cache_index = INTERFACE_INDEX_CACHE . with ( |cache| cache. borrow ( ) . get ( iface) . cloned ( ) ) ;
243+ if let Some ( ( idx, insert_time) ) = cache_index {
244+ // short-path, cache hit for most cases
245+ let now = Instant :: now ( ) ;
246+ if now - insert_time < INDEX_EXPIRE_DURATION {
247+ return Ok ( idx) ;
248+ }
249+ }
250+
251+ let index = unsafe {
236252 let mut ciface = [ 0u8 ; libc:: IFNAMSIZ ] ;
237253 if iface. len ( ) >= ciface. len ( ) {
238254 return Err ( ErrorKind :: InvalidInput . into ( ) ) ;
@@ -241,12 +257,28 @@ fn set_ip_bound_if<S: AsRawFd>(socket: &S, addr: &SocketAddr, iface: &str) -> io
241257 let iface_bytes = iface. as_bytes ( ) ;
242258 ptr:: copy_nonoverlapping ( iface_bytes. as_ptr ( ) , ciface. as_mut_ptr ( ) , iface_bytes. len ( ) ) ;
243259
244- let index = libc:: if_nametoindex ( ciface. as_ptr ( ) as * const libc:: c_char ) ;
245- if index == 0 {
246- let err = io:: Error :: last_os_error ( ) ;
247- error ! ( "if_nametoindex ifname: {} error: {}" , iface, err) ;
248- return Err ( err) ;
249- }
260+ libc:: if_nametoindex ( ciface. as_ptr ( ) as * const libc:: c_char )
261+ } ;
262+
263+ if index == 0 {
264+ let err = io:: Error :: last_os_error ( ) ;
265+ error ! ( "if_nametoindex ifname: {} error: {}" , iface, err) ;
266+ return Err ( err) ;
267+ }
268+
269+ INTERFACE_INDEX_CACHE . with ( |cache| {
270+ cache. borrow_mut ( ) . insert ( iface. to_owned ( ) , ( index, Instant :: now ( ) ) ) ;
271+ } ) ;
272+
273+ Ok ( index)
274+ }
275+
276+ fn set_ip_bound_if < S : AsRawFd > ( socket : & S , addr : & SocketAddr , iface : & str ) -> io:: Result < ( ) > {
277+ const IP_BOUND_IF : libc:: c_int = 25 ; // bsd/netinet/in.h
278+ const IPV6_BOUND_IF : libc:: c_int = 125 ; // bsd/netinet6/in6.h
279+
280+ unsafe {
281+ let index = find_interface_index_cached ( iface) ?;
250282
251283 let ret = match addr {
252284 SocketAddr :: V4 ( ..) => libc:: setsockopt (
@@ -359,6 +391,7 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, config: &ConnectOp
359391
360392/// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/socket.h
361393#[ repr( C ) ]
394+ #[ allow( non_camel_case_types) ]
362395struct msghdr_x {
363396 msg_name : * mut libc:: c_void , //< optional address
364397 msg_namelen : libc:: socklen_t , //< size of address
0 commit comments