@@ -1004,7 +1004,12 @@ async fn auto_mtu_detection(
1004
1004
use talpid_tunnel:: { ICMP_HEADER_SIZE , MIN_IPV4_MTU } ;
1005
1005
use tokio_stream:: StreamExt ;
1006
1006
1007
- const PING_TIMEOUT : Duration = Duration :: from_secs ( 5 ) ;
1007
+ /// Max time to wait for any ping, when this expires,
1008
+ /// we give up and throw an `Error::MtuDetectionAllDropped`
1009
+ const PING_TIMEOUT : Duration = Duration :: from_secs ( 10 ) ;
1010
+ /// Max time to wait after the first ping arrives. Every ping
1011
+ /// after this timeout will be counted as dropped.
1012
+ const PING_OFFSET_TIMEOUT : Duration = Duration :: from_secs ( 2 ) ;
1008
1013
1009
1014
let config_builder = Config :: builder ( ) . kind ( surge_ping:: ICMP :: V4 ) ;
1010
1015
#[ cfg( any( target_os = "macos" , target_os = "linux" ) ) ]
@@ -1015,6 +1020,8 @@ async fn auto_mtu_detection(
1015
1020
let linspace = mtu_spacing ( MIN_IPV4_MTU , current_mtu, step_size) ;
1016
1021
1017
1022
let payload_buf = vec ! [ 0 ; current_mtu as usize ] ;
1023
+
1024
+ let mut ping_stream = linspace
1018
1025
. iter ( )
1019
1026
. enumerate ( )
1020
1027
. map ( |( i, & mtu) | {
@@ -1031,31 +1038,35 @@ async fn auto_mtu_detection(
1031
1038
. await
1032
1039
}
1033
1040
} )
1034
- . collect ( ) ;
1041
+ . collect :: < FuturesUnordered < _ > > ( )
1042
+ . map_ok ( |( packet, _rtt) | {
1043
+ let surge_ping:: IcmpPacket :: V4 ( packet) = packet else {
1044
+ panic ! ( "ICMP ping response was not of IPv4 type" ) ;
1045
+ } ;
1046
+ let size = packet. get_size ( ) as u16 + IPV4_HEADER_SIZE ;
1047
+ log:: trace!( "Got ICMP ping response of total size {size}" ) ;
1048
+ debug_assert_eq ! ( size, linspace[ packet. get_sequence( ) . 0 as usize ] ) ;
1049
+ size
1050
+ } ) ;
1051
+
1052
+ let first_ping_size = match ping_stream
1053
+ . next ( )
1054
+ . await
1055
+ . expect ( "At least one pings should be sent" )
1056
+ {
1057
+ Ok ( size) => size,
1058
+ // If the first ping we get back timed out, then all of them did
1059
+ Err ( SurgeError :: Timeout { .. } ) => return Err ( Error :: MtuDetectionAllDropped ) ,
1060
+ // Short circuit and return error on unexpected error types
1061
+ Err ( e) => return Err ( Error :: MtuDetectionPingError ( e) ) ,
1062
+ } ;
1035
1063
1036
1064
ping_stream
1037
- . map ( |res| match res {
1038
- // Map successful pings to packet size
1039
- Ok ( ( packet, _rtt) ) => {
1040
- let surge_ping:: IcmpPacket :: V4 ( packet) = packet else {
1041
- panic ! ( "ICMP ping response was not of IPv4 type" ) ;
1042
- } ;
1043
- let size = packet. get_size ( ) as u16 + IPV4_HEADER_SIZE ;
1044
- log:: trace!( "Got ICMP ping response of total size {size}" ) ;
1045
- debug_assert_eq ! ( size, linspace[ packet. get_sequence( ) . 0 as usize ] ) ;
1046
- Ok ( Some ( size) )
1047
- }
1048
- // Filter out dropped pings
1049
- Err ( SurgeError :: Timeout { seq } ) => {
1050
- log:: info!( "Ping of size {} dropped" , linspace[ seq. 0 as usize ] ) ;
1051
- Ok ( None )
1052
- }
1053
- // Short circuit and return error on unexpected error types
1054
- Err ( e) => Err ( Error :: MtuDetectionPingError ( e) ) ,
1055
- } )
1056
- . try_fold ( None , |acc, mtu| future:: ready ( Ok ( acc. max ( mtu) ) ) )
1057
- . await ?
1058
- . ok_or ( Error :: MtuDetectionAllDropped )
1065
+ . timeout ( PING_OFFSET_TIMEOUT ) // Start a new, sorter, timeout
1066
+ . map_while ( |res| res. ok ( ) ) // Filter out remaining pings after this timeout
1067
+ . try_fold ( first_ping_size, |acc, mtu| future:: ready ( Ok ( acc. max ( mtu) ) ) ) // Get largest ping
1068
+ . await
1069
+ . map_err ( Error :: MtuDetectionPingError )
1059
1070
}
1060
1071
1061
1072
/// Creates a linear spacing of MTU values with the given step size. Always includes the given end
0 commit comments