From 1566d9b6e273510b376adaa1ce920152a089132f Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Tue, 19 May 2026 22:25:47 +0200 Subject: lib: Skip DRF rebase on same-epoch retransmit On some stalls the receiver can go inactive and NACK-driven HoL retransmits and brief snd-window drains can deliver a DRF-flagged packet that's still part of the current receive epoch (rxm_pkt_prepare preserves the original flags). This treats in-window DRF and RXM-flagged DRF arrivals as same-epoch once the window is seeded and skip the release_rq + lwe/rwe rebase that would otherwise wipe valid OOO fragments from rq[]. Bootstrap (lwe == rwe) still rebases unconditionally so a NACK-driven retransmit of a lost initial DRF can seed the receiver. Harden seqno_rotate to redraw the ISN until it falls outside the peer's current rcv window, closing the ~RQ_SIZE / 2^32 collision where a true new epoch would be misclassified as same-epoch. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/lib/frct.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/lib/frct.c b/src/lib/frct.c index 1d583162..17fca272 100644 --- a/src/lib/frct.c +++ b/src/lib/frct.c @@ -588,6 +588,22 @@ static __inline__ bool within(uint32_t seq, uint32_t lo, uint32_t hi) return after(seq, lo) && !after(seq, hi); } +static __inline__ bool in_window(uint32_t seq, const struct frct_cr * cr) +{ + return !before(seq, cr->lwe) && before(seq, cr->rwe); +} + +/* DRF arrival that stays within the current receive epoch. */ +static __inline__ bool same_epoch_drf(uint32_t seq, + uint16_t flags, + const struct frct_cr * cr) +{ + if (cr->lwe == cr->rwe) + return false; + + return (flags & FRCT_RXM) || in_window(seq, cr); +} + /* * RACK reorder window R (RFC 8985 ยง6.2): * R = MIN(reo_wnd_mult * RACK.min_RTT / 4, SRTT) @@ -3156,7 +3172,10 @@ static enum frct_act rcv_inact_check(struct frcti * frcti, return FRCT_ACTIVE; if (flags & FRCT_DRF) { - /* Release stale rq[] slots before rebasing. */ + if (same_epoch_drf(seqno, flags, rcv_cr)) + return FRCT_ACTIVE; + + /* Bootstrap or fresh epoch: rebase. */ release_rq(frcti); STORE_RELEASE(&rcv_cr->lwe, seqno); rcv_cr->rwe = seqno + RQ_SIZE; @@ -3306,7 +3325,10 @@ static void seqno_rotate(struct frcti * frcti, if (snd_cr->seqno != snd_cr->lwe) return; - random_buffer(&snd_cr->seqno, sizeof(snd_cr->seqno)); + /* Avoid colliding with peer's current rcv window. */ + do { + random_buffer(&snd_cr->seqno, sizeof(snd_cr->seqno)); + } while (in_window(snd_cr->seqno, snd_cr)); STORE_RELEASE(&snd_cr->lwe, snd_cr->seqno); STORE_RELAXED(&snd_cr->rwe, snd_cr->lwe + START_WINDOW); frcti->rtt_lwe = snd_cr->seqno; -- cgit v1.2.3