diff options
| author | Dimitri Staessens <dimitri@ouroboros.rocks> | 2026-05-19 22:25:47 +0200 |
|---|---|---|
| committer | Sander Vrijders <sander@ouroboros.rocks> | 2026-05-20 08:17:07 +0200 |
| commit | 1566d9b6e273510b376adaa1ce920152a089132f (patch) | |
| tree | 418867b780d8144fae0892404809c60aa581ac1b /src/lib/frct.c | |
| parent | cbf7f953a49a98adfc4803340475ffeccefbe9fb (diff) | |
| download | ouroboros-1566d9b6e273510b376adaa1ce920152a089132f.tar.gz ouroboros-1566d9b6e273510b376adaa1ce920152a089132f.zip | |
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 <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/lib/frct.c')
| -rw-r--r-- | src/lib/frct.c | 26 |
1 files changed, 24 insertions, 2 deletions
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; |
