Skip to content

ACK frames are unnecessarily limited to 32 ranges #3641

@larseggert

Description

@larseggert

Problem

write_frame in tracking.rs caps ACK frames at MAX_ACKS_PER_FRAME (32) ranges to "keep plenty of space for other stuff." This cap is overly conservative in two situations:

  1. ACK-only packets (cwnd < 256): These are limited to 255 bytes by ACK_ONLY_SIZE_LIMIT, which fits only ~13 ranges. The cap of 32 never fires — the space constraint is always binding. These packets could use the full MTU instead, since ACK-only packets are exempt from congestion control per RFC 9002 Section 6.1.

  2. Non-ACK-only packets with nothing else to send: When cwnd >= 256 but no data or control frames are queued, the packet is effectively ACK-only, yet the 32-range cap still applies. At a 1200-byte MTU, up to ~72 ranges could fit, but 40 of those slots go unused.

Impact

After #3639 increased MAX_TRACKED_RANGES to 128, we can now track many more ranges — but we can only communicate 32 per frame. Under reordering, this means it takes more round trips to acknowledge all received packets, prolonging the window where the sender might falsely declare packets lost.

Possible approaches

  • Remove MAX_ACKS_PER_FRAME entirely. The space-based formula 1 + (avail / 16) already uses a pessimistic 16-bytes-per-range estimate. In practice, most ranges encode much smaller, so even at the calculated maximum, the ACK frame leaves substantial room for other frames. The cap is belt-and-suspenders on top of an already conservative estimate.

  • Lift ACK_ONLY_SIZE_LIMIT to the full MTU. Since ACK-only packets don't count against the congestion window (RFC 9002 Section 6.1), there's no reason to artificially constrain them to 255 bytes. This was an implementation choice from Pacing #618 (2020), not an RFC requirement.

  • Write other frames first, ACK last. This would let the ACK frame fill whatever space remains, but is a more invasive change.

Context

ACK_ONLY_SIZE_LIMIT (256) was introduced in #618 and MAX_ACKS_PER_FRAME (32) in #998, both without recorded discussion about the specific values.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions