golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/loss.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.21 6 7 package quic 8 9 import ( 10 "context" 11 "log/slog" 12 "math" 13 "time" 14 ) 15 16 type lossState struct { 17 side connSide 18 19 // True when the handshake is confirmed. 20 // https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2 21 handshakeConfirmed bool 22 23 // Peer's max_ack_delay transport parameter. 24 // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1 25 maxAckDelay time.Duration 26 27 // Time of the next event: PTO expiration (if ptoTimerArmed is true), 28 // or loss detection. 29 // The connection must call lossState.advance when the timer expires. 30 timer time.Time 31 32 // True when the PTO timer is set. 33 ptoTimerArmed bool 34 35 // True when the PTO timer has expired and a probe packet has not yet been sent. 36 ptoExpired bool 37 38 // Count of PTO expirations since the lack received acknowledgement. 39 // https://www.rfc-editor.org/rfc/rfc9002#section-6.2.1-9 40 ptoBackoffCount int 41 42 // Anti-amplification limit: Three times the amount of data received from 43 // the peer, less the amount of data sent. 44 // 45 // Set to antiAmplificationUnlimited (MaxInt) to disable the limit. 46 // The limit is always disabled for clients, and for servers after the 47 // peer's address is validated. 48 // 49 // Anti-amplification is per-address; this will need to change if/when we 50 // support address migration. 51 // 52 // https://www.rfc-editor.org/rfc/rfc9000#section-8-2 53 antiAmplificationLimit int 54 55 // Count of non-ack-eliciting packets (ACKs) sent since the last ack-eliciting one. 56 consecutiveNonAckElicitingPackets int 57 58 rtt rttState 59 pacer pacerState 60 cc *ccReno 61 62 // Per-space loss detection state. 63 spaces [numberSpaceCount]struct { 64 sentPacketList 65 maxAcked packetNumber 66 lastAckEliciting packetNumber 67 } 68 69 // Temporary state used when processing an ACK frame. 70 ackFrameRTT time.Duration // RTT from latest packet in frame 71 ackFrameContainsAckEliciting bool // newly acks an ack-eliciting packet? 72 } 73 74 const antiAmplificationUnlimited = math.MaxInt 75 76 func (c *lossState) init(side connSide, maxDatagramSize int, now time.Time) { 77 c.side = side 78 if side == clientSide { 79 // Clients don't have an anti-amplification limit. 80 c.antiAmplificationLimit = antiAmplificationUnlimited 81 } 82 c.rtt.init() 83 c.cc = newReno(maxDatagramSize) 84 c.pacer.init(now, c.cc.congestionWindow, timerGranularity) 85 86 // Peer's assumed max_ack_delay, prior to receiving transport parameters. 87 // https://www.rfc-editor.org/rfc/rfc9000#section-18.2 88 c.maxAckDelay = 25 * time.Millisecond 89 90 for space := range c.spaces { 91 c.spaces[space].maxAcked = -1 92 c.spaces[space].lastAckEliciting = -1 93 } 94 } 95 96 // setMaxAckDelay sets the max_ack_delay transport parameter received from the peer. 97 func (c *lossState) setMaxAckDelay(d time.Duration) { 98 if d >= (1<<14)*time.Millisecond { 99 // Values of 2^14 or greater are invalid. 100 // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1 101 return 102 } 103 c.maxAckDelay = d 104 } 105 106 // confirmHandshake indicates the handshake has been confirmed. 107 func (c *lossState) confirmHandshake() { 108 c.handshakeConfirmed = true 109 } 110 111 // validateClientAddress disables the anti-amplification limit after 112 // a server validates a client's address. 113 func (c *lossState) validateClientAddress() { 114 c.antiAmplificationLimit = antiAmplificationUnlimited 115 } 116 117 // minDatagramSize is the minimum datagram size permitted by 118 // anti-amplification protection. 119 // 120 // Defining a minimum size avoids the case where, say, anti-amplification 121 // technically allows us to send a 1-byte datagram, but no such datagram 122 // can be constructed. 123 const minPacketSize = 128 124 125 type ccLimit int 126 127 const ( 128 ccOK = ccLimit(iota) // OK to send 129 ccBlocked // sending blocked by anti-amplification 130 ccLimited // sending blocked by congestion control 131 ccPaced // sending allowed by congestion, but delayed by pacer 132 ) 133 134 // sendLimit reports whether sending is possible at this time. 135 // When sending is pacing limited, it returns the next time a packet may be sent. 136 func (c *lossState) sendLimit(now time.Time) (limit ccLimit, next time.Time) { 137 if c.antiAmplificationLimit < minPacketSize { 138 // When at the anti-amplification limit, we may not send anything. 139 return ccBlocked, time.Time{} 140 } 141 if c.ptoExpired { 142 // On PTO expiry, send a probe. 143 return ccOK, time.Time{} 144 } 145 if !c.cc.canSend() { 146 // Congestion control blocks sending. 147 return ccLimited, time.Time{} 148 } 149 if c.cc.bytesInFlight == 0 { 150 // If no bytes are in flight, send packet unpaced. 151 return ccOK, time.Time{} 152 } 153 canSend, next := c.pacer.canSend(now) 154 if !canSend { 155 // Pacer blocks sending. 156 return ccPaced, next 157 } 158 return ccOK, time.Time{} 159 } 160 161 // maxSendSize reports the maximum datagram size that may be sent. 162 func (c *lossState) maxSendSize() int { 163 return min(c.antiAmplificationLimit, c.cc.maxDatagramSize) 164 } 165 166 // advance is called when time passes. 167 // The lossf function is called for each packet newly detected as lost. 168 func (c *lossState) advance(now time.Time, lossf func(numberSpace, *sentPacket, packetFate)) { 169 c.pacer.advance(now, c.cc.congestionWindow, c.rtt.smoothedRTT) 170 if c.ptoTimerArmed && !c.timer.IsZero() && !c.timer.After(now) { 171 c.ptoExpired = true 172 c.timer = time.Time{} 173 c.ptoBackoffCount++ 174 } 175 c.detectLoss(now, lossf) 176 } 177 178 // nextNumber returns the next packet number to use in a space. 179 func (c *lossState) nextNumber(space numberSpace) packetNumber { 180 return c.spaces[space].nextNum 181 } 182 183 // packetSent records a sent packet. 184 func (c *lossState) packetSent(now time.Time, log *slog.Logger, space numberSpace, sent *sentPacket) { 185 sent.time = now 186 c.spaces[space].add(sent) 187 size := sent.size 188 if c.antiAmplificationLimit != antiAmplificationUnlimited { 189 c.antiAmplificationLimit = max(0, c.antiAmplificationLimit-size) 190 } 191 if sent.inFlight { 192 c.cc.packetSent(now, log, space, sent) 193 c.pacer.packetSent(now, size, c.cc.congestionWindow, c.rtt.smoothedRTT) 194 if sent.ackEliciting { 195 c.spaces[space].lastAckEliciting = sent.num 196 c.ptoExpired = false // reset expired PTO timer after sending probe 197 } 198 c.scheduleTimer(now) 199 if logEnabled(log, QLogLevelPacket) { 200 logBytesInFlight(log, c.cc.bytesInFlight) 201 } 202 } 203 if sent.ackEliciting { 204 c.consecutiveNonAckElicitingPackets = 0 205 } else { 206 c.consecutiveNonAckElicitingPackets++ 207 } 208 } 209 210 // datagramReceived records a datagram (not packet!) received from the peer. 211 func (c *lossState) datagramReceived(now time.Time, size int) { 212 if c.antiAmplificationLimit != antiAmplificationUnlimited { 213 c.antiAmplificationLimit += 3 * size 214 // Reset the PTO timer, possibly to a point in the past, in which 215 // case the caller should execute it immediately. 216 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-2 217 c.scheduleTimer(now) 218 if c.ptoTimerArmed && !c.timer.IsZero() && !c.timer.After(now) { 219 c.ptoExpired = true 220 c.timer = time.Time{} 221 } 222 } 223 } 224 225 // receiveAckStart starts processing an ACK frame. 226 // Call receiveAckRange for each range in the frame. 227 // Call receiveAckFrameEnd after all ranges are processed. 228 func (c *lossState) receiveAckStart() { 229 c.ackFrameContainsAckEliciting = false 230 c.ackFrameRTT = -1 231 } 232 233 // receiveAckRange processes a range within an ACK frame. 234 // The ackf function is called for each newly-acknowledged packet. 235 func (c *lossState) receiveAckRange(now time.Time, space numberSpace, rangeIndex int, start, end packetNumber, ackf func(numberSpace, *sentPacket, packetFate)) { 236 // Limit our range to the intersection of the ACK range and 237 // the in-flight packets we have state for. 238 if s := c.spaces[space].start(); start < s { 239 start = s 240 } 241 if e := c.spaces[space].end(); end > e { 242 end = e 243 } 244 if start >= end { 245 return 246 } 247 if rangeIndex == 0 { 248 // If the latest packet in the ACK frame is newly-acked, 249 // record the RTT in c.ackFrameRTT. 250 sent := c.spaces[space].num(end - 1) 251 if !sent.acked { 252 c.ackFrameRTT = max(0, now.Sub(sent.time)) 253 } 254 } 255 for pnum := start; pnum < end; pnum++ { 256 sent := c.spaces[space].num(pnum) 257 if sent.acked || sent.lost { 258 continue 259 } 260 // This is a newly-acknowledged packet. 261 if pnum > c.spaces[space].maxAcked { 262 c.spaces[space].maxAcked = pnum 263 } 264 sent.acked = true 265 c.cc.packetAcked(now, sent) 266 ackf(space, sent, packetAcked) 267 if sent.ackEliciting { 268 c.ackFrameContainsAckEliciting = true 269 } 270 } 271 } 272 273 // receiveAckEnd finishes processing an ack frame. 274 // The lossf function is called for each packet newly detected as lost. 275 func (c *lossState) receiveAckEnd(now time.Time, log *slog.Logger, space numberSpace, ackDelay time.Duration, lossf func(numberSpace, *sentPacket, packetFate)) { 276 c.spaces[space].sentPacketList.clean() 277 // Update the RTT sample when the largest acknowledged packet in the ACK frame 278 // is newly acknowledged, and at least one newly acknowledged packet is ack-eliciting. 279 // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.1-2.2 280 if c.ackFrameRTT >= 0 && c.ackFrameContainsAckEliciting { 281 c.rtt.updateSample(now, c.handshakeConfirmed, space, c.ackFrameRTT, ackDelay, c.maxAckDelay) 282 } 283 // Reset the PTO backoff. 284 // Exception: A client does not reset the backoff on acks for Initial packets. 285 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-9 286 if !(c.side == clientSide && space == initialSpace) { 287 c.ptoBackoffCount = 0 288 } 289 // If the client has set a PTO timer with no packets in flight 290 // we want to restart that timer now. Clearing c.timer does this. 291 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-3 292 c.timer = time.Time{} 293 c.detectLoss(now, lossf) 294 c.cc.packetBatchEnd(now, log, space, &c.rtt, c.maxAckDelay) 295 296 if logEnabled(log, QLogLevelPacket) { 297 var ssthresh slog.Attr 298 if c.cc.slowStartThreshold != math.MaxInt { 299 ssthresh = slog.Int("ssthresh", c.cc.slowStartThreshold) 300 } 301 log.LogAttrs(context.Background(), QLogLevelPacket, 302 "recovery:metrics_updated", 303 slog.Duration("min_rtt", c.rtt.minRTT), 304 slog.Duration("smoothed_rtt", c.rtt.smoothedRTT), 305 slog.Duration("latest_rtt", c.rtt.latestRTT), 306 slog.Duration("rtt_variance", c.rtt.rttvar), 307 slog.Int("congestion_window", c.cc.congestionWindow), 308 slog.Int("bytes_in_flight", c.cc.bytesInFlight), 309 ssthresh, 310 ) 311 } 312 } 313 314 // discardPackets declares that packets within a number space will not be delivered 315 // and that data contained in them should be resent. 316 // For example, after receiving a Retry packet we discard already-sent Initial packets. 317 func (c *lossState) discardPackets(space numberSpace, log *slog.Logger, lossf func(numberSpace, *sentPacket, packetFate)) { 318 for i := 0; i < c.spaces[space].size; i++ { 319 sent := c.spaces[space].nth(i) 320 sent.lost = true 321 c.cc.packetDiscarded(sent) 322 lossf(numberSpace(space), sent, packetLost) 323 } 324 c.spaces[space].clean() 325 if logEnabled(log, QLogLevelPacket) { 326 logBytesInFlight(log, c.cc.bytesInFlight) 327 } 328 } 329 330 // discardKeys is called when dropping packet protection keys for a number space. 331 func (c *lossState) discardKeys(now time.Time, log *slog.Logger, space numberSpace) { 332 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.4 333 for i := 0; i < c.spaces[space].size; i++ { 334 sent := c.spaces[space].nth(i) 335 c.cc.packetDiscarded(sent) 336 } 337 c.spaces[space].discard() 338 c.spaces[space].maxAcked = -1 339 c.spaces[space].lastAckEliciting = -1 340 c.scheduleTimer(now) 341 if logEnabled(log, QLogLevelPacket) { 342 logBytesInFlight(log, c.cc.bytesInFlight) 343 } 344 } 345 346 func (c *lossState) lossDuration() time.Duration { 347 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2 348 return max((9*max(c.rtt.smoothedRTT, c.rtt.latestRTT))/8, timerGranularity) 349 } 350 351 func (c *lossState) detectLoss(now time.Time, lossf func(numberSpace, *sentPacket, packetFate)) { 352 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.1-1 353 const lossThreshold = 3 354 355 lossTime := now.Add(-c.lossDuration()) 356 for space := numberSpace(0); space < numberSpaceCount; space++ { 357 for i := 0; i < c.spaces[space].size; i++ { 358 sent := c.spaces[space].nth(i) 359 if sent.lost || sent.acked { 360 continue 361 } 362 // RFC 9002 Section 6.1 states that a packet is only declared lost if it 363 // is "in flight", which excludes packets that contain only ACK frames. 364 // However, we need some way to determine when to drop state for ACK-only 365 // packets, and the loss algorithm in Appendix A handles loss detection of 366 // not-in-flight packets identically to all others, so we do the same here. 367 switch { 368 case c.spaces[space].maxAcked-sent.num >= lossThreshold: 369 // Packet threshold 370 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.1 371 fallthrough 372 case sent.num <= c.spaces[space].maxAcked && !sent.time.After(lossTime): 373 // Time threshold 374 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2 375 sent.lost = true 376 lossf(space, sent, packetLost) 377 if sent.inFlight { 378 c.cc.packetLost(now, space, sent, &c.rtt) 379 } 380 } 381 if !sent.lost { 382 break 383 } 384 } 385 c.spaces[space].clean() 386 } 387 c.scheduleTimer(now) 388 } 389 390 // scheduleTimer sets the loss or PTO timer. 391 // 392 // The connection is responsible for arranging for advance to be called after 393 // the timer expires. 394 // 395 // The timer may be set to a point in the past, in which advance should be called 396 // immediately. We don't do this here, because executing the timer can cause 397 // packet loss events, and it's simpler for the connection if loss events only 398 // occur when advancing time. 399 func (c *lossState) scheduleTimer(now time.Time) { 400 c.ptoTimerArmed = false 401 402 // Loss timer for sent packets. 403 // The loss timer is only started once a later packet has been acknowledged, 404 // and takes precedence over the PTO timer. 405 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.1.2 406 var oldestPotentiallyLost time.Time 407 for space := numberSpace(0); space < numberSpaceCount; space++ { 408 if c.spaces[space].size > 0 && c.spaces[space].start() <= c.spaces[space].maxAcked { 409 firstTime := c.spaces[space].nth(0).time 410 if oldestPotentiallyLost.IsZero() || firstTime.Before(oldestPotentiallyLost) { 411 oldestPotentiallyLost = firstTime 412 } 413 } 414 } 415 if !oldestPotentiallyLost.IsZero() { 416 c.timer = oldestPotentiallyLost.Add(c.lossDuration()) 417 return 418 } 419 420 // PTO timer. 421 if c.ptoExpired { 422 // PTO timer has expired, don't restart it until we send a probe. 423 c.timer = time.Time{} 424 return 425 } 426 if c.antiAmplificationLimit >= 0 && c.antiAmplificationLimit < minPacketSize { 427 // Server is at its anti-amplification limit and can't send any more data. 428 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1-1 429 c.timer = time.Time{} 430 return 431 } 432 // Timer starts at the most recently sent ack-eliciting packet. 433 // Prior to confirming the handshake, we consider the Initial and Handshake 434 // number spaces; after, we consider only Application Data. 435 var last time.Time 436 if !c.handshakeConfirmed { 437 for space := initialSpace; space <= handshakeSpace; space++ { 438 sent := c.spaces[space].num(c.spaces[space].lastAckEliciting) 439 if sent == nil { 440 continue 441 } 442 if last.IsZero() || last.After(sent.time) { 443 last = sent.time 444 } 445 } 446 } else { 447 sent := c.spaces[appDataSpace].num(c.spaces[appDataSpace].lastAckEliciting) 448 if sent != nil { 449 last = sent.time 450 } 451 } 452 if last.IsZero() && 453 c.side == clientSide && 454 c.spaces[handshakeSpace].maxAcked < 0 && 455 !c.handshakeConfirmed { 456 // The client must always set a PTO timer prior to receiving an ack for a 457 // handshake packet or the handshake being confirmed. 458 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.2.1 459 if !c.timer.IsZero() { 460 // If c.timer is non-zero here, we've already set the PTO timer and 461 // should leave it as-is rather than moving it forward. 462 c.ptoTimerArmed = true 463 return 464 } 465 last = now 466 } else if last.IsZero() { 467 c.timer = time.Time{} 468 return 469 } 470 c.timer = last.Add(c.ptoPeriod()) 471 c.ptoTimerArmed = true 472 } 473 474 func (c *lossState) ptoPeriod() time.Duration { 475 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1 476 return c.ptoBasePeriod() << c.ptoBackoffCount 477 } 478 479 func (c *lossState) ptoBasePeriod() time.Duration { 480 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1 481 pto := c.rtt.smoothedRTT + max(4*c.rtt.rttvar, timerGranularity) 482 if c.handshakeConfirmed { 483 // The max_ack_delay is the maximum amount of time the peer might delay sending 484 // an ack to us. We only take it into account for the Application Data space. 485 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-4 486 pto += c.maxAckDelay 487 } 488 return pto 489 } 490 491 func logBytesInFlight(log *slog.Logger, bytesInFlight int) { 492 log.LogAttrs(context.Background(), QLogLevelPacket, 493 "recovery:metrics_updated", 494 slog.Int("bytes_in_flight", bytesInFlight), 495 ) 496 }