Skip to content

Fix spurious SACKs by respecting delayed ACK timer#94

Merged
boivie merged 1 commit intowebrtc:mainfrom
boivie:bug-spurious-sack
Feb 16, 2026
Merged

Fix spurious SACKs by respecting delayed ACK timer#94
boivie merged 1 commit intowebrtc:mainfrom
boivie:bug-spurious-sack

Conversation

@boivie
Copy link
Copy Markdown
Collaborator

@boivie boivie commented Feb 16, 2026

SCTP employs a "Delayed Acknowledgement" algorithm (RFC 9260, section 6.2) to reduce network traffic. Instead of immediately acknowledging every received DATA chunk, the protocol allows the receiver to delay sending a Selective Acknowledgement (SACK) for up to 200ms (or until a second packet arrives). This allows the SACK to be "piggybacked" onto outgoing user data or bundled with other control chunks, reducing the number of small packets sent.

Before this change, the Socket::advance_time method broke this by unconditionally flushing pending packets. Whenever advance_time was called (which the application is allowed to do at any time), it would trigger packet transmission logic that forced any pending SACK to be sent immediately. This broke the delayed SACK optimization, resulting in spurious, standalone SACK transmissions whenever the socket clock advanced (when there was a pending and delayed SACK).

This commit fixes the issue by ensuring advance_time only attempts to send packets if a timer (such as the delayed ACK timer) has actually expired. And it also modifies the packet builder to only include a pending SACK if it can be bundled with outgoing data/retransmissions, or if the delayed ACK timer has explicitly expired.

SCTP employs a "Delayed Acknowledgement" algorithm (RFC 9260, 
section 6.2) to reduce network traffic. Instead of immediately 
acknowledging every received DATA chunk, the protocol allows the 
receiver to delay sending a Selective Acknowledgement (SACK) for up to 
200ms (or until a second packet arrives). This allows the SACK to be 
"piggybacked" onto outgoing user data or bundled with other control 
chunks, reducing the number of small packets sent.

Before this change, the `Socket::advance_time` method broke this by
unconditionally flushing pending packets. Whenever `advance_time` was
called (which the application is allowed to do at any time), it would 
trigger packet transmission logic that forced any pending SACK to be 
sent immediately. This broke the delayed SACK optimization, resulting in 
spurious, standalone SACK transmissions whenever the socket clock 
advanced (when there was a pending and delayed SACK).

This commit fixes the issue by ensuring `advance_time` only attempts to 
send packets if a timer (such as the delayed ACK timer) has actually 
expired. And it also modifies the packet builder to only include a 
pending SACK if it can be bundled with outgoing data/retransmissions, or 
if the delayed ACK timer has explicitly expired.
@boivie boivie merged commit 82e5143 into webrtc:main Feb 16, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants