feat(core): ARP after Java, measured with Java FILIUS
An ARP lookup asks, waits a round trip, asks once more and gives up after another round trip; then host unreachable, observed at the sender itself or sent back by the router (for ICMP only for an echo request). A waiting sender wakes after what else happens at that time. A ping then waits a round trip for its answer, counted from when it left. Swift asks twice at once while events run and lets the packet wait without end when the address exists anywhere in the network; the option swiftArp keeps that for the Swift comparison, and three scenarios are compared with Swift until the trace where ARP fails or a ping goes unanswered. arpJava.test.ts compares ArpRun: both pings match; the two UDP stories wait for Java's transport thread (next step). DNS without a server now matches Java too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1531,3 +1531,60 @@ describe('DNS', () => {
|
||||
expect(() => lan({ '10.30.0.2': [] }, true)).toThrow(NotPortedError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ARP', () => {
|
||||
const requestsFor = (engine: NetworkRuntimeEngine, ipAddress: string) =>
|
||||
engine.state.packetTraces.filter((trace) => trace.detail === `ARP request for ${ipAddress}`).map((trace) => trace.timeMilliseconds);
|
||||
|
||||
it('asks twice, a round trip apart, then gives up', () => {
|
||||
const { engine, sourceNodeId } = routedTopology();
|
||||
const called = engine.state.currentTimeMilliseconds;
|
||||
|
||||
expect(engine.resolveMacAddress(sourceNodeId, '10.0.0.9')).toBeUndefined();
|
||||
expect(requestsFor(engine, '10.0.0.9')).toEqual([called, called + 2_750]);
|
||||
expect(engine.state.currentTimeMilliseconds).toBe(called + 2 * 2_750);
|
||||
});
|
||||
|
||||
it('lets a router answer host unreachable for an echo request, not for an echo reply', () => {
|
||||
const { engine, sourceNodeId } = routedTopology();
|
||||
const toNowhere = (kind: 'echoRequest' | 'echoReply', identity: number): IPv4Packet => ({
|
||||
identity,
|
||||
senderIPAddress: '10.0.0.2',
|
||||
receiverIPAddress: '20.0.0.9',
|
||||
timeToLive: 64,
|
||||
protocol: 'icmp',
|
||||
payload: { kind: 'icmp', message: icmpMessage(kind, { identifier: identity, sequenceNumber: 1, data: new Uint8Array() }) },
|
||||
});
|
||||
const observed = () => (engine.state.icmpObservationsByNodeId.get(sourceNodeId) ?? []).map((each) => each.message.kind);
|
||||
|
||||
engine.sendIPv4Packet(sourceNodeId, toNowhere('echoReply', 901));
|
||||
engine.handle({ type: 'advance', toMilliseconds: 10_000 });
|
||||
expect(observed()).toEqual([]);
|
||||
|
||||
engine.sendIPv4Packet(sourceNodeId, toNowhere('echoRequest', 902));
|
||||
engine.handle({ type: 'advance', toMilliseconds: 20_000 });
|
||||
expect(observed()).toEqual(['destinationHostUnreachable']);
|
||||
});
|
||||
|
||||
it('forgets the lookups that wait on stop', () => {
|
||||
const [firstId, firstPortId, secondId, secondPortId] = [0x1a01, 0x1a02, 0x1a03, 0x1a04].map(uuid) as [string, string, string, string];
|
||||
const input = (secondIp: string) => ({
|
||||
nodes: [pc(firstId, firstPortId), pc(secondId, secondPortId)],
|
||||
links: [link(firstId, firstPortId, secondId, secondPortId)],
|
||||
deviceConfigurations: new Map([
|
||||
[firstId, device('10.0.0.2')],
|
||||
[secondId, device(secondIp)],
|
||||
]),
|
||||
});
|
||||
const engine = startedEngine(input('10.0.0.8'));
|
||||
const socket = engine.bindUdpSocket(firstId, { remoteIPAddress: '10.0.0.9', remotePort: 9 })!;
|
||||
engine.sendUdp(socket, new TextEncoder().encode('lost'));
|
||||
|
||||
engine.handle({ type: 'stop' });
|
||||
engine.handle({ type: 'start', snapshot: topologySnapshot(input('10.0.0.9')), seed: 14, initialTimeMilliseconds: 0 });
|
||||
expect(engine.resolveMacAddress(firstId, '10.0.0.9')).toBeDefined();
|
||||
engine.handle({ type: 'advance', toMilliseconds: 1_000 });
|
||||
|
||||
expect(engine.state.packetTraces.filter((trace) => trace.layer === 'network' && trace.operation === 'sent')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user