@@ -1591,6 +1591,8 @@ impl Connection {
15911591 path_id : PathId ,
15921592 ) -> Option < Transmit > {
15931593 let ( prev_cid, prev_path) = self . paths . get_mut ( & path_id) ?. prev . as_mut ( ) ?;
1594+ // TODO (matheus23): We could use !prev_path.is_validating() here instead to
1595+ // (possibly) also re-send challenges when they get lost.
15941596 if !prev_path. send_new_challenge {
15951597 return None ;
15961598 } ;
@@ -1843,13 +1845,22 @@ impl Connection {
18431845 let Some ( path) = self . paths . get_mut ( & path_id) else {
18441846 continue ;
18451847 } ;
1848+ self . timers
1849+ . stop ( Timer :: PerPath ( path_id, PathTimer :: PathChallengeLost ) ) ;
18461850 debug ! ( "path validation failed" ) ;
18471851 if let Some ( ( _, prev) ) = path. prev . take ( ) {
18481852 path. data = prev;
18491853 }
18501854 path. data . challenges_sent . clear ( ) ;
18511855 path. data . send_new_challenge = false ;
18521856 }
1857+ PathTimer :: PathChallengeLost => {
1858+ let Some ( path) = self . paths . get_mut ( & path_id) else {
1859+ continue ;
1860+ } ;
1861+ trace ! ( "path challenge deemed lost" ) ;
1862+ path. data . send_new_challenge = true ;
1863+ }
18531864 PathTimer :: PathOpen => {
18541865 let Some ( path) = self . path_mut ( path_id) else {
18551866 continue ;
@@ -4037,6 +4048,8 @@ impl Connection {
40374048 } else if let Some ( & challenge_sent) = path. data . challenges_sent . get ( & token) {
40384049 self . timers
40394050 . stop ( Timer :: PerPath ( path_id, PathTimer :: PathValidation ) ) ;
4051+ self . timers
4052+ . stop ( Timer :: PerPath ( path_id, PathTimer :: PathChallengeLost ) ) ;
40404053 if !path. data . validated {
40414054 trace ! ( "new path validated" ) ;
40424055 }
@@ -4663,7 +4676,7 @@ impl Connection {
46634676
46644677 let mut prev = mem:: replace ( path, new_path) ;
46654678 // Don't clobber the original path if the previous one hasn't been validated yet
4666- if !prev. challenges_sent . is_empty ( ) {
4679+ if !prev. is_validating_path ( ) {
46674680 prev. send_new_challenge = true ;
46684681 // We haven't updated the remote CID yet, this captures the remote CID we were using on
46694682 // the previous path.
@@ -4925,48 +4938,48 @@ impl Connection {
49254938 }
49264939
49274940 // PATH_CHALLENGE
4928- if buf. remaining_mut ( ) > 9 && space_id == SpaceId :: Data {
4929- // Transmit challenges with every outgoing packet on an unvalidated path
4930- if path. is_validating_path ( ) {
4931- // Generate a new challenge every time we send a new PATH_CHALLENGE
4932- let token = self . rng . random ( ) ;
4933- path. challenges_sent . insert ( token, now) ;
4934- sent. non_retransmits = true ;
4935- sent. requires_padding = true ;
4936- trace ! ( "PATH_CHALLENGE {:08x}" , token) ;
4937- buf. write ( frame:: FrameType :: PATH_CHALLENGE ) ;
4938- buf. write ( token) ;
4939- self . stats . frame_tx . path_challenge += 1 ;
4941+ if buf. remaining_mut ( ) > 9 && space_id == SpaceId :: Data && path. send_new_challenge {
4942+ path. send_new_challenge = false ;
49404943
4941- if is_multipath_negotiated && !path. validated && path. send_new_challenge {
4942- // queue informing the path status along with the challenge
4943- space. pending . path_status . insert ( path_id) ;
4944- }
4944+ // Generate a new challenge every time we send a new PATH_CHALLENGE
4945+ let token = self . rng . random ( ) ;
4946+ path. challenges_sent . insert ( token, now) ;
4947+ sent. non_retransmits = true ;
4948+ sent. requires_padding = true ;
4949+ trace ! ( "PATH_CHALLENGE {:08x}" , token) ;
4950+ buf. write ( frame:: FrameType :: PATH_CHALLENGE ) ;
4951+ buf. write ( token) ;
4952+ self . stats . frame_tx . path_challenge += 1 ;
4953+ let pto = self . ack_frequency . max_ack_delay_for_pto ( ) + path. rtt . pto_base ( ) ;
4954+ self . timers . set (
4955+ Timer :: PerPath ( path_id, PathTimer :: PathChallengeLost ) ,
4956+ now + pto,
4957+ ) ;
49454958
4946- // But only send a packet solely for that purpose at most once
4947- path. send_new_challenge = false ;
4959+ if is_multipath_negotiated && !path. validated && path. send_new_challenge {
4960+ // queue informing the path status along with the challenge
4961+ space. pending . path_status . insert ( path_id) ;
4962+ }
49484963
4949- // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
4950- // of whether one has already been sent on this path.
4951- if space_id == SpaceId :: Data
4952- && self
4953- . config
4954- . address_discovery_role
4955- . should_report ( & self . peer_params . address_discovery_role )
4956- {
4957- let frame =
4958- frame:: ObservedAddr :: new ( path. remote , self . next_observed_addr_seq_no ) ;
4959- if buf. remaining_mut ( ) > frame. size ( ) {
4960- frame. write ( buf) ;
4964+ // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
4965+ // of whether one has already been sent on this path.
4966+ if space_id == SpaceId :: Data
4967+ && self
4968+ . config
4969+ . address_discovery_role
4970+ . should_report ( & self . peer_params . address_discovery_role )
4971+ {
4972+ let frame = frame:: ObservedAddr :: new ( path. remote , self . next_observed_addr_seq_no ) ;
4973+ if buf. remaining_mut ( ) > frame. size ( ) {
4974+ frame. write ( buf) ;
49614975
4962- self . next_observed_addr_seq_no =
4963- self . next_observed_addr_seq_no . saturating_add ( 1u8 ) ;
4964- path. observed_addr_sent = true ;
4976+ self . next_observed_addr_seq_no =
4977+ self . next_observed_addr_seq_no . saturating_add ( 1u8 ) ;
4978+ path. observed_addr_sent = true ;
49654979
4966- self . stats . frame_tx . observed_addr += 1 ;
4967- sent. retransmits . get_or_create ( ) . observed_addr = true ;
4968- space. pending . observed_addr = false ;
4969- }
4980+ self . stats . frame_tx . observed_addr += 1 ;
4981+ sent. retransmits . get_or_create ( ) . observed_addr = true ;
4982+ space. pending . observed_addr = false ;
49704983 }
49714984 }
49724985 }
@@ -5747,6 +5760,14 @@ impl Connection {
57475760 self . path_data ( PathId :: ZERO ) . current_mtu ( )
57485761 }
57495762
5763+ /// Triggers path validation on all paths
5764+ #[ cfg( test) ]
5765+ pub ( crate ) fn trigger_path_validation ( & mut self ) {
5766+ for path in self . paths . values_mut ( ) {
5767+ path. data . send_new_challenge = true ;
5768+ }
5769+ }
5770+
57505771 /// Whether we have 1-RTT data to send
57515772 ///
57525773 /// This checks for frames that can only be sent in the data space (1-RTT):
0 commit comments