feat(core): Java's IP and ICMP threads, measured with Java FILIUS
Every packet that comes in goes through its node's IP thread (UDP, TCP) or ICMP thread (ICMP), as Java's IPThread and ICMPThread: deliver, answer an echo request or forward, one after the other; while one waits for ARP, the next wait behind it. A packet a node sends to itself is delivered at once, as Java's IP.senden does. The transport send threads and these are one mechanism now (handToProtocolThread). ArpRun has three stories through the router (routedUdpBehind, routedPingBesideUdp, routedPingBehindPing); the oracle writes ICMP frames with type, code, addresses and sequence number. All seven ArpRun stories match Java FILIUS. Measured and not taken: Java's ARP wakes one waiter with notify, not the one whose answer came; the core wakes all that wait for the address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -13,7 +13,7 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
|
|||||||
| `src/model/link.ts`, `src/model/graph.ts` | `Model/TopologyLink.swift`, `Model/TopologyGraph.swift` | übertragen, bisher nur die Daten (Abfragen folgen mit dem Editor) |
|
| `src/model/link.ts`, `src/model/graph.ts` | `Model/TopologyLink.swift`, `Model/TopologyGraph.swift` | übertragen, bisher nur die Daten (Abfragen folgen mit dem Editor) |
|
||||||
| `src/model/documentation.ts` | `Model/TopologyDocumentation.swift` | übertragen; FNV-Kennung mit BigInt, gegen die IDs aus dem Swift-Import geprüft |
|
| `src/model/documentation.ts` | `Model/TopologyDocumentation.swift` | übertragen; FNV-Kennung mit BigInt, gegen die IDs aus dem Swift-Import geprüft |
|
||||||
| `src/runtime/config.ts` | `State/TopologyEditorState.swift` (Laufzeit-Konfigurationen, WLAN eines Rechners, DHCP, Portweiterleitung, Remote Link ohne LAN-Transport, Programme und ihre Konfiguration, Firewall, DNS-Einträge aus `State/TopologyDNSResolver.swift`, E-Mail-Konten und -Nachrichten aus `State/TopologyEmail.swift`); dazu Echo-Server- und Client-Konfiguration, die Swift nicht speichert | übertragen; Java-Beschriftungen der Oberfläche weggelassen |
|
| `src/runtime/config.ts` | `State/TopologyEditorState.swift` (Laufzeit-Konfigurationen, WLAN eines Rechners, DHCP, Portweiterleitung, Remote Link ohne LAN-Transport, Programme und ihre Konfiguration, Firewall, DNS-Einträge aus `State/TopologyDNSResolver.swift`, E-Mail-Konten und -Nachrichten aus `State/TopologyEmail.swift`); dazu Echo-Server- und Client-Konfiguration, die Swift nicht speichert | übertragen; Java-Beschriftungen der Oberfläche weggelassen |
|
||||||
| `src/runtime/engine.ts` | `State/TopologyNetworkRuntimeEngine.swift` Z. ~313–3585, ~4472–5187 (UDP), ~4753–4850 | übertragen: Warteschlange, Uhr, Ethernet, Switch, ARP, IPv4, ICMP, Routing, Firewall, UDP-Sockets (binden, freie Ports nach Javas Zufall, senden samt Broadcast, zustellen, empfangen mit Wartezeit, schließen), TCP (Z. ~5187–5913: Server- und Client-Sockets, Handschlag mit Ergebnis, Segmente zu 1460 Byte bis zum PSH, höchstens drei Versuche, Abbau über alle Java-Zustände samt `TIME_WAIT`; seit F2.3c nach Javas `TCPSocket` an Java-Filius gemessen: je Versuch und für `TIME_WAIT` 3 RTT nach der Geschwindigkeit (8250 ms bei 90 %, Swift fest 7500 bzw. 2500 ms), der Zeitablauf zählt ab der Übergabe des Segments, vor ARP; ein Segment beendet `TIME_WAIT`; die Gegenseite schließt beim FIN von selbst; nicht bestätigte Daten schließen mit FIN statt sofort; der angenommene Socket hat die Startnummer des Listeners, der Listener eine neue. seit F2.3e wie Java: SYN+ACK und FIN gehen nie ein zweites Mal hinaus; der Server gibt einen Handschlag nach 3 RTT auf und schließt mit FIN; nach dem FIN wartet ein Socket höchstens fünfmal 3 RTT, jedes Segment beendet eine Wartezeit, dann CLOSED (Java lässt den Zustand stehen). Abweichend von Java: drei SYN statt einem, weil Javas Wiederholungsschleife nach dem ersten Zeitablauf abbricht, und ein wiederholtes SYN in `SYN_RCVD` bekommt das SYN+ACK noch einmal, statt den Socket zu schließen), DHCP (Z. ~5913–6694: Server mit festen Zuordnungen, Adressbereich, Angeboten, Leases und Sperrliste; Client seit F2.3b nach Javas `DHCPClient` neu gebaut und an Java-Filius gemessen: je Versuch eine Nachricht, eine Antwort, die nicht passt, wird gelesen und verworfen; die ARP-Prüfung wartet eine RTT, eine Antwort für die Adresse beendet sie sofort, dann DECLINE; nach einem Zeitablauf wiederholt der Zustand seine Nachricht, nach dem fünften nimmt der Client eine Link-Local-Adresse `169.254.1.x/16` (ARP-Probe, ARP-Reply an alle) und gibt keine alte Adresse zurück; Swift sendete doppelt, wartete nicht auf ARP und kehrte nach zehn Versuchen zur alten Adresse zurück. Abweichend von Java: Nach fünf belegten Link-Local-Adressen gibt der Client auf (Java sucht endlos), und ein ARP-Paket für eine andere Adresse beendet die Prüfung nicht. Seit F2.3d befördert ein Kabel einen Frame nach dem anderen wie Javas `SimplexVerbindung` (ein Frame wartet im Ausgangspuffer, bis der vorige angekommen ist, und erscheint im Protokoll, wenn er losgeht); Swift schickt alle zugleich, die Option `swiftCables` behält das für den Swift-Vergleich. Die RTT der DHCP-Zeiten (Warten, Angebote, Sperrliste) folgt wie in Java der Geschwindigkeit (50 Kabel, 2750 ms bei 90 %); Swift rechnet fest mit 2500 ms. ARP seit F2.4b nach Javas `holeARPTabellenEintrag`, an Java-Filius gemessen: eine Auflösung fragt, wartet eine RTT, fragt ein zweites Mal und gibt nach einer weiteren RTT auf; dann „Host unreachable" beim Absender selbst oder vom Router zurück (bei ICMP nur für Echo-Anfragen); ein wartender Absender wacht nach den übrigen Ereignissen desselben Zeitpunkts auf; der Ping wartet danach eine RTT auf die Antwort. Swift schickt in der Ereignisverarbeitung beide Anfragen zugleich und lässt das Paket ohne Ende warten, wenn die Adresse irgendwo im Netz vorkommt (Wissen, das kein Knoten hat), und blockiert direkte Aufrufe ohne Wartezeit; die Option `swiftArp` behält das für den Swift-Vergleich. Seit F2.4b-2 kehrt ein Senden wie in Java sofort zurück, die Frames reisen, während die Uhr läuft (Swift: der Aufruf kehrt erst zurück, wenn alle Frames angekommen sind; die Option `swiftSends` behält das für den Swift-Vergleich), und UDP und TCP senden über Javas Transport-Thread je Knoten und Protokoll: ein Segment nach dem anderen; solange IP auf ARP wartet, blockiert das nächste `senden`. Wartende Absender kommen in der Reihenfolge an die Reihe, in der sie kamen (Javas Sperre überlässt das dem Zufall). DNS seit F2.4a nach Javas `DNSServer`/`Resolver`, an Java-Filius gemessen: Server auf Port 53 mit Einträgen aus dem Modell (die Antwort wiederholt die Frage nicht, RCODE immer 0, ein Verweis mit NS und A im Antwortteil), iterativer Resolver mit 5 RTT je Anfrage, der nur die Server behält, an die er verwiesen wurde; noch ohne Rekursion. `receiveUdp` kehrt wie Javas `empfangen` zurück, sobald ein Datagramm da ist. Klasse statt Werttyp. Noch nicht übertragen (wirft `NotPortedError`): Gateway-NAT, DNS, RIP, Remote Link, WLAN |
|
| `src/runtime/engine.ts` | `State/TopologyNetworkRuntimeEngine.swift` Z. ~313–3585, ~4472–5187 (UDP), ~4753–4850 | übertragen: Warteschlange, Uhr, Ethernet, Switch, ARP, IPv4, ICMP, Routing, Firewall, UDP-Sockets (binden, freie Ports nach Javas Zufall, senden samt Broadcast, zustellen, empfangen mit Wartezeit, schließen), TCP (Z. ~5187–5913: Server- und Client-Sockets, Handschlag mit Ergebnis, Segmente zu 1460 Byte bis zum PSH, höchstens drei Versuche, Abbau über alle Java-Zustände samt `TIME_WAIT`; seit F2.3c nach Javas `TCPSocket` an Java-Filius gemessen: je Versuch und für `TIME_WAIT` 3 RTT nach der Geschwindigkeit (8250 ms bei 90 %, Swift fest 7500 bzw. 2500 ms), der Zeitablauf zählt ab der Übergabe des Segments, vor ARP; ein Segment beendet `TIME_WAIT`; die Gegenseite schließt beim FIN von selbst; nicht bestätigte Daten schließen mit FIN statt sofort; der angenommene Socket hat die Startnummer des Listeners, der Listener eine neue. seit F2.3e wie Java: SYN+ACK und FIN gehen nie ein zweites Mal hinaus; der Server gibt einen Handschlag nach 3 RTT auf und schließt mit FIN; nach dem FIN wartet ein Socket höchstens fünfmal 3 RTT, jedes Segment beendet eine Wartezeit, dann CLOSED (Java lässt den Zustand stehen). Abweichend von Java: drei SYN statt einem, weil Javas Wiederholungsschleife nach dem ersten Zeitablauf abbricht, und ein wiederholtes SYN in `SYN_RCVD` bekommt das SYN+ACK noch einmal, statt den Socket zu schließen), DHCP (Z. ~5913–6694: Server mit festen Zuordnungen, Adressbereich, Angeboten, Leases und Sperrliste; Client seit F2.3b nach Javas `DHCPClient` neu gebaut und an Java-Filius gemessen: je Versuch eine Nachricht, eine Antwort, die nicht passt, wird gelesen und verworfen; die ARP-Prüfung wartet eine RTT, eine Antwort für die Adresse beendet sie sofort, dann DECLINE; nach einem Zeitablauf wiederholt der Zustand seine Nachricht, nach dem fünften nimmt der Client eine Link-Local-Adresse `169.254.1.x/16` (ARP-Probe, ARP-Reply an alle) und gibt keine alte Adresse zurück; Swift sendete doppelt, wartete nicht auf ARP und kehrte nach zehn Versuchen zur alten Adresse zurück. Abweichend von Java: Nach fünf belegten Link-Local-Adressen gibt der Client auf (Java sucht endlos), und ein ARP-Paket für eine andere Adresse beendet die Prüfung nicht. Seit F2.3d befördert ein Kabel einen Frame nach dem anderen wie Javas `SimplexVerbindung` (ein Frame wartet im Ausgangspuffer, bis der vorige angekommen ist, und erscheint im Protokoll, wenn er losgeht); Swift schickt alle zugleich, die Option `swiftCables` behält das für den Swift-Vergleich. Die RTT der DHCP-Zeiten (Warten, Angebote, Sperrliste) folgt wie in Java der Geschwindigkeit (50 Kabel, 2750 ms bei 90 %); Swift rechnet fest mit 2500 ms. ARP seit F2.4b nach Javas `holeARPTabellenEintrag`, an Java-Filius gemessen: eine Auflösung fragt, wartet eine RTT, fragt ein zweites Mal und gibt nach einer weiteren RTT auf; dann „Host unreachable" beim Absender selbst oder vom Router zurück (bei ICMP nur für Echo-Anfragen); ein wartender Absender wacht nach den übrigen Ereignissen desselben Zeitpunkts auf; der Ping wartet danach eine RTT auf die Antwort. Swift schickt in der Ereignisverarbeitung beide Anfragen zugleich und lässt das Paket ohne Ende warten, wenn die Adresse irgendwo im Netz vorkommt (Wissen, das kein Knoten hat), und blockiert direkte Aufrufe ohne Wartezeit; die Option `swiftArp` behält das für den Swift-Vergleich. Seit F2.4b-2 kehrt ein Senden wie in Java sofort zurück, die Frames reisen, während die Uhr läuft (Swift: der Aufruf kehrt erst zurück, wenn alle Frames angekommen sind; die Option `swiftSends` behält das für den Swift-Vergleich), und UDP und TCP senden über Javas Transport-Thread je Knoten und Protokoll: ein Segment nach dem anderen; solange IP auf ARP wartet, blockiert das nächste `senden`. Wartende Absender kommen in der Reihenfolge an die Reihe, in der sie kamen (Javas Sperre überlässt das dem Zufall). Seit F2.4b-3 geht jedes ankommende Paket wie in Java durch den IP-Thread (UDP, TCP) oder den ICMP-Thread (ICMP) seines Knotens: zustellen, Echo-Reply, weiterleiten, eins nach dem anderen; solange ARP wartet, warten die folgenden. Abweichend von Java weckt eine ARP-Antwort alle, die auf die Adresse warten (Javas `notify` weckt einen beliebigen). DNS seit F2.4a nach Javas `DNSServer`/`Resolver`, an Java-Filius gemessen: Server auf Port 53 mit Einträgen aus dem Modell (die Antwort wiederholt die Frage nicht, RCODE immer 0, ein Verweis mit NS und A im Antwortteil), iterativer Resolver mit 5 RTT je Anfrage, der nur die Server behält, an die er verwiesen wurde; noch ohne Rekursion. `receiveUdp` kehrt wie Javas `empfangen` zurück, sobald ein Datagramm da ist. Klasse statt Werttyp. Noch nicht übertragen (wirft `NotPortedError`): Gateway-NAT, DNS, RIP, Remote Link, WLAN |
|
||||||
| `src/runtime/dns.ts` | Java `filius.software.dns.DNSNachricht`, `Query`, `ResourceRecord` | neu nach Java statt nach Swift (Swift ersetzt Javas Textnachrichten durch `FILIUS-DNS-TYPED/1`): Kopfzeile `ID=… QR=… RCODE=… QDCOUNT=… …`, Frage- und Eintragszeilen, Suche nach Namen ohne Rücksicht auf Groß- und Kleinschreibung, NS der längsten Zone, sonst der Wurzel |
|
| `src/runtime/dns.ts` | Java `filius.software.dns.DNSNachricht`, `Query`, `ResourceRecord` | neu nach Java statt nach Swift (Swift ersetzt Javas Textnachrichten durch `FILIUS-DNS-TYPED/1`): Kopfzeile `ID=… QR=… RCODE=… QDCOUNT=… …`, Frage- und Eintragszeilen, Suche nach Namen ohne Rücksicht auf Groß- und Kleinschreibung, NS der längsten Zone, sonst der Wurzel |
|
||||||
| `src/runtime/ipv4.ts` | Engine `parseIPv4`/`sameSubnet` | übertragen |
|
| `src/runtime/ipv4.ts` | Engine `parseIPv4`/`sameSubnet` | übertragen |
|
||||||
| `src/runtime/packets.ts` | Engine, Paketmodelle | übertragen; `Data` → `Uint8Array` |
|
| `src/runtime/packets.ts` | Engine, Paketmodelle | übertragen; `Data` → `Uint8Array` |
|
||||||
@@ -30,7 +30,7 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
|
|||||||
| `test/engine.test.ts` | `TopologyNetworkRuntimeEngineTests.swift` (21 von 72 Tests) | übertragen; `nextInt`- und `nextDouble`-Referenzwerte an OpenJDK 21 gemessen; die DHCP-Client-Tests seit F2.3b nach Javas Verhalten (neun, davon zwei benannte Java-Fehler, die der Kern nicht übernimmt) |
|
| `test/engine.test.ts` | `TopologyNetworkRuntimeEngineTests.swift` (21 von 72 Tests) | übertragen; `nextInt`- und `nextDouble`-Referenzwerte an OpenJDK 21 gemessen; die DHCP-Client-Tests seit F2.3b nach Javas Verhalten (neun, davon zwei benannte Java-Fehler, die der Kern nicht übernimmt) |
|
||||||
| `test/oracle.test.ts`, `test/scenarios/` | — | neu: 14 Szenarien (davon 2 UDP, 3 TCP, 4 DHCP), Ergebnis mit dem Swift-Kern abgeglichen (`tools/swift-oracle`); Schritte für UDP, TCP und DHCP; die vier DHCP-Szenarien folgen seit F2.3b Java, die drei TCP-Szenarien seit F2.3c; sie müssen von Swift abweichen; drei weitere (`firewall-and-loss`, `routed-two-networks`, `switch-lan`) folgen seit F2.4b ab der Spur, an der ARP scheitert oder ein Ping unbeantwortet bleibt, Java und stimmen bis dahin mit Swift |
|
| `test/oracle.test.ts`, `test/scenarios/` | — | neu: 14 Szenarien (davon 2 UDP, 3 TCP, 4 DHCP), Ergebnis mit dem Swift-Kern abgeglichen (`tools/swift-oracle`); Schritte für UDP, TCP und DHCP; die vier DHCP-Szenarien folgen seit F2.3b Java, die drei TCP-Szenarien seit F2.3c; sie müssen von Swift abweichen; drei weitere (`firewall-and-loss`, `routed-two-networks`, `switch-lan`) folgen seit F2.4b ab der Spur, an der ARP scheitert oder ein Ping unbeantwortet bleibt, Java und stimmen bis dahin mit Swift |
|
||||||
| `test/dhcpJava.test.ts`, `test/scenarios/*.java.json`, `test/scenario.ts`, `test/javaRun.ts` | — | neu: die DHCP-Szenarien gegen Java-Filius (`tools/java-oracle`, `DhcpRun`): je Client dieselben Frames in derselben Reihenfolge, Abstände auf 200 ms, dieselben Adressen am Ende |
|
| `test/dhcpJava.test.ts`, `test/scenarios/*.java.json`, `test/scenario.ts`, `test/javaRun.ts` | — | neu: die DHCP-Szenarien gegen Java-Filius (`tools/java-oracle`, `DhcpRun`): je Client dieselben Frames in derselben Reihenfolge, Abstände auf 200 ms, dieselben Adressen am Ende |
|
||||||
| `test/arpJava.test.ts`, `test/scenarios/arp-routed.scenario.json`, `test/scenarios/arp-java-*.java.json` | — | neu: vier ARP-Abläufe gegen Java-Filius (`ArpRun`): Frames, Abstände und wann jeder Aufruf zurückkehrt; die zwei Pings stimmen seit F2.4b-1, die zwei UDP-Abläufe seit F2.4b-2 (Javas Transport-Thread) |
|
| `test/arpJava.test.ts`, `test/scenarios/arp-routed.scenario.json`, `test/scenarios/arp-java-*.java.json` | — | neu: vier ARP-Abläufe gegen Java-Filius (`ArpRun`): Frames, Abstände und wann jeder Aufruf zurückkehrt; die zwei Pings stimmen seit F2.4b-1, die zwei UDP-Abläufe seit F2.4b-2 (Javas Transport-Thread), drei Abläufe durch den Router seit F2.4b-3 (IP- und ICMP-Thread) |
|
||||||
| `test/dnsJava.test.ts`, `test/scenarios/dns-lan.scenario.json`, `test/scenarios/dns-java-*.java.json` | — | neu: fünf DNS-Abfragen gegen Java-Filius (`DnsRun`): Frames, Ergebnis und Dauer; seit F2.4b-1 alle wie Java |
|
| `test/dnsJava.test.ts`, `test/scenarios/dns-lan.scenario.json`, `test/scenarios/dns-java-*.java.json` | — | neu: fünf DNS-Abfragen gegen Java-Filius (`DnsRun`): Frames, Ergebnis und Dauer; seit F2.4b-1 alle wie Java |
|
||||||
| `test/tcpJava.test.ts`, `test/scenarios/tcp-java-*.java.json` | — | neu: sechs TCP-Abläufe gegen Java-Filius (`TcpRun`, drei davon mit genau einem verlorenen Frame, `loseFirst`): Frames mit Flags und Nummern, Socket-Zustände; benannte Java-Fehler (`JAVA_BUGS`: nur ein SYN, der Zustand bleibt nach dem Schließen stehen), `lostSynAck` nur bis zum zweiten SYN verglichen (`JAVA_UNTIL`) |
|
| `test/tcpJava.test.ts`, `test/scenarios/tcp-java-*.java.json` | — | neu: sechs TCP-Abläufe gegen Java-Filius (`TcpRun`, drei davon mit genau einem verlorenen Frame, `loseFirst`): Frames mit Flags und Nummern, Socket-Zustände; benannte Java-Fehler (`JAVA_BUGS`: nur ein SYN, der Zustand bleibt nach dem Schließen stehen), `lostSynAck` nur bis zum zweiten SYN verglichen (`JAVA_UNTIL`) |
|
||||||
| `test/routeTable.test.ts` | `TopologyJavaRouteTableTests.swift` (8 von 12 Tests) | übertragen; Geometrie- und Reducer-Tests folgen mit Editor und Dokument |
|
| `test/routeTable.test.ts` | `TopologyJavaRouteTableTests.swift` (8 von 12 Tests) | übertragen; Geometrie- und Reducer-Tests folgen mit Editor und Dokument |
|
||||||
|
|||||||
@@ -24,8 +24,10 @@
|
|||||||
// A send hands its frame to the card and returns, as in Java; the frames
|
// A send hands its frame to the card and returns, as in Java; the frames
|
||||||
// travel while the clock advances (Swift: the send returns when every frame
|
// travel while the clock advances (Swift: the send returns when every frame
|
||||||
// in flight has arrived; the option swiftSends keeps that for the comparison).
|
// in flight has arrived; the option swiftSends keeps that for the comparison).
|
||||||
// Java's transport send thread: per node and protocol, one segment after the
|
// Java's threads: per node a send thread for UDP and for TCP, the IP thread
|
||||||
// other; while IP waits for ARP, a socket's next send blocks.
|
// (delivers or forwards UDP and TCP) and the ICMP thread (answers, delivers
|
||||||
|
// or forwards ICMP); each takes one unit after the other and holds the rest
|
||||||
|
// while it waits for ARP. A socket's send blocks behind its thread.
|
||||||
// DNS after Java (filius.software.dns: DNSServer, DNSServerMitarbeiter,
|
// DNS after Java (filius.software.dns: DNSServer, DNSServerMitarbeiter,
|
||||||
// Resolver, DNSQueryAgent), not Swift's typed format; recursion not yet.
|
// Resolver, DNSQueryAgent), not Swift's typed format; recursion not yet.
|
||||||
// Java counterparts: filius.hardware.Verbindung, filius.software.netzzugangsschicht,
|
// Java counterparts: filius.hardware.Verbindung, filius.software.netzzugangsschicht,
|
||||||
@@ -557,13 +559,17 @@ interface ArpLookup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java's TransportProtokoll send thread, one per node and protocol: it takes
|
* Java's threads that each take one unit after the other and wait for ARP
|
||||||
* the segments one after the other and hands each to IP, which waits there
|
* with it: per node the send thread of each transport protocol
|
||||||
* for ARP.
|
* (TransportProtokoll: `udp`, `tcp`), the IP thread (IPThread: delivers or
|
||||||
|
* forwards every UDP and TCP packet that comes in, `ip`) and the ICMP thread
|
||||||
|
* (ICMPThread: answers, delivers or forwards every ICMP packet, `icmp`).
|
||||||
*/
|
*/
|
||||||
interface TransportSender {
|
type ProtocolThreadKind = 'udp' | 'tcp' | 'ip' | 'icmp';
|
||||||
|
|
||||||
|
interface ProtocolThread {
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
/** How many segments it has taken, and how many are out. */
|
/** How many units it has taken, and how many it is done with. */
|
||||||
started: number;
|
started: number;
|
||||||
finished: number;
|
finished: number;
|
||||||
readonly queue: ((finished: () => void) => void)[];
|
readonly queue: ((finished: () => void) => void)[];
|
||||||
@@ -616,8 +622,8 @@ export class NetworkRuntimeEngine {
|
|||||||
/** ARP lookups waiting for their answer (Java: a thread in holeARPTabellenEintrag). */
|
/** ARP lookups waiting for their answer (Java: a thread in holeARPTabellenEintrag). */
|
||||||
private arpLookups: ArpLookup[] = [];
|
private arpLookups: ArpLookup[] = [];
|
||||||
private nextArpLookupId = 1;
|
private nextArpLookupId = 1;
|
||||||
/** Per node and transport protocol (`nodeId/udp`, `nodeId/tcp`). */
|
/** Per node and kind (`nodeId/udp`, `nodeId/icmp`, …). */
|
||||||
private transportSenders = new Map<string, TransportSender>();
|
private protocolThreads = new Map<string, ProtocolThread>();
|
||||||
/** Per cable end (node and port): when the last frame sent there arrives. */
|
/** Per cable end (node and port): when the last frame sent there arrives. */
|
||||||
private cableBusyUntilByPort = new Map<string, number>();
|
private cableBusyUntilByPort = new Map<string, number>();
|
||||||
private readonly cablesCarryOneFrameAtATime: boolean;
|
private readonly cablesCarryOneFrameAtATime: boolean;
|
||||||
@@ -703,7 +709,7 @@ export class NetworkRuntimeEngine {
|
|||||||
state.globalPacketLossEnabled = false;
|
state.globalPacketLossEnabled = false;
|
||||||
this.isProcessingScheduledEvents = false;
|
this.isProcessingScheduledEvents = false;
|
||||||
this.arpLookups = [];
|
this.arpLookups = [];
|
||||||
this.transportSenders.clear();
|
this.protocolThreads.clear();
|
||||||
state.pendingEvents = [];
|
state.pendingEvents = [];
|
||||||
state.arpCachesByNodeId.clear();
|
state.arpCachesByNodeId.clear();
|
||||||
state.switchForwardingTablesByNodeId.clear();
|
state.switchForwardingTablesByNodeId.clear();
|
||||||
@@ -1093,7 +1099,7 @@ export class NetworkRuntimeEngine {
|
|||||||
if (destinationIPAddress === LIMITED_BROADCAST_IP_ADDRESS) {
|
if (destinationIPAddress === LIMITED_BROADCAST_IP_ADDRESS) {
|
||||||
const broadcastInterfaces = boundInterface === undefined ? interfaces : [boundInterface];
|
const broadcastInterfaces = boundInterface === undefined ? interfaces : [boundInterface];
|
||||||
for (const networkInterface of broadcastInterfaces) traceSent(networkInterface, packetIdentity);
|
for (const networkInterface of broadcastInterfaces) traceSent(networkInterface, packetIdentity);
|
||||||
return this.handToTransportSender(socket.nodeId, 'udp', handedOn, (finished) => {
|
return this.handToProtocolThread(socket.nodeId, 'udp', handedOn, (finished) => {
|
||||||
for (const networkInterface of broadcastInterfaces) {
|
for (const networkInterface of broadcastInterfaces) {
|
||||||
const packet: IPv4Packet = {
|
const packet: IPv4Packet = {
|
||||||
identity: packetIdentity,
|
identity: packetIdentity,
|
||||||
@@ -1127,55 +1133,51 @@ export class NetworkRuntimeEngine {
|
|||||||
};
|
};
|
||||||
traceSent(networkInterface, packet.identity);
|
traceSent(networkInterface, packet.identity);
|
||||||
const portId = networkInterface.portId;
|
const portId = networkInterface.portId;
|
||||||
return this.handToTransportSender(socket.nodeId, 'udp', handedOn, (finished) =>
|
return this.handToProtocolThread(socket.nodeId, 'udp', handedOn, (finished) =>
|
||||||
this.sendIPv4PacketWithOptions(socket.nodeId, packet, portId, true, 'sent', { onFinished: finished }),
|
this.sendIPv4PacketWithOptions(socket.nodeId, packet, portId, true, 'sent', { onFinished: finished }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hands a segment to the node's send thread for the protocol (Java's
|
* Hands a unit to one of the node's threads (ProtocolThreadKind). The
|
||||||
* TransportProtokoll.senden). The thread holds its list while IP waits for
|
* thread holds its list while it waits for ARP. A socket's send then blocks
|
||||||
* ARP: a socket's send then blocks until the segment in hand is out, as the
|
* until the unit in hand is done, as the caller does here
|
||||||
* caller does here; from within an event, the segment queues behind it.
|
* (TransportProtokoll.senden); from within an event, the unit queues
|
||||||
* The senders that wait take their turns in the order they came, before
|
* behind it. Those that wait take their turns in the order they came,
|
||||||
* the thread takes the next — as with a fair lock; Java's is not fair and
|
* before the thread takes the next — as with a fair lock; Java's is not
|
||||||
* leaves the order to chance. The result is the send's own when the thread
|
* fair and leaves the order to chance. The result is the unit's own when
|
||||||
* takes it at once, else that it was handed on.
|
* the thread takes it at once, else `handedOn`.
|
||||||
*/
|
*/
|
||||||
private handToTransportSender(
|
private handToProtocolThread<T>(nodeId: Id, kind: ProtocolThreadKind, handedOn: T, unit: (finished: () => void) => T): T {
|
||||||
nodeId: Id,
|
const key = `${nodeId}/${kind}`;
|
||||||
protocol: 'udp' | 'tcp',
|
const thread = getOrCreate(this.protocolThreads, key, () => ({ busy: false, started: 0, finished: 0, queue: [] }));
|
||||||
handedOn: IPv4DeliveryResult,
|
if (thread.busy && !this.isProcessingScheduledEvents) {
|
||||||
send: (finished: () => void) => IPv4DeliveryResult,
|
// The unit in hand is done once ARP has answered or given up.
|
||||||
): IPv4DeliveryResult {
|
const inHand = thread.started;
|
||||||
const sender = getOrCreate(this.transportSenders, `${nodeId}/${protocol}`, () => ({ busy: false, started: 0, finished: 0, queue: [] }));
|
|
||||||
if (sender.busy && !this.isProcessingScheduledEvents) {
|
|
||||||
// The segment in hand is out once ARP has answered or given up.
|
|
||||||
const inHand = sender.started;
|
|
||||||
const deadline = this.stateValue.currentTimeMilliseconds + (ARP_ATTEMPTS + 1) * this.runtimeSpeed.roundTripTimeMilliseconds;
|
const deadline = this.stateValue.currentTimeMilliseconds + (ARP_ATTEMPTS + 1) * this.runtimeSpeed.roundTripTimeMilliseconds;
|
||||||
this.advanceUntil(() => sender.finished >= inHand, deadline);
|
this.advanceUntil(() => thread.finished >= inHand, deadline);
|
||||||
if (this.stateValue.phase !== 'running' || this.transportSenders.get(`${nodeId}/${protocol}`) !== sender) return handedOn;
|
if (this.stateValue.phase !== 'running' || this.protocolThreads.get(key) !== thread) return handedOn;
|
||||||
}
|
}
|
||||||
let result = handedOn;
|
let result = handedOn;
|
||||||
sender.queue.push((finished) => {
|
thread.queue.push((finished) => {
|
||||||
result = send(finished);
|
result = unit(finished);
|
||||||
});
|
});
|
||||||
if (!sender.busy) this.startNextTransportSend(sender);
|
if (!thread.busy) this.startNextProtocolUnit(thread);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private startNextTransportSend(sender: TransportSender): void {
|
private startNextProtocolUnit(thread: ProtocolThread): void {
|
||||||
const next = sender.queue.shift();
|
const next = thread.queue.shift();
|
||||||
if (next === undefined) return;
|
if (next === undefined) return;
|
||||||
sender.busy = true;
|
thread.busy = true;
|
||||||
sender.started += 1;
|
thread.started += 1;
|
||||||
let out = false;
|
let done = false;
|
||||||
next(() => {
|
next(() => {
|
||||||
if (out) return;
|
if (done) return;
|
||||||
out = true;
|
done = true;
|
||||||
sender.finished += 1;
|
thread.finished += 1;
|
||||||
sender.busy = false;
|
thread.busy = false;
|
||||||
this.startNextTransportSend(sender);
|
this.startNextProtocolUnit(thread);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1881,7 +1883,7 @@ export class NetworkRuntimeEngine {
|
|||||||
});
|
});
|
||||||
const portId = networkInterface.portId;
|
const portId = networkInterface.portId;
|
||||||
const handedOn: IPv4DeliveryResult = { type: 'delivered', packetIdentity: packet.identity, nodeId: socket.nodeId };
|
const handedOn: IPv4DeliveryResult = { type: 'delivered', packetIdentity: packet.identity, nodeId: socket.nodeId };
|
||||||
return this.handToTransportSender(socket.nodeId, 'tcp', handedOn, (finished) => {
|
return this.handToProtocolThread(socket.nodeId, 'tcp', handedOn, (finished) => {
|
||||||
// Java's socket starts waiting when it has handed the segment down,
|
// Java's socket starts waiting when it has handed the segment down,
|
||||||
// before ARP has found the next hop; the timeout counts from here.
|
// before ARP has found the next hop; the timeout counts from here.
|
||||||
const handedDownAt = state.currentTimeMilliseconds;
|
const handedDownAt = state.currentTimeMilliseconds;
|
||||||
@@ -3134,7 +3136,7 @@ export class NetworkRuntimeEngine {
|
|||||||
this.receiveArpPacket(frame.payload.packet, frame.identity, nodeId, networkInterface);
|
this.receiveArpPacket(frame.payload.packet, frame.identity, nodeId, networkInterface);
|
||||||
return;
|
return;
|
||||||
case 'ipv4':
|
case 'ipv4':
|
||||||
this.receiveIPv4Packet(frame.payload.packet, frame.identity, nodeId, networkInterface);
|
this.receiveIPv4Packet(frame.payload.packet, frame.identity, nodeId, networkInterface, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3581,7 +3583,7 @@ export class NetworkRuntimeEngine {
|
|||||||
case 'local': {
|
case 'local': {
|
||||||
const networkInterface = route.interface ?? this.networkInterfaces(fromNodeId)[0];
|
const networkInterface = route.interface ?? this.networkInterfaces(fromNodeId)[0];
|
||||||
if (networkInterface === undefined) return { type: 'dropped', packetIdentity: packet.identity, nodeId: fromNodeId };
|
if (networkInterface === undefined) return { type: 'dropped', packetIdentity: packet.identity, nodeId: fromNodeId };
|
||||||
this.receiveIPv4Packet(packet, undefined, fromNodeId, networkInterface);
|
this.receiveIPv4Packet(packet, undefined, fromNodeId, networkInterface, false);
|
||||||
return { type: 'delivered', packetIdentity: packet.identity, nodeId: fromNodeId };
|
return { type: 'delivered', packetIdentity: packet.identity, nodeId: fromNodeId };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3636,11 +3638,17 @@ export class NetworkRuntimeEngine {
|
|||||||
this.sendEthernetFrame(fromNodeId, networkInterface.portId, frame);
|
this.sendEthernetFrame(fromNodeId, networkInterface.portId, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A packet that came in on a card goes to the node's IP or ICMP thread
|
||||||
|
* (`throughThread`); one the node sent to itself is delivered at once, as
|
||||||
|
* Java's IP.senden does.
|
||||||
|
*/
|
||||||
private receiveIPv4Packet(
|
private receiveIPv4Packet(
|
||||||
packet: IPv4Packet,
|
packet: IPv4Packet,
|
||||||
frameIdentity: number | undefined,
|
frameIdentity: number | undefined,
|
||||||
nodeId: Id,
|
nodeId: Id,
|
||||||
networkInterface: NetworkInterface,
|
networkInterface: NetworkInterface,
|
||||||
|
throughThread: boolean,
|
||||||
): void {
|
): void {
|
||||||
this.recordTrace({
|
this.recordTrace({
|
||||||
frameIdentity,
|
frameIdentity,
|
||||||
@@ -3661,6 +3669,29 @@ export class NetworkRuntimeEngine {
|
|||||||
const decision = this.evaluateFirewall(packet, nodeId, networkInterface.portId, frameIdentity);
|
const decision = this.evaluateFirewall(packet, nodeId, networkInterface.portId, frameIdentity);
|
||||||
if (!decision.accepted) return;
|
if (!decision.accepted) return;
|
||||||
}
|
}
|
||||||
|
if (throughThread) {
|
||||||
|
this.handToProtocolThread(nodeId, packet.protocol === 'icmp' ? 'icmp' : 'ip', undefined, (finished) =>
|
||||||
|
this.processIPv4Packet(packet, frameIdentity, nodeId, networkInterface, finished),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.processIPv4Packet(packet, frameIdentity, nodeId, networkInterface, () => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What Java's IPThread and ICMPThread do with a packet: deliver it, answer
|
||||||
|
* an echo request, or forward it. `finished` when the thread is free again:
|
||||||
|
* at once, or when the answer or the forwarded packet has left (or ARP gave
|
||||||
|
* up on it).
|
||||||
|
*/
|
||||||
|
private processIPv4Packet(
|
||||||
|
packet: IPv4Packet,
|
||||||
|
frameIdentity: number | undefined,
|
||||||
|
nodeId: Id,
|
||||||
|
networkInterface: NetworkInterface,
|
||||||
|
finished: () => void,
|
||||||
|
): void {
|
||||||
|
const node = this.nodeSnapshot(nodeId)!;
|
||||||
const packetForProcessing = packet;
|
const packetForProcessing = packet;
|
||||||
|
|
||||||
const localAddresses = new Set(this.networkInterfaces(nodeId).map((candidate) => candidate.ipAddress));
|
const localAddresses = new Set(this.networkInterfaces(nodeId).map((candidate) => candidate.ipAddress));
|
||||||
@@ -3673,9 +3704,12 @@ export class NetworkRuntimeEngine {
|
|||||||
this.stateValue.topologySnapshot.firewallConfigurationsByNodeId.has(nodeId)
|
this.stateValue.topologySnapshot.firewallConfigurationsByNodeId.has(nodeId)
|
||||||
) {
|
) {
|
||||||
const decision = this.evaluateFirewall(packetForProcessing, nodeId, networkInterface.portId, frameIdentity);
|
const decision = this.evaluateFirewall(packetForProcessing, nodeId, networkInterface.portId, frameIdentity);
|
||||||
if (!decision.accepted) return;
|
if (!decision.accepted) {
|
||||||
|
finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.deliverIPv4Locally(packetForProcessing, nodeId, networkInterface);
|
if (!this.deliverIPv4Locally(packetForProcessing, nodeId, networkInterface, finished)) finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node.kind !== 'router') {
|
if (node.kind !== 'router') {
|
||||||
@@ -3690,10 +3724,12 @@ export class NetworkRuntimeEngine {
|
|||||||
beforeHeaders: ipv4Headers(packetForProcessing),
|
beforeHeaders: ipv4Headers(packetForProcessing),
|
||||||
detail: 'IPv4 forwarding disabled',
|
detail: 'IPv4 forwarding disabled',
|
||||||
});
|
});
|
||||||
|
finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packetForProcessing.timeToLive <= 1) {
|
if (packetForProcessing.timeToLive <= 1) {
|
||||||
this.emitIcmpError(nodeId, networkInterface, 'timeExceeded', packet);
|
this.emitIcmpError(nodeId, networkInterface, 'timeExceeded', packet);
|
||||||
|
finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3717,20 +3753,22 @@ export class NetworkRuntimeEngine {
|
|||||||
onUnresolved: () => {
|
onUnresolved: () => {
|
||||||
if (echoOrNotIcmp) this.emitIcmpError(nodeId, networkInterface, 'destinationHostUnreachable', packet);
|
if (echoOrNotIcmp) this.emitIcmpError(nodeId, networkInterface, 'destinationHostUnreachable', packet);
|
||||||
},
|
},
|
||||||
|
onFinished: finished,
|
||||||
});
|
});
|
||||||
if (result.type === 'dropped' && this.routeForPacket(nodeId, packetForProcessing.receiverIPAddress).type === 'unavailable') {
|
if (result.type === 'dropped' && this.routeForPacket(nodeId, packetForProcessing.receiverIPAddress).type === 'unavailable') {
|
||||||
this.emitIcmpError(nodeId, networkInterface, 'destinationNetworkUnreachable', packet);
|
this.emitIcmpError(nodeId, networkInterface, 'destinationNetworkUnreachable', packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private deliverIPv4Locally(packet: IPv4Packet, nodeId: Id, networkInterface: NetworkInterface): void {
|
/** Delivers the packet; true when an answer goes out, which calls `finished` once it has left. */
|
||||||
|
private deliverIPv4Locally(packet: IPv4Packet, nodeId: Id, networkInterface: NetworkInterface, finished: () => void): boolean {
|
||||||
const payload = packet.payload;
|
const payload = packet.payload;
|
||||||
switch (payload.kind) {
|
switch (payload.kind) {
|
||||||
case 'icmp': {
|
case 'icmp': {
|
||||||
const message = payload.message;
|
const message = payload.message;
|
||||||
switch (message.kind) {
|
switch (message.kind) {
|
||||||
case 'echoRequest': {
|
case 'echoRequest': {
|
||||||
if (packet.receiverIPAddress === LIMITED_BROADCAST_IP_ADDRESS && packet.timeToLive !== 1) return;
|
if (packet.receiverIPAddress === LIMITED_BROADCAST_IP_ADDRESS && packet.timeToLive !== 1) return false;
|
||||||
const reply: IPv4Packet = {
|
const reply: IPv4Packet = {
|
||||||
identity: this.allocatePacketIdentity(),
|
identity: this.allocatePacketIdentity(),
|
||||||
senderIPAddress: networkInterface.ipAddress,
|
senderIPAddress: networkInterface.ipAddress,
|
||||||
@@ -3756,8 +3794,8 @@ export class NetworkRuntimeEngine {
|
|||||||
afterHeaders: ipv4Headers(reply),
|
afterHeaders: ipv4Headers(reply),
|
||||||
detail: 'ICMP echo reply',
|
detail: 'ICMP echo reply',
|
||||||
});
|
});
|
||||||
this.sendIPv4PacketWithOptions(nodeId, reply, undefined, false, 'sent');
|
this.sendIPv4PacketWithOptions(nodeId, reply, undefined, false, 'sent', { onFinished: finished });
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
case 'echoReply':
|
case 'echoReply':
|
||||||
case 'destinationNetworkUnreachable':
|
case 'destinationNetworkUnreachable':
|
||||||
@@ -3779,9 +3817,9 @@ export class NetworkRuntimeEngine {
|
|||||||
beforeHeaders: icmpHeaders(message),
|
beforeHeaders: icmpHeaders(message),
|
||||||
detail: 'ICMP observation queued',
|
detail: 'ICMP observation queued',
|
||||||
});
|
});
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
case 'udp':
|
case 'udp':
|
||||||
getOrCreate(this.stateValue.deliveredIPv4PacketsByNodeId, nodeId, () => []).push({
|
getOrCreate(this.stateValue.deliveredIPv4PacketsByNodeId, nodeId, () => []).push({
|
||||||
@@ -3790,7 +3828,7 @@ export class NetworkRuntimeEngine {
|
|||||||
packet,
|
packet,
|
||||||
});
|
});
|
||||||
this.deliverUdp(packet, payload.datagram, nodeId, networkInterface);
|
this.deliverUdp(packet, payload.datagram, nodeId, networkInterface);
|
||||||
return;
|
return false;
|
||||||
case 'tcp':
|
case 'tcp':
|
||||||
getOrCreate(this.stateValue.deliveredIPv4PacketsByNodeId, nodeId, () => []).push({
|
getOrCreate(this.stateValue.deliveredIPv4PacketsByNodeId, nodeId, () => []).push({
|
||||||
nodeId,
|
nodeId,
|
||||||
@@ -3798,7 +3836,7 @@ export class NetworkRuntimeEngine {
|
|||||||
packet,
|
packet,
|
||||||
});
|
});
|
||||||
this.deliverTcp(packet, payload.segment, nodeId, networkInterface);
|
this.deliverTcp(packet, payload.segment, nodeId, networkInterface);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { readFileSync } from 'node:fs';
|
|||||||
import { dirname, join } from 'node:path';
|
import { dirname, join } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import type { IPv4DeliveryResult, NetworkRuntimeEngine } from '../src/index.js';
|
import { icmpMessage, type NetworkRuntimeEngine } from '../src/index.js';
|
||||||
import { GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame } from './javaRun.js';
|
import { GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame } from './javaRun.js';
|
||||||
import type { Scenario } from './scenario.js';
|
import type { Scenario } from './scenario.js';
|
||||||
|
|
||||||
@@ -21,9 +21,13 @@ interface JavaRun {
|
|||||||
events: [number, string][];
|
events: [number, string][];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A ping's result in Java's words: the ICMP answer's type and code, or null when none came. */
|
/** A ping from HOST in Java's words: the ICMP answer's type and code, or null when none came in time. */
|
||||||
function pingResult(result: IPv4DeliveryResult, answered: boolean): string {
|
function ping(engine: NetworkRuntimeEngine, target: string, sequenceNumber = 1): string {
|
||||||
|
const result = engine.sendIcmpEcho(HOST, target, { sequenceNumber });
|
||||||
if (result.type === 'icmpError' && result.kind === 'destinationHostUnreachable') return 'ICMP 3/1';
|
if (result.type === 'icmpError' && result.kind === 'destinationHostUnreachable') return 'ICMP 3/1';
|
||||||
|
const answered = (engine.state.icmpObservationsByNodeId.get(HOST) ?? []).some(
|
||||||
|
(observation) => observation.message.kind === 'echoReply' && observation.message.sequenceNumber === sequenceNumber,
|
||||||
|
);
|
||||||
return answered ? 'ICMP 0/0' : 'null';
|
return answered ? 'ICMP 0/0' : 'null';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,12 +41,8 @@ function udpSend(engine: NetworkRuntimeEngine, target: string, payload: string):
|
|||||||
}
|
}
|
||||||
|
|
||||||
const STORIES: Record<string, (engine: NetworkRuntimeEngine, call: Call) => void> = {
|
const STORIES: Record<string, (engine: NetworkRuntimeEngine, call: Call) => void> = {
|
||||||
pingLan: (engine, call) => call('sendProbe 10.0.0.9', () => pingResult(engine.sendIcmpEcho(HOST, '10.0.0.9'), false)),
|
pingLan: (engine, call) => call('sendProbe 10.0.0.9', () => ping(engine, '10.0.0.9')),
|
||||||
pingRouted: (engine, call) =>
|
pingRouted: (engine, call) => call('sendProbe 20.0.0.9', () => ping(engine, '20.0.0.9')),
|
||||||
call('sendProbe 20.0.0.9', () => {
|
|
||||||
const result = engine.sendIcmpEcho(HOST, '20.0.0.9');
|
|
||||||
return pingResult(result, result.type === 'delivered' && result.nodeId !== HOST);
|
|
||||||
}),
|
|
||||||
udpTwice: (engine, call) => {
|
udpTwice: (engine, call) => {
|
||||||
call('senden eins', () => udpSend(engine, '10.0.0.9', 'eins'));
|
call('senden eins', () => udpSend(engine, '10.0.0.9', 'eins'));
|
||||||
call('senden zwei', () => udpSend(engine, '10.0.0.9', 'zwei'));
|
call('senden zwei', () => udpSend(engine, '10.0.0.9', 'zwei'));
|
||||||
@@ -51,6 +51,32 @@ const STORIES: Record<string, (engine: NetworkRuntimeEngine, call: Call) => void
|
|||||||
call('senden verloren', () => udpSend(engine, '10.0.0.9', 'verloren'));
|
call('senden verloren', () => udpSend(engine, '10.0.0.9', 'verloren'));
|
||||||
call('senden dahinter', () => udpSend(engine, '20.0.0.2', 'dahinter'));
|
call('senden dahinter', () => udpSend(engine, '20.0.0.2', 'dahinter'));
|
||||||
},
|
},
|
||||||
|
routedUdpBehind: (engine, call) => {
|
||||||
|
call('senden verloren', () => udpSend(engine, '20.0.0.9', 'verloren'));
|
||||||
|
call('senden dahinter', () => udpSend(engine, '20.0.0.2', 'dahinter'));
|
||||||
|
},
|
||||||
|
routedPingBesideUdp: (engine, call) => {
|
||||||
|
call('sendProbe 20.0.0.2', () => ping(engine, '20.0.0.2'));
|
||||||
|
call('senden verloren', () => udpSend(engine, '20.0.0.9', 'verloren'));
|
||||||
|
engine.handle({ type: 'advance', toMilliseconds: engine.state.currentTimeMilliseconds + 100 });
|
||||||
|
call('sendProbe 20.0.0.2', () => ping(engine, '20.0.0.2', 2));
|
||||||
|
},
|
||||||
|
routedPingBehindPing: (engine, call) => {
|
||||||
|
// Java's sendeICMP sends in the caller's thread, which waits for ARP.
|
||||||
|
call('sendeICMP 20.0.0.9', () => {
|
||||||
|
engine.resolveMacAddress(HOST, '10.0.0.1');
|
||||||
|
engine.sendIPv4Packet(HOST, {
|
||||||
|
identity: engine.allocatePacketIdentity(),
|
||||||
|
senderIPAddress: '10.0.0.2',
|
||||||
|
receiverIPAddress: '20.0.0.9',
|
||||||
|
timeToLive: 64,
|
||||||
|
protocol: 'icmp',
|
||||||
|
payload: { kind: 'icmp', message: icmpMessage('echoRequest', { identifier: 2, sequenceNumber: 2, data: new Uint8Array() }) },
|
||||||
|
});
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
call('sendProbe 20.0.0.2', () => ping(engine, '20.0.0.2'));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The client's free port as "client": Java draws it at random, the engine from its seed. */
|
/** The client's free port as "client": Java draws it at random, the engine from its seed. */
|
||||||
|
|||||||
@@ -1783,3 +1783,77 @@ describe('ARP', () => {
|
|||||||
expect(engine.state.packetTraces.filter((trace) => trace.layer === 'network' && trace.operation === 'sent')).toEqual([]);
|
expect(engine.state.packetTraces.filter((trace) => trace.layer === 'network' && trace.operation === 'sent')).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Java's IP and ICMP threads", () => {
|
||||||
|
const twoPcs = (prefix: string, base: number, options: { secondIsGateway?: boolean; firewall?: FirewallConfiguration } = {}) => {
|
||||||
|
const [firstId, firstPortId, secondId, secondPortId] = [base, base + 1, base + 2, base + 3].map(uuid) as [string, string, string, string];
|
||||||
|
const engine = startedEngine({
|
||||||
|
nodes: [pc(firstId, firstPortId), pc(secondId, secondPortId)],
|
||||||
|
links: [link(firstId, firstPortId, secondId, secondPortId)],
|
||||||
|
deviceConfigurations: new Map([
|
||||||
|
[firstId, device(`${prefix}.1`, options.secondIsGateway === true ? { gateway: `${prefix}.2` } : {})],
|
||||||
|
[secondId, device(`${prefix}.2`)],
|
||||||
|
]),
|
||||||
|
...(options.firewall === undefined ? {} : { firewallConfigurationsByNodeId: new Map([[secondId, options.firewall]]) }),
|
||||||
|
});
|
||||||
|
return { engine, firstId, secondId };
|
||||||
|
};
|
||||||
|
const text = (payload: Uint8Array | undefined) => new TextDecoder().decode(payload ?? new Uint8Array());
|
||||||
|
|
||||||
|
it('holds the ICMP thread while an echo reply waits for ARP: the next echo request waits behind it', () => {
|
||||||
|
const { engine, firstId } = twoPcs('10.29.0', 0x1b01);
|
||||||
|
// An echo request from .99, which no card has: the answer waits for ARP.
|
||||||
|
engine.sendIPv4Packet(firstId, {
|
||||||
|
identity: engine.allocatePacketIdentity(),
|
||||||
|
senderIPAddress: '10.29.0.99',
|
||||||
|
receiverIPAddress: '10.29.0.2',
|
||||||
|
timeToLive: 64,
|
||||||
|
protocol: 'icmp',
|
||||||
|
payload: { kind: 'icmp', message: icmpMessage('echoRequest', { identifier: 7, sequenceNumber: 7, data: new Uint8Array() }) },
|
||||||
|
});
|
||||||
|
engine.handle({ type: 'advance', toMilliseconds: 200 });
|
||||||
|
const replies = () => (engine.state.icmpObservationsByNodeId.get(firstId) ?? []).filter((observation) => observation.message.kind === 'echoReply');
|
||||||
|
|
||||||
|
engine.sendIcmpEcho(firstId, '10.29.0.2');
|
||||||
|
expect(replies()).toEqual([]);
|
||||||
|
// The request came at 165 ms after ARP there and back; ARP for .99 gives up two round trips later, then the reply takes a cable.
|
||||||
|
engine.handle({ type: 'advance', toMilliseconds: 165 + 2 * 2_750 + 55 - 1 });
|
||||||
|
expect(replies()).toEqual([]);
|
||||||
|
engine.handle({ type: 'advance', toMilliseconds: 165 + 2 * 2_750 + 55 });
|
||||||
|
expect(replies().map((observation) => observation.nodeId)).toEqual([firstId]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('frees the IP thread of a PC that drops a packet it would have to forward', () => {
|
||||||
|
const { engine, firstId, secondId } = twoPcs('10.30.1', 0x1b11, { secondIsGateway: true });
|
||||||
|
const receiver = engine.bindUdpSocket(secondId, { localPort: 9 })!;
|
||||||
|
engine.sendUdp(engine.bindUdpSocket(firstId, { remoteIPAddress: '20.0.0.5', remotePort: 9 })!, new TextEncoder().encode('elsewhere'));
|
||||||
|
engine.sendUdp(engine.bindUdpSocket(firstId, { remoteIPAddress: '10.30.1.2', remotePort: 9 })!, new TextEncoder().encode('here'));
|
||||||
|
|
||||||
|
expect(text(engine.receiveUdp(receiver, 1_000)?.datagram.payload)).toBe('here');
|
||||||
|
expect(engine.state.packetTraces.some((trace) => trace.nodeId === secondId && trace.detail === 'IPv4 forwarding disabled')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('frees the IP thread of a PC whose firewall drops a packet', () => {
|
||||||
|
const firewall = firewallConfiguration({ isActive: true, defaultPolicy: 'drop', rules: [firewallRule({ port: 9, protocol: 'udp', action: 'accept' })] });
|
||||||
|
const { engine, firstId, secondId } = twoPcs('10.30.2', 0x1b21, { firewall });
|
||||||
|
const receiver = engine.bindUdpSocket(secondId, { localPort: 9 })!;
|
||||||
|
engine.bindUdpSocket(secondId, { localPort: 8 });
|
||||||
|
engine.sendUdp(engine.bindUdpSocket(firstId, { remoteIPAddress: '10.30.2.2', remotePort: 8 })!, new TextEncoder().encode('blocked'));
|
||||||
|
engine.sendUdp(engine.bindUdpSocket(firstId, { remoteIPAddress: '10.30.2.2', remotePort: 9 })!, new TextEncoder().encode('let in'));
|
||||||
|
|
||||||
|
expect(text(engine.receiveUdp(receiver, 1_000)?.datagram.payload)).toBe('let in');
|
||||||
|
expect(engine.state.firewallDecisions.some((decision) => !decision.accepted)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delivers what a router sends to itself at once, while its IP thread waits for ARP', () => {
|
||||||
|
const { engine, sourceNodeId, routerNodeId } = routedTopology();
|
||||||
|
// The router forwards to 20.0.0.9, which no card has: its IP thread waits two round trips.
|
||||||
|
engine.sendUdp(engine.bindUdpSocket(sourceNodeId, { remoteIPAddress: '20.0.0.9', remotePort: 9 })!, new TextEncoder().encode('nobody'));
|
||||||
|
engine.handle({ type: 'advance', toMilliseconds: 500 });
|
||||||
|
const receiver = engine.bindUdpSocket(routerNodeId, { localPort: 9 })!;
|
||||||
|
engine.sendUdp(engine.bindUdpSocket(routerNodeId, { remoteIPAddress: '10.0.0.1', remotePort: 9 })!, new TextEncoder().encode('self'));
|
||||||
|
|
||||||
|
expect(text(engine.receiveUdp(receiver)?.datagram.payload)).toBe('self');
|
||||||
|
expect(engine.state.currentTimeMilliseconds).toBe(500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export interface JavaFrame {
|
|||||||
destinationPort?: number;
|
destinationPort?: number;
|
||||||
payload?: string | null;
|
payload?: string | null;
|
||||||
icmp?: number;
|
icmp?: number;
|
||||||
|
icmpCode?: number;
|
||||||
tcpFlags?: string;
|
tcpFlags?: string;
|
||||||
sequence?: number;
|
sequence?: number;
|
||||||
acknowledgement?: number;
|
acknowledgement?: number;
|
||||||
@@ -54,7 +55,7 @@ export interface SeenFrame {
|
|||||||
function javaText(frame: JavaFrame): string {
|
function javaText(frame: JavaFrame): string {
|
||||||
const macs = `${frame.source} > ${frame.destination}`.toUpperCase();
|
const macs = `${frame.source} > ${frame.destination}`.toUpperCase();
|
||||||
if (frame.arp !== undefined) return `${macs} ARP ${frame.arp === 1 ? 'request' : 'reply'} ${frame.senderIp} > ${frame.targetIp}`;
|
if (frame.arp !== undefined) return `${macs} ARP ${frame.arp === 1 ? 'request' : 'reply'} ${frame.senderIp} > ${frame.targetIp}`;
|
||||||
if (frame.icmp !== undefined) return `${macs} ICMP ${frame.icmp}`;
|
if (frame.icmp !== undefined) return icmpText(macs, frame.icmp, frame.icmpCode!, frame.sourceIp!, frame.destinationIp!, frame.sequence!);
|
||||||
const ends = `${frame.sourceIp}:${frame.sourcePort} > ${frame.destinationIp}:${frame.destinationPort}`;
|
const ends = `${frame.sourceIp}:${frame.sourcePort} > ${frame.destinationIp}:${frame.destinationPort}`;
|
||||||
if (frame.tcpFlags !== undefined) {
|
if (frame.tcpFlags !== undefined) {
|
||||||
return `${macs} TCP ${ends} ${frame.tcpFlags || '-'} seq=${frame.sequence} ack=${frame.acknowledgement} length=${(frame.payload ?? '').length}`;
|
return `${macs} TCP ${ends} ${frame.tcpFlags || '-'} seq=${frame.sequence} ack=${frame.acknowledgement} length=${(frame.payload ?? '').length}`;
|
||||||
@@ -63,14 +64,21 @@ function javaText(frame: JavaFrame): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The ICMP type numbers Java logs. */
|
/** The ICMP type numbers Java logs. */
|
||||||
const ICMP_TYPES: Record<string, number> = {
|
/** Type and code of each ICMP message the engine knows. */
|
||||||
echoReply: 0,
|
const ICMP_TYPES: Record<string, [number, number]> = {
|
||||||
destinationNetworkUnreachable: 3,
|
echoReply: [0, 0],
|
||||||
destinationHostUnreachable: 3,
|
destinationNetworkUnreachable: [3, 0],
|
||||||
echoRequest: 8,
|
destinationHostUnreachable: [3, 1],
|
||||||
timeExceeded: 11,
|
echoRequest: [8, 0],
|
||||||
|
timeExceeded: [11, 0],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** The sequence number only for echo messages: an error carries none of its own. */
|
||||||
|
function icmpText(macs: string, type: number, code: number, source: string, destination: string, sequence: number): string {
|
||||||
|
const echo = type === 0 || type === 8 ? ` seq=${sequence}` : '';
|
||||||
|
return `${macs} ICMP ${type}/${code} ${source} > ${destination}${echo}`;
|
||||||
|
}
|
||||||
|
|
||||||
const FLAG_LETTERS: [number, string][] = [
|
const FLAG_LETTERS: [number, string][] = [
|
||||||
[TCP_FLAG.synchronize, 'S'],
|
[TCP_FLAG.synchronize, 'S'],
|
||||||
[TCP_FLAG.acknowledgement, 'A'],
|
[TCP_FLAG.acknowledgement, 'A'],
|
||||||
@@ -86,7 +94,11 @@ function engineText(frame: EthernetFrame): string {
|
|||||||
return `${macs} ARP ${arp.operation} ${arp.senderIPAddress} > ${arp.targetIPAddress}`;
|
return `${macs} ARP ${arp.operation} ${arp.senderIPAddress} > ${arp.targetIPAddress}`;
|
||||||
}
|
}
|
||||||
const packet = frame.payload.packet;
|
const packet = frame.payload.packet;
|
||||||
if (packet.payload.kind === 'icmp') return `${macs} ICMP ${ICMP_TYPES[packet.payload.message.kind]}`;
|
if (packet.payload.kind === 'icmp') {
|
||||||
|
const message = packet.payload.message;
|
||||||
|
const [type, code] = ICMP_TYPES[message.kind]!;
|
||||||
|
return icmpText(macs, type, code, packet.senderIPAddress, packet.receiverIPAddress, message.sequenceNumber);
|
||||||
|
}
|
||||||
if (packet.payload.kind === 'tcp') {
|
if (packet.payload.kind === 'tcp') {
|
||||||
const segment = packet.payload.segment;
|
const segment = packet.payload.segment;
|
||||||
const flags = FLAG_LETTERS.filter(([bit]) => (segment.flags & bit) !== 0).map(([, letter]) => letter).join('');
|
const flags = FLAG_LETTERS.filter(([bit]) => (segment.flags & bit) !== 0).map(([, letter]) => letter).join('');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"frames": {
|
"frames": {
|
||||||
"00000000-0000-0000-0000-000000000301": [
|
"00000000-0000-0000-0000-000000000301": [
|
||||||
{
|
{
|
||||||
"time": 720,
|
"time": 718,
|
||||||
"source": "02:00:00:00:03:11",
|
"source": "02:00:00:00:03:11",
|
||||||
"destination": "FF:FF:FF:FF:FF:FF",
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
"arp": 1,
|
"arp": 1,
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"targetIp": "10.0.0.1"
|
"targetIp": "10.0.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 955,
|
"time": 951,
|
||||||
"source": "02:00:00:00:03:21",
|
"source": "02:00:00:00:03:21",
|
||||||
"destination": "02:00:00:00:03:11",
|
"destination": "02:00:00:00:03:11",
|
||||||
"arp": 2,
|
"arp": 2,
|
||||||
@@ -18,21 +18,29 @@
|
|||||||
"targetIp": "10.0.0.2"
|
"targetIp": "10.0.0.2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 959,
|
"time": 955,
|
||||||
"source": "02:00:00:00:03:11",
|
"source": "02:00:00:00:03:11",
|
||||||
"destination": "02:00:00:00:03:21",
|
"destination": "02:00:00:00:03:21",
|
||||||
"icmp": 8
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"sequence": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 6685,
|
"time": 6681,
|
||||||
"source": "02:00:00:00:03:21",
|
"source": "02:00:00:00:03:21",
|
||||||
"destination": "02:00:00:00:03:11",
|
"destination": "02:00:00:00:03:11",
|
||||||
"icmp": 3
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"00000000-0000-0000-0000-000000000302": [
|
"00000000-0000-0000-0000-000000000302": [
|
||||||
{
|
{
|
||||||
"time": 841,
|
"time": 838,
|
||||||
"source": "02:00:00:00:03:11",
|
"source": "02:00:00:00:03:11",
|
||||||
"destination": "FF:FF:FF:FF:FF:FF",
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
"arp": 1,
|
"arp": 1,
|
||||||
@@ -40,7 +48,7 @@
|
|||||||
"targetIp": "10.0.0.1"
|
"targetIp": "10.0.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 843,
|
"time": 840,
|
||||||
"source": "02:00:00:00:03:21",
|
"source": "02:00:00:00:03:21",
|
||||||
"destination": "02:00:00:00:03:11",
|
"destination": "02:00:00:00:03:11",
|
||||||
"arp": 2,
|
"arp": 2,
|
||||||
@@ -48,21 +56,29 @@
|
|||||||
"targetIp": "10.0.0.2"
|
"targetIp": "10.0.0.2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 1072,
|
"time": 1068,
|
||||||
"source": "02:00:00:00:03:11",
|
"source": "02:00:00:00:03:11",
|
||||||
"destination": "02:00:00:00:03:21",
|
"destination": "02:00:00:00:03:21",
|
||||||
"icmp": 8
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"sequence": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 6574,
|
"time": 6569,
|
||||||
"source": "02:00:00:00:03:21",
|
"source": "02:00:00:00:03:21",
|
||||||
"destination": "02:00:00:00:03:11",
|
"destination": "02:00:00:00:03:11",
|
||||||
"icmp": 3
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"00000000-0000-0000-0000-000000000303": [
|
"00000000-0000-0000-0000-000000000303": [
|
||||||
{
|
{
|
||||||
"time": 1128,
|
"time": 1124,
|
||||||
"source": "02:00:00:00:03:22",
|
"source": "02:00:00:00:03:22",
|
||||||
"destination": "FF:FF:FF:FF:FF:FF",
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
"arp": 1,
|
"arp": 1,
|
||||||
@@ -70,7 +86,7 @@
|
|||||||
"targetIp": "20.0.0.9"
|
"targetIp": "20.0.0.9"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 3878,
|
"time": 3874,
|
||||||
"source": "02:00:00:00:03:22",
|
"source": "02:00:00:00:03:22",
|
||||||
"destination": "FF:FF:FF:FF:FF:FF",
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
"arp": 1,
|
"arp": 1,
|
||||||
@@ -81,11 +97,11 @@
|
|||||||
},
|
},
|
||||||
"events": [
|
"events": [
|
||||||
[
|
[
|
||||||
716,
|
713,
|
||||||
"sendProbe 20.0.0.9 called"
|
"sendProbe 20.0.0.9 called"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
3709,
|
3705,
|
||||||
"sendProbe 20.0.0.9 returned null"
|
"sendProbe 20.0.0.9 returned null"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
{
|
||||||
|
"frames": {
|
||||||
|
"00000000-0000-0000-0000-000000000301": [
|
||||||
|
{
|
||||||
|
"time": 718,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "10.0.0.2",
|
||||||
|
"targetIp": "10.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 952,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "10.0.0.1",
|
||||||
|
"targetIp": "10.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 955,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1012,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6681,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6904,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"00000000-0000-0000-0000-000000000302": [
|
||||||
|
{
|
||||||
|
"time": 838,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "10.0.0.2",
|
||||||
|
"targetIp": "10.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 840,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "10.0.0.1",
|
||||||
|
"targetIp": "10.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1068,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1124,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6570,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6793,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"00000000-0000-0000-0000-000000000303": [
|
||||||
|
{
|
||||||
|
"time": 1124,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 3874,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6625,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6625,
|
||||||
|
"source": "02:00:00:00:03:13",
|
||||||
|
"destination": "02:00:00:00:03:22",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "20.0.0.2",
|
||||||
|
"targetIp": "20.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6737,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "02:00:00:00:03:13",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6737,
|
||||||
|
"source": "02:00:00:00:03:13",
|
||||||
|
"destination": "02:00:00:00:03:22",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
[
|
||||||
|
713,
|
||||||
|
"sendeICMP 20.0.0.9 called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
955,
|
||||||
|
"sendeICMP 20.0.0.9 returned "
|
||||||
|
],
|
||||||
|
[
|
||||||
|
955,
|
||||||
|
"sendProbe 20.0.0.2 called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3763,
|
||||||
|
"sendProbe 20.0.0.2 returned null"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
{
|
||||||
|
"frames": {
|
||||||
|
"00000000-0000-0000-0000-000000000301": [
|
||||||
|
{
|
||||||
|
"time": 717,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "10.0.0.2",
|
||||||
|
"targetIp": "10.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 951,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "10.0.0.1",
|
||||||
|
"targetIp": "10.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 955,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1402,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1408,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 59998,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "verloren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1507,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1842,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 7133,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"00000000-0000-0000-0000-000000000302": [
|
||||||
|
{
|
||||||
|
"time": 837,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "10.0.0.2",
|
||||||
|
"targetIp": "10.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 839,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "10.0.0.1",
|
||||||
|
"targetIp": "10.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1068,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1291,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1521,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 59998,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "verloren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1618,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1731,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 7022,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"00000000-0000-0000-0000-000000000303": [
|
||||||
|
{
|
||||||
|
"time": 1123,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1124,
|
||||||
|
"source": "02:00:00:00:03:13",
|
||||||
|
"destination": "02:00:00:00:03:22",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "20.0.0.2",
|
||||||
|
"targetIp": "20.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1235,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "02:00:00:00:03:13",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1235,
|
||||||
|
"source": "02:00:00:00:03:13",
|
||||||
|
"destination": "02:00:00:00:03:22",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1576,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1675,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "02:00:00:00:03:13",
|
||||||
|
"icmp": 8,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1675,
|
||||||
|
"source": "02:00:00:00:03:13",
|
||||||
|
"destination": "02:00:00:00:03:22",
|
||||||
|
"icmp": 0,
|
||||||
|
"icmpCode": 0,
|
||||||
|
"sourceIp": "20.0.0.2",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 4327,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
[
|
||||||
|
712,
|
||||||
|
"sendProbe 20.0.0.2 called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1404,
|
||||||
|
"sendProbe 20.0.0.2 returned ICMP 0/0 from 20.0.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1404,
|
||||||
|
"senden verloren called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1407,
|
||||||
|
"senden verloren returned "
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1507,
|
||||||
|
"sendProbe 20.0.0.2 called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1842,
|
||||||
|
"sendProbe 20.0.0.2 returned ICMP 0/0 from 20.0.0.2"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
{
|
||||||
|
"frames": {
|
||||||
|
"00000000-0000-0000-0000-000000000301": [
|
||||||
|
{
|
||||||
|
"time": 721,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "10.0.0.2",
|
||||||
|
"targetIp": "10.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 957,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "10.0.0.1",
|
||||||
|
"targetIp": "10.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 959,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 49345,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "verloren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1017,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 58689,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "dahinter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6689,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"00000000-0000-0000-0000-000000000302": [
|
||||||
|
{
|
||||||
|
"time": 844,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "10.0.0.2",
|
||||||
|
"targetIp": "10.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 846,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "10.0.0.1",
|
||||||
|
"targetIp": "10.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1073,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.9",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 49345,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "verloren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1129,
|
||||||
|
"source": "02:00:00:00:03:11",
|
||||||
|
"destination": "02:00:00:00:03:21",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 58689,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "dahinter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6577,
|
||||||
|
"source": "02:00:00:00:03:21",
|
||||||
|
"destination": "02:00:00:00:03:11",
|
||||||
|
"icmp": 3,
|
||||||
|
"icmpCode": 1,
|
||||||
|
"sourceIp": "10.0.0.1",
|
||||||
|
"destinationIp": "10.0.0.2",
|
||||||
|
"sequence": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"00000000-0000-0000-0000-000000000303": [
|
||||||
|
{
|
||||||
|
"time": 1129,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 3879,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6632,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "FF:FF:FF:FF:FF:FF",
|
||||||
|
"arp": 1,
|
||||||
|
"senderIp": "20.0.0.1",
|
||||||
|
"targetIp": "20.0.0.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6633,
|
||||||
|
"source": "02:00:00:00:03:13",
|
||||||
|
"destination": "02:00:00:00:03:22",
|
||||||
|
"arp": 2,
|
||||||
|
"senderIp": "20.0.0.2",
|
||||||
|
"targetIp": "20.0.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 6744,
|
||||||
|
"source": "02:00:00:00:03:22",
|
||||||
|
"destination": "02:00:00:00:03:13",
|
||||||
|
"sourceIp": "10.0.0.2",
|
||||||
|
"destinationIp": "20.0.0.2",
|
||||||
|
"protocol": 17,
|
||||||
|
"sourcePort": 58689,
|
||||||
|
"destinationPort": 9,
|
||||||
|
"payload": "dahinter"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
[
|
||||||
|
712,
|
||||||
|
"senden verloren called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
717,
|
||||||
|
"senden verloren returned "
|
||||||
|
],
|
||||||
|
[
|
||||||
|
717,
|
||||||
|
"senden dahinter called"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
959,
|
||||||
|
"senden dahinter returned "
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -276,7 +276,12 @@ Adressen, die keine Karte hat, und schreibt nach `arp-java-<ablauf>.java.json`
|
|||||||
die Frames und die Aufrufe. Abläufe: `pingLan` (Ping an 10.0.0.9,
|
die Frames und die Aufrufe. Abläufe: `pingLan` (Ping an 10.0.0.9,
|
||||||
`ICMP.sendProbe`), `pingRouted` (Ping an 20.0.0.9), `udpTwice` (zwei
|
`ICMP.sendProbe`), `pingRouted` (Ping an 20.0.0.9), `udpTwice` (zwei
|
||||||
Datagramme an 10.0.0.9), `udpBehind` (eines an 10.0.0.9, dann eines an
|
Datagramme an 10.0.0.9), `udpBehind` (eines an 10.0.0.9, dann eines an
|
||||||
20.0.0.2).
|
20.0.0.2), `routedUdpBehind` (eines an 20.0.0.9, dann eines an 20.0.0.2:
|
||||||
|
der Router leitet beide weiter), `routedPingBesideUdp` (Ping an 20.0.0.2,
|
||||||
|
damit der Router es kennt, ein Datagramm an 20.0.0.9, 100 ms später wieder
|
||||||
|
ein Ping an 20.0.0.2) und `routedPingBehindPing` (ein Echo-Request an
|
||||||
|
20.0.0.9 ohne Warten auf Antwort, dann ein Ping an 20.0.0.2). ICMP-Frames
|
||||||
|
schreibt `ScenarioNetwork` mit Typ, Code, Adressen und Sequenznummer.
|
||||||
|
|
||||||
Gemessen (30.09.2026), was der Kern (nach Swift) anders macht:
|
Gemessen (30.09.2026), was der Kern (nach Swift) anders macht:
|
||||||
|
|
||||||
@@ -293,7 +298,28 @@ Gemessen (30.09.2026), was der Kern (nach Swift) anders macht:
|
|||||||
* Ein Ping wartet eine RTT auf die Antwort; das „Host unreachable" des
|
* Ein Ping wartet eine RTT auf die Antwort; das „Host unreachable" des
|
||||||
Routers (nach dessen 5,5 s) kommt zu spät.
|
Routers (nach dessen 5,5 s) kommt zu spät.
|
||||||
|
|
||||||
Der Kern spielt alle vier Abläufe wie Java (`packages/core/test/arpJava.test.ts`:
|
Gemessen (30.09.2026) an den Router-Abläufen:
|
||||||
|
|
||||||
|
* Der Router leitet UDP und TCP in seinem IP-Thread weiter, ICMP in seinem
|
||||||
|
ICMP-Thread (`EthernetThread` sortiert nach Protokoll in zwei Puffer).
|
||||||
|
Beide halten ihren Puffer, solange ARP wartet: Ein Datagramm an 20.0.0.2
|
||||||
|
wartet 5,5 s hinter einem an 20.0.0.9, ein Echo-Request ebenso hinter
|
||||||
|
einem Echo-Request (der Ping kehrt nach einer RTT ohne Antwort zurück, die
|
||||||
|
Antwort kommt später). Ein Ping neben einem hängenden Datagramm geht sofort
|
||||||
|
durch. Auch das Echo-Reply eines Rechners schickt sein ICMP-Thread.
|
||||||
|
* Javas ARP weckt mit `arpTabelle.notify()` nur **einen** Wartenden, nicht
|
||||||
|
den, dessen Antwort kam. Warten zwei Threads (im Rechner der UDP-Thread und
|
||||||
|
der Ping-Aufrufer, im Router IP- und ICMP-Thread), wartet der andere bis
|
||||||
|
zum Ende seiner RTT; einer, der fälschlich geweckt wurde, verbraucht einen
|
||||||
|
Versuch und fragt sofort wieder (gemessen: das zweite ARP nach 371 ms
|
||||||
|
statt 2750, der ICMP-Thread schickte den Echo-Request eine RTT zu spät).
|
||||||
|
Ein Java-Fehler, den der Kern nicht übernimmt: Er weckt alle, die auf die
|
||||||
|
Adresse warten. Die Abläufe meiden ihn (nur ein Wartender zur Zeit).
|
||||||
|
* Ein Datagramm, das `senden` dem UDP-Thread übergibt, geht oft erst nach
|
||||||
|
einem Frame hinaus, den der Aufrufer gleich danach selbst schickt (der
|
||||||
|
Thread wacht später auf). `routedPingBesideUdp` wartet deshalb 100 ms.
|
||||||
|
|
||||||
|
Der Kern spielt alle sieben Abläufe wie Java (`packages/core/test/arpJava.test.ts`:
|
||||||
Frames, Abstände und die Zeiten, zu denen die Aufrufe zurückkehren). Die UDP-Abläufe
|
Frames, Abstände und die Zeiten, zu denen die Aufrufe zurückkehren). Die UDP-Abläufe
|
||||||
seit F2.4b-2: ein Senden kehrt sofort zurück wie in Java (Swifts Senden wartete, bis
|
seit F2.4b-2: ein Senden kehrt sofort zurück wie in Java (Swifts Senden wartete, bis
|
||||||
alle Frames angekommen waren), und je Knoten und Protokoll sendet ein Transport-Thread
|
alle Frames angekommen waren), und je Knoten und Protokoll sendet ein Transport-Thread
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ done
|
|||||||
|
|
||||||
# ARP as Java FILIUS runs it: sends to addresses no card has on arp-routed's
|
# ARP as Java FILIUS runs it: sends to addresses no card has on arp-routed's
|
||||||
# network (ArpRun); frames and the calls, for the core's ARP comparison.
|
# network (ArpRun); frames and the calls, for the core's ARP comparison.
|
||||||
for story in pingLan pingRouted udpTwice udpBehind; do
|
for story in pingLan pingRouted udpTwice udpBehind routedUdpBehind routedPingBesideUdp routedPingBehindPing; do
|
||||||
output="$root/packages/core/test/scenarios/arp-java-$story.java.json"
|
output="$root/packages/core/test/scenarios/arp-java-$story.java.json"
|
||||||
rm -f "$output"
|
rm -f "$output"
|
||||||
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" ArpRun \
|
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" ArpRun \
|
||||||
|
|||||||
@@ -10,8 +10,19 @@
|
|||||||
// pingRouted 10.0.0.2 pings 20.0.0.9: the router's ARP fails
|
// pingRouted 10.0.0.2 pings 20.0.0.9: the router's ARP fails
|
||||||
// udpTwice 10.0.0.2 sends two UDP datagrams to 10.0.0.9 at once
|
// udpTwice 10.0.0.2 sends two UDP datagrams to 10.0.0.9 at once
|
||||||
// udpBehind 10.0.0.2 sends a UDP datagram to 10.0.0.9, then one to 20.0.0.2
|
// udpBehind 10.0.0.2 sends a UDP datagram to 10.0.0.9, then one to 20.0.0.2
|
||||||
|
// routedUdpBehind 10.0.0.2 sends a UDP datagram to 20.0.0.9, then one
|
||||||
|
// to 20.0.0.2: the router forwards both
|
||||||
|
// routedPingBesideUdp a ping to 20.0.0.2 (the router learns it), a UDP
|
||||||
|
// datagram to 20.0.0.9, 100 ms later (the UDP thread
|
||||||
|
// sends first) a ping to 20.0.0.2 again: only
|
||||||
|
// one thread waits for ARP at a time. Two would meet
|
||||||
|
// ARP's notify, which wakes one waiter, not the one
|
||||||
|
// whose answer came (a Java bug the core does not take)
|
||||||
|
// routedPingBehindPing an echo request to 20.0.0.9 (sent, not waited for),
|
||||||
|
// then a ping to 20.0.0.2
|
||||||
|
|
||||||
import filius.software.transportschicht.UDPSocket;
|
import filius.software.transportschicht.UDPSocket;
|
||||||
|
import filius.software.vermittlungsschicht.ICMP;
|
||||||
import filius.software.vermittlungsschicht.IcmpPaket;
|
import filius.software.vermittlungsschicht.IcmpPaket;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -83,6 +94,32 @@ public class ArpRun {
|
|||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "routedUdpBehind":
|
||||||
|
call("senden verloren", () -> {
|
||||||
|
send("20.0.0.9", "verloren");
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
call("senden dahinter", () -> {
|
||||||
|
send("20.0.0.2", "dahinter");
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "routedPingBesideUdp":
|
||||||
|
call("sendProbe 20.0.0.2", () -> icmp(network.system(HOST).holeICMP().sendProbe("20.0.0.2", 64, 1, 1)));
|
||||||
|
call("senden verloren", () -> {
|
||||||
|
send("20.0.0.9", "verloren");
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
Thread.sleep(100);
|
||||||
|
call("sendProbe 20.0.0.2", () -> icmp(network.system(HOST).holeICMP().sendProbe("20.0.0.2", 64, 1, 2)));
|
||||||
|
break;
|
||||||
|
case "routedPingBehindPing":
|
||||||
|
call("sendeICMP 20.0.0.9", () -> {
|
||||||
|
network.system(HOST).holeICMP().sendeICMP(ICMP.TYPE_ECHO_REQUEST, ICMP.CODE_ECHO, 2, 2, null, "20.0.0.9");
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
call("sendProbe 20.0.0.2", () -> icmp(network.system(HOST).holeICMP().sendProbe("20.0.0.2", 64, 1, 1)));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("unknown story " + story);
|
throw new IllegalArgumentException("unknown story " + story);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,10 @@ public class ScenarioNetwork {
|
|||||||
out.key("arp").number(arp.getOperation()).key("senderIp").string(arp.getSenderIP()).key("targetIp")
|
out.key("arp").number(arp.getOperation()).key("senderIp").string(arp.getSenderIP()).key("targetIp")
|
||||||
.string(arp.getTargetIP());
|
.string(arp.getTargetIP());
|
||||||
} else if (data instanceof IcmpPaket) {
|
} else if (data instanceof IcmpPaket) {
|
||||||
out.key("icmp").number(((IcmpPaket) data).getIcmpType());
|
IcmpPaket icmp = (IcmpPaket) data;
|
||||||
|
out.key("icmp").number(icmp.getIcmpType()).key("icmpCode").number(icmp.getIcmpCode()).key("sourceIp")
|
||||||
|
.string(icmp.getSender()).key("destinationIp").string(icmp.getEmpfaenger()).key("sequence")
|
||||||
|
.number(icmp.getSeqNr());
|
||||||
} else if (data instanceof IpPaket) {
|
} else if (data instanceof IpPaket) {
|
||||||
IpPaket ip = (IpPaket) data;
|
IpPaket ip = (IpPaket) data;
|
||||||
Segment segment = ip.getSegment();
|
Segment segment = ip.getSegment();
|
||||||
|
|||||||
Reference in New Issue
Block a user