feat(core): SYN+ACK and FIN go once, as in Java, measured with Java FILIUS
TcpRun loses exactly one frame (loseFirst: the card's output buffer drops it; the sender's packet log still shows it, as with setDrop) in three new stories: lostSynAck, lostFin, lostLastFin. Measured: Java never repeats a SYN+ACK or a FIN. The server gives a handshake up after one timeout (3 RTT) and closes with a FIN from SYN_RCVD (its boundary check sometimes lets it wait twice; it means once). After its FIN a socket waits at most five times, each wait ended by a segment or the timeout; then it is gone. The core does the same (TCP_CLOSING_WAITS, with TIME_WAIT in the loop) and shows CLOSED where Java leaves the state standing (a named Java bug). One departure: a repeated SYN in SYN_RCVD gets the SYN+ACK once more and the wait starts anew; Java closes there, but its client never repeats the SYN. lostSynAck is compared up to the second SYN (JAVA_UNTIL); the rest is an engine test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
|
||||
import {
|
||||
deviceConfiguration,
|
||||
type EthernetFrame,
|
||||
type TcpSegment,
|
||||
NetworkRuntimeEngine,
|
||||
portSnapshot,
|
||||
topologySnapshot,
|
||||
@@ -62,3 +64,23 @@ export function letFramesArrive(engine: NetworkRuntimeEngine): void {
|
||||
engine.handle({ type: 'advance', toMilliseconds: Math.min(...deadlines) });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The first frame the node sends whose TCP segment matches is lost, as with
|
||||
* TcpRun's loseFirst: it leaves (the sender's card shows it) and never arrives.
|
||||
*/
|
||||
export function loseFirst(engine: NetworkRuntimeEngine, nodeId: Id, which: (segment: TcpSegment) => boolean): void {
|
||||
type Depart = (fromNodeId: Id, portId: Id, endpoint: unknown, frame: EthernetFrame) => void;
|
||||
const target = engine as unknown as { departEthernetFrame: Depart };
|
||||
const depart = target.departEthernetFrame.bind(engine);
|
||||
let lost = false;
|
||||
target.departEthernetFrame = (fromNodeId, portId, endpoint, frame) => {
|
||||
depart(fromNodeId, portId, endpoint, frame);
|
||||
const payload = frame.payload.kind === 'ipv4' ? frame.payload.packet.payload : undefined;
|
||||
if (lost || fromNodeId !== nodeId || payload?.kind !== 'tcp' || !which(payload.segment)) return;
|
||||
lost = true;
|
||||
engine.state.pendingEvents = engine.state.pendingEvents.filter(
|
||||
(event) => !(event.kind.kind === 'ethernetLinkDelivery' && event.kind.frame.identity === frame.identity),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user