Checksum the header and refuse sequence numbers the sender never sent

This commit is contained in:
flamingcow
2026-08-04 20:41:16 -07:00
parent 703039587b
commit 1d54a1a1f6
9 changed files with 241 additions and 35 deletions
+25 -4
View File
@@ -18,19 +18,39 @@ type lossWindow struct {
bits []uint64
lost atomic.Uint64
late atomic.Uint64
// The sending half of this stream. Both halves are in this process and the
// receiving socket ignores its own outgoing frames, so this window is fed by
// that sender alone and by nothing else on the wire.
sent *atomic.Uint64
}
func newLossWindows(n int) []lossWindow {
w := make([]lossWindow, n)
// Takes the sending halves rather than a count, so a window cannot be built
// without the bound it judges sequence numbers against.
func newLossWindows(tx []*txStats) []lossWindow {
w := make([]lossWindow, len(tx))
for i := range w {
w[i].bits = make([]uint64, lossWords)
w[i].sent = &tx[i].sent
}
return w
}
// A sequence number is only judged once it falls out of the window, so frames
// still queued in another rx worker are never miscounted as lost.
func (w *lossWindow) observe(seq uint64) {
//
// Reports whether the sequence number could have come off the wire at all. One
// above what the sender has reached is a damaged header rather than a gap, and
// is refused before it can touch the base. Believing one would drag the window
// up past every real sequence, write off the span in between as lost, and leave
// every frame after it arriving below the base and counted late for the rest of
// the run. Nothing that arrives later is evidence enough to undo that, which is
// why the sender's own frontier is the guard rather than a limit on how far a
// single step may move.
func (w *lossWindow) observe(seq uint64) bool {
if seq >= w.sent.Load() {
return false
}
w.mu.Lock()
if !w.inited {
// Start half a window below the first sequence seen, so frames another
@@ -43,7 +63,7 @@ func (w *lossWindow) observe(seq uint64) {
if seq < w.base {
w.mu.Unlock()
w.late.Add(1)
return
return true
}
if seq >= w.base+lossSlots {
w.evict(seq - lossSlots + 1)
@@ -51,6 +71,7 @@ func (w *lossWindow) observe(seq uint64) {
idx := seq & (lossSlots - 1)
w.bits[idx>>6] |= 1 << (idx & 63)
w.mu.Unlock()
return true
}
func (w *lossWindow) evict(newBase uint64) {