engine: downstream MTU and downstream train in the measurement document

Three facts the client cannot produce alone, kept deliberately separate:
mtu.pmtud_down (largest datagram that arrives unfragmented — meaningful only
because the server sets DF), mtu.frag_delivery (whether larger ones arrive once
fragmentation is allowed), and train.udp_downstream (loss, reordering and
arrival spacing in the download direction, which a round trip cannot separate
from upstream loss).

ServerMeasurement now runs them on the same ProbeSession as the echo train. It
had to: a fresh session restarts client-side sequence numbers and the server's
anti-replay window discards the lot, so the re-primed source is never recorded
and every granted send goes to a socket that has already closed. That produced
four confidently-wrong FAILED tests and a RED verdict on a healthy network.

Live against fmr: path MTU 1500, fragments to 4000, 100/100 downstream, GREEN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mrambossek
2026-08-01 10:54:38 +02:00
co-authored by Claude Fable 5
parent ce1aaa332a
commit 14e5fad1b2
18 changed files with 911 additions and 127 deletions
@@ -69,6 +69,8 @@ object TestType {
const val TRACEROUTE_ICMP6 = "traceroute.icmp6"
// train
const val TRAIN_UDP_UPDOWN = "train.udp_updown"
/** Server-to-client train under a §3.4 grant: the direction a round trip cannot separate. */
const val TRAIN_UDP_DOWNSTREAM = "train.udp_downstream"
// mtu
const val MTU_PMTUD_UP = "mtu.pmtud_up"
const val MTU_PMTUD_DOWN = "mtu.pmtud_down"
@@ -69,6 +69,8 @@ object TestType {
const val TRACEROUTE_ICMP6 = "traceroute.icmp6"
// train
const val TRAIN_UDP_UPDOWN = "train.udp_updown"
/** Server-to-client train under a §3.4 grant: the direction a round trip cannot separate. */
const val TRAIN_UDP_DOWNSTREAM = "train.udp_downstream"
// mtu
const val MTU_PMTUD_UP = "mtu.pmtud_up"
const val MTU_PMTUD_DOWN = "mtu.pmtud_down"