@@ -22,6 +22,7 @@ use crate::{
22
22
cid_queue:: CidQueue ,
23
23
coding:: BufMutExt ,
24
24
config:: { ServerConfig , TransportConfig } ,
25
+ connection:: spaces:: LostPacket ,
25
26
crypto:: { self , KeyPair , Keys , PacketKey } ,
26
27
frame:: { self , Close , Datagram , FrameStruct , NewConnectionId , NewToken } ,
27
28
packet:: {
@@ -1461,6 +1462,10 @@ impl Connection {
1461
1462
}
1462
1463
} ;
1463
1464
1465
+ if self . detect_spurious_loss ( & ack, space) {
1466
+ self . path . congestion . on_spurious_congestion_event ( ) ;
1467
+ }
1468
+
1464
1469
// Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
1465
1470
let mut newly_acked = ArrayRangeSet :: new ( ) ;
1466
1471
for range in ack. iter ( ) {
@@ -1555,6 +1560,43 @@ impl Connection {
1555
1560
Ok ( ( ) )
1556
1561
}
1557
1562
1563
+ fn detect_spurious_loss ( & mut self , ack : & frame:: Ack , space : SpaceId ) -> bool {
1564
+ let lost_packets = & mut self . spaces [ space] . lost_packets ;
1565
+
1566
+ if lost_packets. is_empty ( ) {
1567
+ return false ;
1568
+ }
1569
+
1570
+ for range in ack. iter ( ) {
1571
+ let spurious_losses: Vec < u64 > = lost_packets
1572
+ . range ( range. clone ( ) )
1573
+ . map ( |( pn, _info) | pn)
1574
+ . copied ( )
1575
+ . collect ( ) ;
1576
+
1577
+ for pn in spurious_losses {
1578
+ lost_packets. remove ( & pn) ;
1579
+ }
1580
+ }
1581
+
1582
+ // If this ACK frame acknowledged all deemed lost packets,
1583
+ // then we have raised a spurious congestion event in the past.
1584
+ // We cannot conclude when there are remaining packets,
1585
+ // but future ACK frames might indicate a spurious loss detection.
1586
+ lost_packets. is_empty ( )
1587
+ }
1588
+
1589
+ /// Drain lost packets that we reasonably think will never arrive
1590
+ ///
1591
+ /// The current criterion is copied from `msquic`:
1592
+ /// discard packets that were sent earlier than 2 probe timeouts ago.
1593
+ fn drain_lost_packets ( & mut self , now : Instant , space : SpaceId ) {
1594
+ let two_pto = 2 * self . path . rtt . pto_base ( ) ;
1595
+
1596
+ let lost_packets = & mut self . spaces [ space] . lost_packets ;
1597
+ lost_packets. retain ( |_pn, info| now. saturating_duration_since ( info. time_sent ) <= two_pto) ;
1598
+ }
1599
+
1558
1600
/// Process a new ECN block from an in-order ACK
1559
1601
fn process_ecn (
1560
1602
& mut self ,
@@ -1577,7 +1619,7 @@ impl Connection {
1577
1619
self . stats . path . congestion_events += 1 ;
1578
1620
self . path
1579
1621
. congestion
1580
- . on_congestion_event ( now, largest_sent_time, false , 0 ) ;
1622
+ . on_congestion_event ( now, largest_sent_time, false , true , 0 ) ;
1581
1623
}
1582
1624
}
1583
1625
}
@@ -1735,6 +1777,8 @@ impl Connection {
1735
1777
prev_packet = Some ( packet) ;
1736
1778
}
1737
1779
1780
+ self . drain_lost_packets ( now, pn_space) ;
1781
+
1738
1782
// OnPacketsLost
1739
1783
if let Some ( largest_lost) = lost_packets. last ( ) . cloned ( ) {
1740
1784
let old_bytes_in_flight = self . path . in_flight . bytes ;
@@ -1756,12 +1800,20 @@ impl Connection {
1756
1800
now,
1757
1801
self . orig_rem_cid ,
1758
1802
) ;
1803
+
1759
1804
self . remove_in_flight ( & info) ;
1760
1805
for frame in info. stream_frames {
1761
1806
self . streams . retransmit ( frame) ;
1762
1807
}
1763
1808
self . spaces [ pn_space] . pending |= info. retransmits ;
1764
1809
self . path . mtud . on_non_probe_lost ( packet, info. size ) ;
1810
+
1811
+ self . spaces [ pn_space] . lost_packets . insert (
1812
+ packet,
1813
+ LostPacket {
1814
+ time_sent : info. time_sent ,
1815
+ } ,
1816
+ ) ;
1765
1817
}
1766
1818
1767
1819
if self . path . mtud . black_hole_detected ( now) {
@@ -1783,6 +1835,7 @@ impl Connection {
1783
1835
now,
1784
1836
largest_lost_sent,
1785
1837
in_persistent_congestion,
1838
+ false ,
1786
1839
size_of_lost_packets,
1787
1840
) ;
1788
1841
}
0 commit comments