summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-05-19 22:25:47 +0200
committerSander Vrijders <sander@ouroboros.rocks>2026-05-20 08:17:07 +0200
commit1566d9b6e273510b376adaa1ce920152a089132f (patch)
tree418867b780d8144fae0892404809c60aa581ac1b /src/lib
parentcbf7f953a49a98adfc4803340475ffeccefbe9fb (diff)
downloadouroboros-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')
-rw-r--r--src/lib/frct.c26
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;