package ackhandler import ( "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) // The receivedPacketTracker tracks packets for the Initial and Handshake packet number space. // Every received packet is acknowledged immediately. type receivedPacketTracker struct { ect0, ect1, ecnce uint64 packetHistory receivedPacketHistory lastAck *wire.AckFrame hasNewAck bool // true as soon as we received an ack-eliciting new packet } func newReceivedPacketTracker() *receivedPacketTracker { return &receivedPacketTracker{packetHistory: *newReceivedPacketHistory()} } func (h *receivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { if isNew := h.packetHistory.ReceivedPacket(pn); !isNew { return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", pn) } //nolint:exhaustive // Only need to count ECT(0), ECT(1) and ECN-CE. switch ecn { case protocol.ECT0: h.ect0++ case protocol.ECT1: h.ect1++ case protocol.ECNCE: h.ecnce++ } if !ackEliciting { return nil } h.hasNewAck = true return nil } func (h *receivedPacketTracker) GetAckFrame() *wire.AckFrame { if !h.hasNewAck { return nil } // This function always returns the same ACK frame struct, filled with the most recent values. ack := h.lastAck if ack == nil { ack = &wire.AckFrame{} } ack.Reset() ack.ECT0 = h.ect0 ack.ECT1 = h.ect1 ack.ECNCE = h.ecnce ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges) h.lastAck = ack h.hasNewAck = false return ack } func (h *receivedPacketTracker) IsPotentiallyDuplicate(pn protocol.PacketNumber) bool { return h.packetHistory.IsPotentiallyDuplicate(pn) } // number of ack-eliciting packets received before sending an ACK const packetsBeforeAck = 2 // The appDataReceivedPacketTracker tracks packets received in the Application Data packet number space. // It waits until at least 2 packets were received before queueing an ACK, or until the max_ack_delay was reached. type appDataReceivedPacketTracker struct { receivedPacketTracker largestObservedRcvdTime time.Time largestObserved protocol.PacketNumber ignoreBelow protocol.PacketNumber maxAckDelay time.Duration ackQueued bool // true if we need send a new ACK ackElicitingPacketsReceivedSinceLastAck int ackAlarm time.Time logger utils.Logger } func newAppDataReceivedPacketTracker(logger utils.Logger) *appDataReceivedPacketTracker { h := &appDataReceivedPacketTracker{ receivedPacketTracker: *newReceivedPacketTracker(), maxAckDelay: protocol.MaxAckDelay, logger: logger, } return h } func (h *appDataReceivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { if err := h.receivedPacketTracker.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { return err } if pn >= h.largestObserved { h.largestObserved = pn h.largestObservedRcvdTime = rcvTime } if !ackEliciting { return nil } h.ackElicitingPacketsReceivedSinceLastAck++ isMissing := h.isMissing(pn) if !h.ackQueued && h.shouldQueueACK(pn, ecn, isMissing) { h.ackQueued = true h.ackAlarm = time.Time{} // cancel the ack alarm } if !h.ackQueued { // No ACK queued, but we'll need to acknowledge the packet after max_ack_delay. h.ackAlarm = rcvTime.Add(h.maxAckDelay) if h.logger.Debug() { h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay) } } return nil } // IgnoreBelow sets a lower limit for acknowledging packets. // Packets with packet numbers smaller than p will not be acked. func (h *appDataReceivedPacketTracker) IgnoreBelow(pn protocol.PacketNumber) { if pn <= h.ignoreBelow { return } h.ignoreBelow = pn h.packetHistory.DeleteBelow(pn) if h.logger.Debug() { h.logger.Debugf("\tIgnoring all packets below %d.", pn) } } // isMissing says if a packet was reported missing in the last ACK. func (h *appDataReceivedPacketTracker) isMissing(p protocol.PacketNumber) bool { if h.lastAck == nil || p < h.ignoreBelow { return false } return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p) } func (h *appDataReceivedPacketTracker) hasNewMissingPackets() bool { if h.lastAck == nil { return false } highestRange := h.packetHistory.GetHighestAckRange() return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1 } func (h *appDataReceivedPacketTracker) shouldQueueACK(pn protocol.PacketNumber, ecn protocol.ECN, wasMissing bool) bool { // always acknowledge the first packet if h.lastAck == nil { h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.") return true } // Send an ACK if this packet was reported missing in an ACK sent before. // Ack decimation with reordering relies on the timer to send an ACK, but if // missing packets we reported in the previous ACK, send an ACK immediately. if wasMissing { if h.logger.Debug() { h.logger.Debugf("\tQueueing ACK because packet %d was missing before.", pn) } return true } // send an ACK every 2 ack-eliciting packets if h.ackElicitingPacketsReceivedSinceLastAck >= packetsBeforeAck { if h.logger.Debug() { h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.ackElicitingPacketsReceivedSinceLastAck, packetsBeforeAck) } return true } // queue an ACK if there are new missing packets to report if h.hasNewMissingPackets() { h.logger.Debugf("\tQueuing ACK because there's a new missing packet to report.") return true } // queue an ACK if the packet was ECN-CE marked if ecn == protocol.ECNCE { h.logger.Debugf("\tQueuing ACK because the packet was ECN-CE marked.") return true } return false } func (h *appDataReceivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame { now := time.Now() if onlyIfQueued && !h.ackQueued { if h.ackAlarm.IsZero() || h.ackAlarm.After(now) { return nil } if h.logger.Debug() && !h.ackAlarm.IsZero() { h.logger.Debugf("Sending ACK because the ACK timer expired.") } } ack := h.receivedPacketTracker.GetAckFrame() if ack == nil { return nil } ack.DelayTime = max(0, now.Sub(h.largestObservedRcvdTime)) h.ackQueued = false h.ackAlarm = time.Time{} h.ackElicitingPacketsReceivedSinceLastAck = 0 return ack } func (h *appDataReceivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm }