Skip to content

Make SCTP stream reset and reuse generation-safe - #485

Closed
idy wants to merge 10 commits into
pion:mainfrom
GizClaw:fix
Closed

idy wants to merge 10 commits into
pion:mainfrom
GizClaw:fix

Conversation

@idy

@idy idy commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What changed

  • expose reset-completion notifications and negotiated stream limits
  • retain stream ownership until both reset directions have completed
  • keep delayed acknowledgements and retransmitted reset requests from affecting a reused stream ID
  • preserve established associations when a duplicate INIT arrives
  • separate handshake retransmission limits from data retransmission limits
  • copy configured inbound and outbound stream limits into client and server association options

Why

WebRTC needs to reuse SCTP stream IDs after a DataChannel closes, but an ID is not safe to reuse until the reset procedure has completed. Delayed acknowledgements or a retransmitted reset request from the previous stream generation must also not close or mutate the new generation.

The handshake retry limit is separated because using the data retransmission limit for INIT/COOKIE retries can either terminate association setup too early or make data retransmission behavior unexpectedly permissive.

Relationship to #463

This is an alternative implementation that covers the stream-limit and reset-completion goals of #463 and adds the lifecycle and retransmission handling needed by the corresponding WebRTC integration.

This PR is not intended to invalidate the work in #463. If maintainers prefer to continue with #463, please feel free to close this PR and reuse or adapt any tests and fixes that are useful.

Validation

  • go test ./...
  • go test -race ./...
  • go vet ./...
  • staticcheck .

idy added 6 commits August 6, 2026 20:30
WebRTC needs the negotiated stream bounds and a definitive reset boundary
before it can safely reuse DataChannel IDs on a long-lived association.

- Report negotiated inbound and outbound stream counts in association metadata
- Notify consumers only after a reset removes the matching stream
- Cover asymmetric negotiation, pending resets, duplicate resets, and both peers

Refs: #1, GizClaw/gizclaw#776, pion#463
Upstream-base: 94ddb36

Generated with [Codex](https://github.com/openai)
Release a reset stream identifier only after the local outgoing reset is acknowledged and the peer outgoing reset has removed the inbound stream. This prevents a newly opened DataChannel from colliding with the previous stream generation.
A retransmitted INIT can arrive after its COOKIE ECHO has already moved the
receiver to Established. Treating that delayed packet as a fatal state error
tears down an otherwise healthy association under load.

- Recognize the current handshake by its port pair and peer verification tag
- Ignore only matching duplicate INIT packets without mutating the live TCB
- Keep rejecting INIT packets that represent a different association
- Cover both duplicate and new-association behavior in the state tests

Generated with [Codex](https://github.com/openai)
Allow callers to cap T1-init and T1-cookie independently from DATA, reconfiguration, and shutdown retransmission timers. Preserve the existing WithRTOMax behavior when no handshake-specific value is configured.\n\nRefs: GizClaw/gizclaw#700\nUpstream-base: 94ddb36\n\nGenerated with [Codex](https://github.com/openai)
Retransmitted reset requests could arrive after a completed stream ID had
already been reused, deleting the new stream generation. The deprecated
Config path also dropped explicit negotiated stream limits.

- Track the peer reconfiguration sequence and replay the last response
- Reject stale reset requests before they can mutate a reused stream
- Preserve inbound and outbound stream limits through Config options
- Cover duplicate requests, stale requests, and Config propagation

Generated with [Codex](https://github.com/openai)
@idy
idy marked this pull request as ready for review August 7, 2026 07:40
@JoTurk

JoTurk commented Aug 7, 2026

Copy link
Copy Markdown
Member

hello, can this effort merged with @pando-emil in #463 as single PR or after their PR is merged (currently the CI is failing and requires a review)?

@idy

idy commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Yes, absolutely. We are happy to follow whichever approach the maintainers prefer. We can combine this work with @pando-emil's #463 into a single PR, or rebase the additional fixes and tests on top after #463 is merged. Please let us know which path is easiest to review, and we will adapt #485 accordingly.

idy and others added 2 commits August 27, 2026 22:15
Tail loss probes now restart T3 from the current RTO while PTO remains one-shot, preventing concurrent recovery from being delayed past the caller deadline.
@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.00000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.40%. Comparing base (94ddb36) to head (d1608eb).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
association.go 95.49% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #485      +/-   ##
==========================================
+ Coverage   85.36%   85.40%   +0.03%     
==========================================
  Files          56       56              
  Lines        5458     5555      +97     
==========================================
+ Hits         4659     4744      +85     
- Misses        561      567       +6     
- Partials      238      244       +6     
Flag Coverage Δ
go 85.40% <96.00%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

An incoming Outgoing SSN Reset Request whose senderLastTSN is ahead of
the cumulative TSN is answered "In progress" and kept pending. Pending
requests were only re-checked when DATA advanced the cumulative TSN, and
retransmissions of the pending RSN replayed the cached "In progress"
response. When the gap was closed by an abandoned message's
(I-)FORWARD-TSN, the reset never completed.

Re-check pending requests whenever the cumulative TSN advances,
including via FORWARD-TSN and I-FORWARD-TSN, and re-evaluate a
retransmitted request that is still pending instead of replaying the
cached response. Completed requests keep the RSN ordering protections.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@idy

idy commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #494, which is opened from a branch scoped to the stream reset/reuse changes only. The duplicate-INIT and handshake retransmission limit fixes that were bundled here now live on separate branches.

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