diff --git a/CHANGES.md b/CHANGES.md index cb310d5..42c0f22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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/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/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; seit F2.4c auf den Ereignissen statt blockierend und mit rekursivem Server nach `DNSServer.answerWithRemoteData` (Nameserver der Zone, dann der eigene DNS-Server, sonst Verweis), abweichend von Java fragt er sich nie selbst (Java endlos). Gateway-NAT seit F2.6 (Z. ~3585–3900) nach Javas `filius.software.nat`, an Java-Filius gemessen: die Übersetzung vor dem IP-Thread (wie Javas NAT-Threads zwischen Karte und Ethernet), ausgehend WAN-Adresse und der Port des Eintrags (ICMP behält die Kennung), eingehend nach Gegenstelle, Port und Protokoll, sonst nach der Portweiterleitung (legt einen dynamischen Eintrag an), ein Ping an die WAN-Adresse beantwortet der Heimrouter, Pakete von außen an das LAN verschwinden, beide Seiten durch die Firewall des Heimrouters, ausgehend TTL 1 bekommt „Zeit überschritten“; dynamische Einträge verfallen nach 300 s mal Javas Verzögerungsfaktor (Swift fest 300 s) bei Javas sekündlicher Prüfung, aufgefrischt nur eingehend; der Heimrouter leitet weiter wie ein Router. Abweichend von Java: ein LAN-Endpunkt bekommt für jede neue Gegenstelle einen Eintrag auf demselben WAN-Port (Java behält nur den für die erste, die Antworten weiterer bleiben am Heimrouter hängen). RIP seit F2.5a (Z. ~4700–5090) nach Javas `filius.software.rip`, seit F2.5b an Java-Filius gemessen (die Pause in Javas `float`-Rechnung): eine Route mit 0 Hops je eigenem Netz, die nie verfällt; der erste Beacon nach einer Sekunde, dann alle 25,2–35,2 s aus einem Zufall je Router (Java: ein ungesetzter `Random` je Router; der Kern setzt ihn aus Szenario und Router-ID, `ripSeed`; Swift: ein Zufall für alle); eine Nachricht je Schnittstelle ohne die Routen über sie (Split Horizon); eine neue Route oder mehr Hops vom selben Router senden sofort; eine gelernte Route wird erst beim nächsten eigenen Beacon auf 16 gesetzt, wenn sie verfallen ist (Swift: bei jedem Ereignis); Nachrichten von jedem Quellport (Swift: nur 521); ein RIP-Router leitet nur nach seiner Tabelle weiter, wenige Hops vor langer Maske. `receiveUdp` kehrt wie Javas `empfangen` zurück, sobald ein Datagramm da ist. WLAN seit F2.7 an Java-Filius gemessen: zur Laufzeit ist ein Funkkabel ein Kabel wie jedes andere, und eine SSID verbindet nichts (nach ihr legt nur Javas Editor das Funkkabel, `JHostKonfiguration.updateWifiConnection`); Swifts Verbindung über die SSID zur Laufzeit, auch ohne Kabel (`wirelessAssociations`), nicht übernommen. Klasse statt Werttyp. Noch nicht übertragen (wirft `NotPortedError`): Remote Link | +| `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; seit F2.4c auf den Ereignissen statt blockierend und mit rekursivem Server nach `DNSServer.answerWithRemoteData` (Nameserver der Zone, dann der eigene DNS-Server, sonst Verweis), abweichend von Java fragt er sich nie selbst (Java endlos). Gateway-NAT seit F2.6 (Z. ~3585–3900) nach Javas `filius.software.nat`, an Java-Filius gemessen: die Übersetzung vor dem IP-Thread (wie Javas NAT-Threads zwischen Karte und Ethernet), ausgehend WAN-Adresse und der Port des Eintrags (ICMP behält die Kennung), eingehend nach Gegenstelle, Port und Protokoll, sonst nach der Portweiterleitung (legt einen dynamischen Eintrag an), ein Ping an die WAN-Adresse beantwortet der Heimrouter, Pakete von außen an das LAN verschwinden, beide Seiten durch die Firewall des Heimrouters, ausgehend TTL 1 bekommt „Zeit überschritten“; dynamische Einträge verfallen nach 300 s mal Javas Verzögerungsfaktor (Swift fest 300 s) bei Javas sekündlicher Prüfung, aufgefrischt nur eingehend; der Heimrouter leitet weiter wie ein Router. Abweichend von Java: ein LAN-Endpunkt bekommt für jede neue Gegenstelle einen Eintrag auf demselben WAN-Port (Java behält nur den für die erste, die Antworten weiterer bleiben am Heimrouter hängen). RIP seit F2.5a (Z. ~4700–5090) nach Javas `filius.software.rip`, seit F2.5b an Java-Filius gemessen (die Pause in Javas `float`-Rechnung): eine Route mit 0 Hops je eigenem Netz, die nie verfällt; der erste Beacon nach einer Sekunde, dann alle 25,2–35,2 s aus einem Zufall je Router (Java: ein ungesetzter `Random` je Router; der Kern setzt ihn aus Szenario und Router-ID, `ripSeed`; Swift: ein Zufall für alle); eine Nachricht je Schnittstelle ohne die Routen über sie (Split Horizon); eine neue Route oder mehr Hops vom selben Router senden sofort; eine gelernte Route wird erst beim nächsten eigenen Beacon auf 16 gesetzt, wenn sie verfallen ist (Swift: bei jedem Ereignis); Nachrichten von jedem Quellport (Swift: nur 521); ein RIP-Router leitet nur nach seiner Tabelle weiter, wenige Hops vor langer Maske. `receiveUdp` kehrt wie Javas `empfangen` zurück, sobald ein Datagramm da ist. Den Mitschnitt je Karte (`capture.ts`) schreibt sie seit F2.8a dort, wo Java ihn schreibt: beim Abgang auf das Kabel und bei der Ankunft an der Karte. WLAN seit F2.7 an Java-Filius gemessen: zur Laufzeit ist ein Funkkabel ein Kabel wie jedes andere, und eine SSID verbindet nichts (nach ihr legt nur Javas Editor das Funkkabel, `JHostKonfiguration.updateWifiConnection`); Swifts Verbindung über die SSID zur Laufzeit, auch ohne Kabel (`wirelessAssociations`), nicht übernommen. Klasse statt Werttyp. Noch nicht übertragen (wirft `NotPortedError`): Remote Link | | `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/packets.ts` | Engine, Paketmodelle | übertragen; `Data` → `Uint8Array` | @@ -22,16 +22,18 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand | `src/runtime/snapshot.ts` | Engine, `TopologyNetworkRuntimeTopologySnapshot` | übertragen, bisher mit den Feldern der übertragenen Schichten, seit F2.3 mit den DHCP-Einstellungen von Clients und Servern, seit F2.4a mit den eingeschalteten DNS-Servern; ohne Swifts WLAN-Einstellungen der Rechner (`hostWirelessConfigurationsByNodeID`), die zur Laufzeit nichts bewirken (F2.7) | | `src/runtime/speed.ts` | Engine, `TopologyNetworkRuntimeSpeed` | übertragen; dazu Javas RTT (`Verbindung.holeRTT`: 50 Kabel), die Swift nicht kennt | | `src/runtime/trace.ts` | Engine, Paketspur-Modell | übertragen | +| `src/runtime/capture.ts` | Java `filius.rahmenprogramm.nachrichten.Lauscher` | neu nach Java (Swift hat stattdessen die Paketspur je Schritt): der Mitschnitt je Netzwerkkarte, jeder gesendete und empfangene Frame einmal mit Zeit und Nummer, wie Java auch Frames an andere Karten und solche, die die Firewall verwirft; Switches ohne Liste; verlorene Frames getrennt; bleibt über Stopp und Neustart, geleert nur auf Wunsch. Abweichend von Java: je Port statt je MAC-Adresse (Java legt zwei Karten mit gleicher MAC zusammen) und höchstens 10.000 Frames je Karte, die ältesten 1.000 fallen auf einmal heraus, die Nummern bleiben (Java behält alles) | | `src/util/equal.ts` | — | neu (Ersatz für Swifts `Equatable`) | | `src/util/swiftString.ts` | — | neu: Swift-String-Verhalten, das der übertragene Code stillschweigend voraussetzt (Trimmen, Kleinschreibung je Skalar, kanonische Gleichheit, Sortierung nach Skalarwerten, Base64 wie Foundation); am Swift-Orakel gemessen, Leerraum und Steuerzeichen über ganz Unicode | | `src/model/virtualFileSystem.ts`, `src/model/defaultImages.ts` | `State/TopologyVirtualFileSystem.swift` | übertragen; Schlüssel in NFC statt Swifts kanonischer Wörterbuch-Gleichheit; Steuerzeichen nach Unicode Cc+Cf, ohne Foundations Tabellenfehler in Ebene 14 | | `test/virtualFileSystem.test.ts`, `test/vfs.*.json` | `TopologyProjectPersistenceTests.swift`, `TopologyEditorReducerTests.swift` (drei Dateisystem-Tests, als Fälle) | übertragen und erweitert: 11 Fälle, Ergebnis aus dem Swift-Kern | | `test/strings.test.ts`, `test/strings.*.json` | — | neu: Fälle und ganze Zeichenmengen, Ergebnis aus dem Swift-Kern | -| `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); seit F2.8a sechs Tests für den Mitschnitt (über Neustart, Leeren, Obergrenze, verlorene Frames, Switches) | | `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/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/natJava.test.ts`, `test/scenarios/nat-home.scenario.json`, `test/scenarios/nat-java-*.java.json` | — | neu: sechs NAT-Abläufe gegen Java-Filius (`NatRun`): je Absenderkarte dieselben Frames in derselben Folge zur selben Zeit, dieselben Aufrufe, dieselbe NAT-Tabelle; freie Ports und TCP-Startnummern als Platzhalter; ein benannter Java-Fehler (`JAVA_BUGS`: die Antwort der zweiten Gegenstelle) | +| `test/javaRun.ts` (`expectSameCaptures`) | — | seit F2.8a: der Mitschnitt jeder Karte gegen Javas Liste dieser Karte (dieselben Frames, dieselbe Folge, dieselbe Zeit), in den Vergleichen für ARP, WLAN, DNS, TCP, NAT, RIP und DHCP mit einem Client | | `test/wlanJava.test.ts`, `test/scenarios/wlan-lan.scenario.json`, `test/scenarios/wlan-java-*.java.json` | — | neu: vier Pings gegen Java-Filius (`WlanRun`): über Funkkabel, über ein Kabel und an ein Notebook mit der SSID des Switches, aber ohne Kabel (ARP scheitert in Java wie im Kern); Frames, Abstände, Ergebnis und Dauer | | `test/ripJava.test.ts`, `test/scenarios/rip-chain.scenario.json`, `test/scenarios/rip-java-*.java.json` | — | neu: zwei RIP-Abläufe gegen Java-Filius (`RipRun`, der Zufall jedes Routers auf `ripSeed` gesetzt): je Absenderkarte dieselben Nachrichten zur selben Zeit, jede Tabelle durch dieselben Zustände, derselbe Ping; die ersten 5 s (Javas gestaffelter Start) nur über den erreichten Zustand | | `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; seit F2.4c dazu fünf mit rekursivem Server (`dns-recursive.scenario.json`, `dns-recursive-java-*.java.json`), einer nur bis zur Anfrage des Clients verglichen (`JAVA_UNTIL`: Javas Server fragt sich selbst ohne Ende) | diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4223a6b..cd92e8b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -15,6 +15,7 @@ export * from './runtime/routeTable.js'; export * from './runtime/snapshot.js'; export * from './runtime/speed.js'; export * from './runtime/trace.js'; +export * from './runtime/capture.js'; export * from './util/equal.js'; export * from './util/javaIp.js'; export * from './util/swiftString.js'; diff --git a/packages/core/src/runtime/capture.ts b/packages/core/src/runtime/capture.ts new file mode 100644 index 0000000..7f5c258 --- /dev/null +++ b/packages/core/src/runtime/capture.ts @@ -0,0 +1,120 @@ +// The packet capture as Java FILIUS keeps it (filius.rahmenprogramm.nachrichten +// .Lauscher, the data exchange window): per network card every frame it sent +// or received, once, with the time; and the frames the packet loss dropped. +// +// Java logs a frame on a card when the card hands it to the cable +// (Ethernet.senden, after the cable is free) and when a frame arrives at the +// card (EthernetThread, or before it the firewall or NAT thread of a router +// or home router) — every frame that arrives, also one addressed to another +// card, also one a firewall then drops. Switches have no cards and log +// nothing. A frame shows on a card at most once (EthernetFrame +// .readByLauscherForMac). The log is not cleared when the simulation stops +// or starts again, only for a new or loaded project and by the user. +// +// Departures from Java: a card's list is kept under its port, not its MAC +// address (Java merges two cards with the same MAC into one list), and it +// keeps at most `maximumFrameCount` frames, dropping the oldest +// `evictionBatchSize` at once (Java keeps all); a frame keeps its number. + +import type { Id } from '../ids.js'; +import type { EthernetFrame } from './packets.js'; + +export interface CapturedFrame { + /** Its row number in the card's list, from 1, kept when older frames are dropped (Java: "Nr."). */ + readonly number: number; + readonly timeMilliseconds: number; + readonly frame: EthernetFrame; +} + +/** How many frames a list keeps, and how many it drops at once when full. */ +export interface CaptureRetentionPolicy { + readonly maximumFrameCount: number; + readonly evictionBatchSize: number; +} + +export function captureRetentionPolicy(maximumFrameCount: number, evictionBatchSize: number): CaptureRetentionPolicy { + if (!(maximumFrameCount > 0)) throw new RangeError('Packet capture must keep at least one frame'); + if (!(evictionBatchSize > 0)) throw new RangeError('Packet capture eviction must remove at least one frame'); + if (evictionBatchSize > maximumFrameCount) throw new RangeError('Packet capture eviction cannot exceed the retention limit'); + return { maximumFrameCount, evictionBatchSize }; +} + +export const PRODUCTION_CAPTURE_RETENTION = captureRetentionPolicy(10_000, 1_000); + +class CaptureList { + readonly frames: CapturedFrame[] = []; + private readonly identities = new Set(); + private count = 0; + + /** `once`: a frame shows at most once (a card's list; a switch floods one frame onto several cables, each may drop it). */ + constructor( + private readonly policy: CaptureRetentionPolicy, + private readonly once: boolean, + ) {} + + /** Adds the frame unless it shows once only and the list has it already; false then. */ + add(timeMilliseconds: number, frame: EthernetFrame): boolean { + if (this.once && this.identities.has(frame.identity)) return false; + if (this.frames.length >= this.policy.maximumFrameCount) { + for (const evicted of this.frames.splice(0, this.policy.evictionBatchSize)) this.identities.delete(evicted.frame.identity); + } + this.count += 1; + this.frames.push({ number: this.count, timeMilliseconds, frame }); + this.identities.add(frame.identity); + return true; + } + + /** A new run numbers its frames from 1 again: the identities of the last run no longer tell a frame apart. */ + forgetIdentities(): void { + this.identities.clear(); + } +} + +export class PacketCapture { + private readonly listsByPortId = new Map(); + private dropped: CaptureList; + + constructor(private readonly policy: CaptureRetentionPolicy = PRODUCTION_CAPTURE_RETENTION) { + this.dropped = new CaptureList(policy, false); + } + + /** Logs the frame on the card at `portId`, unless it shows there already. */ + record(portId: Id, timeMilliseconds: number, frame: EthernetFrame): void { + let list = this.listsByPortId.get(portId); + if (list === undefined) { + list = new CaptureList(this.policy, true); + this.listsByPortId.set(portId, list); + } + list.add(timeMilliseconds, frame); + } + + /** Logs a frame the packet loss dropped on its cable (Lauscher.addDroppedDataUnit). */ + recordDropped(timeMilliseconds: number, frame: EthernetFrame): void { + this.dropped.add(timeMilliseconds, frame); + } + + /** The card's frames, oldest first. */ + frames(portId: Id): readonly CapturedFrame[] { + return this.listsByPortId.get(portId)?.frames ?? []; + } + + /** The cards that have frames. */ + portIds(): Id[] { + return [...this.listsByPortId.keys()]; + } + + droppedFrames(): readonly CapturedFrame[] { + return this.dropped.frames; + } + + /** The simulation starts again: the lists stay, frames of the new run are new. */ + beginRun(): void { + for (const list of this.listsByPortId.values()) list.forgetIdentities(); + } + + /** Empties every list (Lauscher.reset). */ + clear(): void { + this.listsByPortId.clear(); + this.dropped = new CaptureList(this.policy, false); + } +} diff --git a/packages/core/src/runtime/engine.ts b/packages/core/src/runtime/engine.ts index 3c28d22..1b006b8 100644 --- a/packages/core/src/runtime/engine.ts +++ b/packages/core/src/runtime/engine.ts @@ -39,6 +39,8 @@ // DNS after Java (filius.software.dns: DNSServer, DNSServerMitarbeiter, // Resolver, DNSQueryAgent), not Swift's typed format; a recursive server // asks on without asking itself (Java does, without end). +// The packet capture after Java's Lauscher (capture.ts): a card logs a frame +// when it leaves on the cable and when it arrives, a switch logs nothing. // Java counterparts: filius.hardware.Verbindung, filius.software.netzzugangsschicht, // filius.software.vermittlungsschicht (ARP, IP, ICMP), system.SwitchFirmware, // filius.software.transportschicht (UDP, TCP), filius.software.dhcp. @@ -103,6 +105,7 @@ import { type DnsMessageRecord, type DnsQuestion, } from './dns.js'; +import { PacketCapture, PRODUCTION_CAPTURE_RETENTION, type CaptureRetentionPolicy } from './capture.js'; import { JavaCompatibleRandom } from './random.js'; import { routeTableRows, type RouteRow } from './routeTable.js'; import { EMPTY_TOPOLOGY_SNAPSHOT, withSnapshot, type NodeSnapshot, type TopologySnapshot } from './snapshot.js'; @@ -845,6 +848,7 @@ export class NetworkRuntimeEngine { private readonly cablesCarryOneFrameAtATime: boolean; private readonly swiftArp: boolean; private readonly swiftSends: boolean; + private readonly packetCapture: PacketCapture; /** * `swiftCables`: a cable carries all frames at once, as in the Swift @@ -855,12 +859,20 @@ export class NetworkRuntimeEngine { * * `swiftArp`, `swiftSends`: Swift's ARP and Swift's send that returns * when the network is quiet, likewise only for the comparison. + * + * `captureRetention`: how many frames each card's packet capture keeps. */ constructor( seed: bigint | number = DEFAULT_SEED, packetTraceRetentionPolicy: TraceRetentionPolicy = PRODUCTION_TRACE_RETENTION, - options: { readonly swiftCables?: boolean; readonly swiftArp?: boolean; readonly swiftSends?: boolean } = {}, + options: { + readonly swiftCables?: boolean; + readonly swiftArp?: boolean; + readonly swiftSends?: boolean; + readonly captureRetention?: CaptureRetentionPolicy; + } = {}, ) { + this.packetCapture = new PacketCapture(options.captureRetention ?? PRODUCTION_CAPTURE_RETENTION); this.cablesCarryOneFrameAtATime = options.swiftCables !== true; this.swiftArp = options.swiftArp === true; this.swiftSends = options.swiftSends === true; @@ -876,6 +888,20 @@ export class NetworkRuntimeEngine { this.stateValue.simulationSpeedPercent = this.runtimeSpeed.percent; } + /** + * The packet capture (Java's Lauscher): per card every frame it sent or + * received. It outlives a stop and a new start, as in Java; clearCapture + * empties it. + */ + get capture(): PacketCapture { + return this.packetCapture; + } + + /** Empties the packet capture (Java: the data exchange window's reset, a new or loaded project). */ + clearCapture(): void { + this.packetCapture.clear(); + } + /** The runtime state. Read it; change it only through the engine's methods. */ get state(): RuntimeState { return this.stateValue; @@ -900,6 +926,7 @@ export class NetworkRuntimeEngine { if (this.stateValue.phase !== 'stopped') return [this.output({ type: 'startIgnoredAlreadyRunning' })]; const seed = toSeed(input.seed); this.stateValue = initialState(seed); + this.packetCapture.beginRun(); this.stateValue.phase = 'running'; this.stateValue.currentTimeMilliseconds = input.initialTimeMilliseconds; this.stateValue.simulationSpeedPercent = this.runtimeSpeed.percent; @@ -1143,6 +1170,7 @@ export class NetworkRuntimeEngine { layer: TraceLayer, ): boolean { if (!this.stateValue.globalPacketLossEnabled) return false; + this.packetCapture.recordDropped(this.stateValue.currentTimeMilliseconds, frame); this.recordTrace({ frameIdentity: frame.identity, packetIdentity: packetIdentityInFrame(frame), @@ -3730,6 +3758,7 @@ export class NetworkRuntimeEngine { /** The frame leaves on the cable (and shows in the packet log); it arrives one cable's delay later. */ private departEthernetFrame(fromNodeId: Id, outgoingPortId: Id, endpoint: { nodeId: Id; portId: Id } | undefined, frame: EthernetFrame): void { + this.captureOnCard(fromNodeId, outgoingPortId, frame); this.recordTrace({ frameIdentity: frame.identity, packetIdentity: packetIdentityInFrame(frame), @@ -3804,7 +3833,14 @@ export class NetworkRuntimeEngine { return this.stateValue.topologySnapshot.nodes.find((node) => node.id === nodeId); } + /** Java logs a frame on a card; a switch has none. */ + private captureOnCard(nodeId: Id, portId: Id, frame: EthernetFrame): void { + if (this.nodeSnapshot(nodeId)?.kind === 'networkSwitch') return; + this.packetCapture.record(portId, this.stateValue.currentTimeMilliseconds, frame); + } + private receiveEthernetFrame(frame: EthernetFrame, nodeId: Id, incomingPortId: Id): void { + this.captureOnCard(nodeId, incomingPortId, frame); this.recordTrace({ frameIdentity: frame.identity, packetIdentity: packetIdentityInFrame(frame), diff --git a/packages/core/test/arpJava.test.ts b/packages/core/test/arpJava.test.ts index ca9f730..519ab10 100644 --- a/packages/core/test/arpJava.test.ts +++ b/packages/core/test/arpJava.test.ts @@ -10,7 +10,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { icmpMessage, type NetworkRuntimeEngine } from '../src/index.js'; -import { GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame } from './javaRun.js'; +import { expectSameCaptures, withClientPortText, GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame } from './javaRun.js'; import type { Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -117,6 +117,7 @@ describe('ARP as Java FILIUS runs it', () => { expect(actual[index]?.text, `frame #${index + 1}`).toBe(expected[index]?.text); expect(Math.abs(actual[index]!.gap - expected[index]!.gap), `gap before frame #${index + 1}`).toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS); } + expectSameCaptures(engine, scenario, java.frames, { text: withClientPortText }); }); } }); diff --git a/packages/core/test/dhcpJava.test.ts b/packages/core/test/dhcpJava.test.ts index 2ea705b..122d89d 100644 --- a/packages/core/test/dhcpJava.test.ts +++ b/packages/core/test/dhcpJava.test.ts @@ -16,7 +16,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { stableMacAddress } from '../src/index.js'; -import { GAP_TOLERANCE_MILLISECONDS, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; +import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; import { type Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -38,7 +38,7 @@ function runEngine(scenario: Scenario, milliseconds: number) { [device.ipAddress, device.subnetMask, device.defaultGateway, device.dnsServer], ]), ); - return { frames: frames(), devices }; + return { engine, frames: frames(), devices }; } /** Consecutive frames without the other clients' own: the text, and the time since the one before. */ @@ -86,6 +86,10 @@ describe('DHCP as Java FILIUS runs it', () => { ); } } + // Each card's capture, with one client: of two that start together, Java's + // log loses the DISCOVER of the one that sends second, and which sends + // first varies (javaRun.ts). + if (clients.length === 1) expectSameCaptures(engine.engine, scenario, java.frames, { text: (text) => text.replace(LINK_LOCAL, '169.254.1.x') }); const linkLocal = (devices: Record) => Object.fromEntries(Object.entries(devices).map(([node, device]) => [node, device.map((value) => value.replace(LINK_LOCAL, '169.254.1.x'))])); expect(linkLocal(engine.devices)).toEqual(linkLocal(java.devices)); diff --git a/packages/core/test/dnsJava.test.ts b/packages/core/test/dnsJava.test.ts index 955003b..efe805b 100644 --- a/packages/core/test/dnsJava.test.ts +++ b/packages/core/test/dnsJava.test.ts @@ -16,7 +16,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import type { DnsAddressResolution, NetworkRuntimeEngine } from '../src/index.js'; -import { GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; +import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; import type { Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -122,6 +122,7 @@ describe('DNS as Java FILIUS runs it', () => { expect(actual[index]?.text, `frame #${index + 1}`).toBe(expected[index]?.text); expect(Math.abs(actual[index]!.gap - expected[index]!.gap), `gap before frame #${index + 1}`).toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS); } + expectSameCaptures(engine, scenario, java.frames, { text: (text) => normalized([{ time: 0, text }])[0]!.text, until }); }); } } diff --git a/packages/core/test/engine.test.ts b/packages/core/test/engine.test.ts index b175c79..538b86c 100644 --- a/packages/core/test/engine.test.ts +++ b/packages/core/test/engine.test.ts @@ -26,6 +26,7 @@ import { JavaCompatibleRandom, ripSeed, NetworkRuntimeEngine, + captureRetentionPolicy, portSnapshot, stableMacAddress, switchConfiguration, @@ -2385,3 +2386,121 @@ describe('Home router NAT', () => { expect(engine.state.natTablesByNodeId.size).toBe(0); }); }); + +describe("packet capture (Java's Lauscher)", () => { + const hostA = uuid(0x2e01); + const portA = uuid(0x2e11); + const hostB = uuid(0x2e02); + const portB = uuid(0x2e12); + const switchId = uuid(0x2e04); + const switchPorts = [1, 2].map((index) => portSnapshot(uuid(0x2e20 + index), `sw${index}`)); + const network = { + nodes: [pc(hostA, portA), pc(hostB, portB), { id: switchId, kind: 'networkSwitch' as const, ports: switchPorts }], + links: [link(hostA, portA, switchId, switchPorts[0]!.id), link(hostB, portB, switchId, switchPorts[1]!.id)], + deviceConfigurations: new Map([ + [hostA, device('10.0.0.1')], + [hostB, device('10.0.0.2')], + ]), + }; + const kinds = (engine: NetworkRuntimeEngine, portId: string) => + engine.capture.frames(portId).map(({ number, frame }) => { + const what = frame.payload.kind === 'arp' ? `arp ${frame.payload.packet.operation}` : frame.payload.packet.payload.kind; + return `${number} ${frame.sourceMacAddress === stableMacAddress(portId) ? 'sent' : 'received'} ${what}`; + }); + const arpRequest = (engine: NetworkRuntimeEngine): EthernetFrame => ({ + identity: engine.allocateFrameIdentity(), + sourceMacAddress: stableMacAddress(portA), + destinationMacAddress: ETHERNET_BROADCAST_MAC_ADDRESS, + payload: { + kind: 'arp', + packet: { operation: 'request', senderMacAddress: stableMacAddress(portA), senderIPAddress: '10.0.0.1', targetMacAddress: UNSPECIFIED_MAC_ADDRESS, targetIPAddress: '10.0.0.9' }, + }, + }); + + it('logs every frame a card sends or receives, numbered, and nothing on a switch', () => { + const engine = startedEngine(network); + engine.sendIcmpEcho(hostA, '10.0.0.2'); + letFramesArrive(engine); + + expect(kinds(engine, portA)).toEqual(['1 sent arp request', '2 received arp reply', '3 sent icmp', '4 received icmp']); + expect(kinds(engine, portB)).toEqual(['1 received arp request', '2 sent arp reply', '3 received icmp', '4 sent icmp']); + expect(switchPorts.map((port) => engine.capture.frames(port.id))).toEqual([[], []]); + // When the card sent it, when the other card received it: two cables later. + const [sent] = engine.capture.frames(portA); + const [received] = engine.capture.frames(portB); + expect(received!.timeMilliseconds - sent!.timeMilliseconds).toBe(2 * engine.simulationSpeed.linkTransmissionDelayMilliseconds); + }); + + it('logs a frame on a card once, also a frame for another card', () => { + const engine = startedEngine(network); + const frame = { ...arpRequest(engine), destinationMacAddress: '02:00:00:00:00:FE' }; + engine.sendEthernetFrame(hostA, portA, frame); + engine.sendEthernetFrame(hostA, portA, frame); + letFramesArrive(engine); + + expect(kinds(engine, portA)).toEqual(['1 sent arp request']); + // The switch floods the unknown destination; B's card logs what is not addressed to it. + expect(kinds(engine, portB)).toEqual(['1 received arp request']); + }); + + it('keeps the frames the packet loss dropped apart; the sender logged them', () => { + const engine = startedEngine(network); + engine.setGlobalPacketLossEnabled(true); + engine.sendEthernetFrame(hostA, portA, arpRequest(engine)); + letFramesArrive(engine); + + expect(kinds(engine, portA)).toEqual(['1 sent arp request']); + expect(engine.capture.frames(portB)).toEqual([]); + expect(engine.capture.droppedFrames().map((each) => each.timeMilliseconds)).toEqual([engine.simulationSpeed.linkTransmissionDelayMilliseconds]); + }); + + it('logs a frame flooded onto two cables once for each cable that drops it', () => { + const hostC = uuid(0x2e03); + const portC = uuid(0x2e13); + const ports = [1, 2, 3].map((index) => portSnapshot(uuid(0x2e30 + index), `sw${index}`)); + const engine = startedEngine({ + nodes: [pc(hostA, portA), pc(hostB, portB), pc(hostC, portC), { id: switchId, kind: 'networkSwitch', ports }], + links: [link(hostA, portA, switchId, ports[0]!.id), link(hostB, portB, switchId, ports[1]!.id), link(hostC, portC, switchId, ports[2]!.id)], + }); + engine.sendEthernetFrame(hostA, portA, arpRequest(engine)); + // The switch has it and floods it; then the loss begins. + engine.handle({ type: 'advance', toMilliseconds: engine.simulationSpeed.linkTransmissionDelayMilliseconds }); + engine.setGlobalPacketLossEnabled(true); + letFramesArrive(engine); + + expect(engine.capture.droppedFrames()).toHaveLength(2); + expect([portB, portC].map((port) => engine.capture.frames(port))).toEqual([[], []]); + }); + + it('outlives a stop and a new start, as in Java, until it is cleared', () => { + const engine = startedEngine(network); + engine.sendEthernetFrame(hostA, portA, arpRequest(engine)); + letFramesArrive(engine); + engine.handle({ type: 'stop' }); + expect(kinds(engine, portA)).toEqual(['1 sent arp request']); + + engine.handle({ type: 'start', snapshot: topologySnapshot(network), seed: 14, initialTimeMilliseconds: 1_000 }); + engine.setGlobalPacketLossEnabled(true); + engine.sendEthernetFrame(hostA, portA, arpRequest(engine)); + letFramesArrive(engine); + expect(engine.capture.frames(portA).map((each) => [each.number, each.timeMilliseconds])).toEqual([ + [1, 0], + [2, 1_000], + ]); + expect(engine.capture.droppedFrames()).toHaveLength(1); + + engine.clearCapture(); + expect(engine.capture.portIds()).toEqual([]); + expect(engine.capture.droppedFrames()).toEqual([]); + }); + + it('keeps at most the retention, dropping the oldest at once; a frame keeps its number', () => { + const engine = new NetworkRuntimeEngine(14, undefined, { captureRetention: captureRetentionPolicy(3, 2) }); + engine.handle({ type: 'start', snapshot: topologySnapshot(network), seed: 14, initialTimeMilliseconds: 0 }); + for (let count = 0; count < 4; count += 1) engine.sendEthernetFrame(hostA, portA, arpRequest(engine)); + letFramesArrive(engine); + + expect(engine.capture.frames(portA).map((each) => each.number)).toEqual([3, 4]); + expect(() => captureRetentionPolicy(2, 3)).toThrow(RangeError); + }); +}); diff --git a/packages/core/test/javaRun.ts b/packages/core/test/javaRun.ts index 1224fec..5fc626c 100644 --- a/packages/core/test/javaRun.ts +++ b/packages/core/test/javaRun.ts @@ -21,6 +21,7 @@ // time (250 ms) may not. A cable carries one frame after the other in both // (engine.test.ts checks that: one cable's delay is below this tolerance). +import { expect } from 'vitest'; import { NetworkRuntimeEngine, TCP_FLAG, type EthernetFrame } from '../src/index.js'; import { snapshotOf, type Scenario } from './scenario.js'; @@ -154,3 +155,62 @@ export function javaFrames(framesByNode: Record): SeenFrame export function gaps(frames: readonly SeenFrame[]) { return frames.map((frame, index) => ({ text: frame.text, gap: index === 0 ? 0 : frame.time - frames[index - 1]!.time })); } + +/** + * Each card's packet capture against Java's (the Lauscher's list of the + * card, as the oracle wrote it: under the node's id for its first card, + * `id#index` for the others): the same frames in the same order, each at + * about the same time (both counted from the run's first frame, or from + * `starts`). `text` + * puts placeholders in a frame's text (ports, IDs a test cannot predict), + * `normalized` does so for a card's whole list; with `until` (Java's time), + * only the frames before it; with `from`, only those from it. + */ +export function expectSameCaptures( + engine: NetworkRuntimeEngine, + scenario: Scenario, + java: Record, + options: { + text?: (text: string) => string; + normalized?: (frames: SeenFrame[]) => SeenFrame[]; + from?: number | undefined; + until?: number | undefined; + tolerance?: number; + starts?: { java: number; engine: number }; + } = {}, +): void { + const text = options.text ?? ((each: string) => each); + const engineByCard = new Map(); + for (const node of scenario.nodes) { + if (node.kind === 'networkSwitch') continue; + node.ports.forEach((port, index) => { + const frames = engine.capture.frames(port.id).map((each) => ({ time: each.timeMilliseconds, text: text(engineText(each.frame)) })); + engineByCard.set(index === 0 ? node.id : `${node.id}#${index}`, frames); + }); + } + const javaByCard = new Map(Object.entries(java).map(([card, frames]) => [card, frames.map((frame) => ({ time: frame.time, text: text(javaText(frame)) }))])); + expect([...engineByCard.keys()].sort()).toEqual([...javaByCard.keys()].sort()); + const first = (byCard: Map) => Math.min(...[...byCard.values()].flat().map((frame) => frame.time)); + const engineStart = options.starts?.engine ?? first(engineByCard); + const javaStart = options.starts?.java ?? first(javaByCard); + const from = (options.from ?? Number.NEGATIVE_INFINITY) - javaStart; + const until = (options.until ?? Number.POSITIVE_INFINITY) - javaStart; + const normalized = options.normalized ?? ((frames: SeenFrame[]) => frames); + const window = (frames: SeenFrame[], start: number) => + frames.filter((frame) => frame.time - start >= from && frame.time - start < until).map((frame) => ({ time: frame.time - start, text: frame.text })); + for (const [card, javaList] of javaByCard) { + const expected = normalized(window(javaList, javaStart)); + const actual = normalized(window(engineByCard.get(card)!, engineStart)); + for (let index = 0; index < Math.max(expected.length, actual.length); index += 1) { + expect(actual[index]?.text, `${card} frame #${index + 1}`).toBe(expected[index]?.text); + expect(Math.abs(actual[index]!.time - expected[index]!.time), `${card} time of frame #${index + 1}`).toBeLessThanOrEqual( + options.tolerance ?? GAP_TOLERANCE_MILLISECONDS, + ); + } + } +} + +/** A client's free port as "client": Java draws it at random, the engine from its seed. */ +export function withClientPortText(text: string): string { + return text.replace(/:(\d{5})\b/g, (whole, port: string) => (Number(port) >= 49_152 ? ':client' : whole)); +} diff --git a/packages/core/test/natJava.test.ts b/packages/core/test/natJava.test.ts index 1185343..137f3bc 100644 --- a/packages/core/test/natJava.test.ts +++ b/packages/core/test/natJava.test.ts @@ -22,7 +22,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import type { Id, IPv4DeliveryResult, NetworkRuntimeEngine } from '../src/index.js'; -import { GAP_TOLERANCE_MILLISECONDS, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; +import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; import type { Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -144,6 +144,8 @@ const STORIES: Record void * end; the second server gets the datagram from the entry's WAN port, but its * answer finds no entry and stays at the router. The engine adds an entry for * the second remote end: its answer goes on to the PC, a cable later. + * (`answer` is the frame on the home router's WAN card, the first list it + * shows in.) */ const JAVA_BUGS: Record JavaRun> = { udpTwoRemotes: (java) => { @@ -162,7 +164,8 @@ const JAVA_BUGS: Record JavaRun> = { const wanPort = java.nat[0]!.match(/42\.0\.0\.20:(\d+)/)![1]; const received = java.events.find(([, event]) => event === 'other server received two')!; return { - frames: { ...java.frames, [LAN_PC]: [...java.frames[LAN_PC]!, onLan] }, + // The home router's LAN card sends it as it arrives on the WAN card; the PC's receives it a cable later. + frames: { ...java.frames, [`${GATEWAY}#1`]: [...java.frames[`${GATEWAY}#1`]!, { ...onLan, time: answer.time + 1 }], [LAN_PC]: [...java.frames[LAN_PC]!, onLan] }, events: java.events.map(([time, event], index) => index === java.events.length - 1 && event === 'empfangen returned null' ? [received[0] + 168, 'empfangen returned pong two'] : [time, event], ), @@ -171,8 +174,8 @@ const JAVA_BUGS: Record JavaRun> = { }, }; -/** Free ports as "port", initial sequence numbers by their order, ties sorted. */ -function normalized(frames: readonly SeenFrame[]): SeenFrame[] { +/** Free ports as "port", initial sequence numbers by their order. */ +function withPlaceholders(frames: readonly SeenFrame[]): SeenFrame[] { const bases = new Map(); const sequence = (value: string) => { const number = Number(value); @@ -180,10 +183,15 @@ function normalized(frames: readonly SeenFrame[]): SeenFrame[] { if (!bases.has(base)) bases.set(base, bases.size + 1); return `isn${bases.get(base)}+${number % 1_000_000}`; }; - const texts = frames.map((frame) => ({ + return frames.map((frame) => ({ time: frame.time, text: withPorts(frame.text).replace(/(seq|ack)=(\d+)/g, (whole, name: string, value: string) => (value === '0' ? whole : `${name}=${sequence(value)}`)), })); +} + +/** With placeholders, ties sorted: over all cards, Java's log writes frames that show together in any order. */ +function normalized(frames: readonly SeenFrame[]): SeenFrame[] { + const texts = withPlaceholders(frames); const result: SeenFrame[] = []; for (let start = 0; start < texts.length; ) { let end = start + 1; @@ -248,6 +256,12 @@ describe('NAT as Java FILIUS runs it', () => { } } + // A card's own list keeps its order. + expectSameCaptures(engine, scenario, java.frames, { + normalized: withPlaceholders, + tolerance: TCP_STORIES.has(name) ? TCP_TOLERANCE_MILLISECONDS : GAP_TOLERANCE_MILLISECONDS, + }); + // The client's calls: Java's server threads write events of their own, which the engine's story has not. const javaCalls = java.events.filter(([, event]) => !event.startsWith('server') && !event.startsWith('other server')); expect(events.map(([, event]) => event)).toEqual(javaCalls.map(([, event]) => event)); diff --git a/packages/core/test/ripJava.test.ts b/packages/core/test/ripJava.test.ts index fc7a9e4..a46800b 100644 --- a/packages/core/test/ripJava.test.ts +++ b/packages/core/test/ripJava.test.ts @@ -31,7 +31,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { RIP_FIRST_BEACON_DELAY_MILLISECONDS, type Id, type IPv4DeliveryResult, type NetworkRuntimeEngine } from '../src/index.js'; -import { GAP_TOLERANCE_MILLISECONDS, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; +import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; import type { Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -142,6 +142,9 @@ describe('RIP as Java FILIUS runs it', () => { } } + // Each card's capture, after the startup. + expectSameCaptures(engine, scenario, java.frames, { from: STARTUP_MILLISECONDS, starts: { java: offset, engine: 0 } }); + // Each router's table: the same state when the startup is over, then the same changes at about the same times. for (const router of routers) { const settled = (states: T[], until: number): T[] => { diff --git a/packages/core/test/scenarios/dhcp-conflict.java.json b/packages/core/test/scenarios/dhcp-conflict.java.json index dc8b33e..69fc4de 100644 --- a/packages/core/test/scenarios/dhcp-conflict.java.json +++ b/packages/core/test/scenarios/dhcp-conflict.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 459, + "time": 453, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -13,7 +13,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 464, + "time": 458, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -24,7 +24,7 @@ "payload": "DHCPOFFER\nyiaddr=192.168.0.100\nchaddr=02:00:00:00:01:12\nrouter=0.0.0.0\nsubnetmask=255.255.255.0\ndnsserver=0.0.0.0\nserverident=192.168.0.10" }, { - "time": 688, + "time": 682, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -32,7 +32,7 @@ "targetIp": "192.168.0.100" }, { - "time": 801, + "time": 794, "source": "02:00:00:00:01:13", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, @@ -40,7 +40,7 @@ "targetIp": "255.255.255.255" }, { - "time": 912, + "time": 905, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -51,7 +51,7 @@ "payload": "DHCPDECLINE\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 968, + "time": 961, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -62,7 +62,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 3663, + "time": 3656, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -73,7 +73,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 6413, + "time": 6406, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -84,7 +84,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 9164, + "time": 9157, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -95,7 +95,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11914, + "time": 11907, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -106,7 +106,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11915, + "time": 11907, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -117,7 +117,7 @@ "payload": "DHCPOFFER\nyiaddr=192.168.0.100\nchaddr=02:00:00:00:01:12\nrouter=0.0.0.0\nsubnetmask=255.255.255.0\ndnsserver=0.0.0.0\nserverident=192.168.0.10" }, { - "time": 12138, + "time": 12132, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -128,7 +128,7 @@ "payload": "DHCPDECLINE\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 12194, + "time": 12188, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -139,25 +139,25 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 14889, + "time": 14881, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "0.0.0.0", - "targetIp": "169.254.1.191" + "targetIp": "169.254.1.107" }, { - "time": 17640, + "time": 17632, "source": "02:00:00:00:01:12", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, - "senderIp": "169.254.1.191", + "senderIp": "169.254.1.107", "targetIp": "255.255.255.255" } ], "00000000-0000-0000-0000-000000000102": [ { - "time": 338, + "time": 331, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -168,7 +168,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 576, + "time": 569, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -179,7 +179,7 @@ "payload": "DHCPOFFER\nyiaddr=192.168.0.100\nchaddr=02:00:00:00:01:12\nrouter=0.0.0.0\nsubnetmask=255.255.255.0\ndnsserver=0.0.0.0\nserverident=192.168.0.10" }, { - "time": 577, + "time": 570, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -187,7 +187,7 @@ "targetIp": "192.168.0.100" }, { - "time": 801, + "time": 794, "source": "02:00:00:00:01:13", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, @@ -195,7 +195,7 @@ "targetIp": "255.255.255.255" }, { - "time": 801, + "time": 794, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -206,7 +206,7 @@ "payload": "DHCPDECLINE\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 857, + "time": 850, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -217,7 +217,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 3552, + "time": 3545, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -228,7 +228,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 6302, + "time": 6295, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -239,7 +239,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 9052, + "time": 9045, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -250,7 +250,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11802, + "time": 11795, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -261,7 +261,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 12026, + "time": 12019, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -272,7 +272,7 @@ "payload": "DHCPOFFER\nyiaddr=192.168.0.100\nchaddr=02:00:00:00:01:12\nrouter=0.0.0.0\nsubnetmask=255.255.255.0\ndnsserver=0.0.0.0\nserverident=192.168.0.10" }, { - "time": 12027, + "time": 12019, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -283,7 +283,7 @@ "payload": "DHCPDECLINE\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 12082, + "time": 12076, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -294,25 +294,25 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 14778, + "time": 14770, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "0.0.0.0", - "targetIp": "169.254.1.191" + "targetIp": "169.254.1.107" }, { - "time": 17528, + "time": 17521, "source": "02:00:00:00:01:12", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, - "senderIp": "169.254.1.191", + "senderIp": "169.254.1.107", "targetIp": "255.255.255.255" } ], "00000000-0000-0000-0000-000000000103": [ { - "time": 459, + "time": 453, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -323,7 +323,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 576, + "time": 569, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -334,7 +334,7 @@ "payload": "DHCPOFFER\nyiaddr=192.168.0.100\nchaddr=02:00:00:00:01:12\nrouter=0.0.0.0\nsubnetmask=255.255.255.0\ndnsserver=0.0.0.0\nserverident=192.168.0.10" }, { - "time": 688, + "time": 682, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -342,7 +342,7 @@ "targetIp": "192.168.0.100" }, { - "time": 690, + "time": 683, "source": "02:00:00:00:01:13", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, @@ -350,7 +350,7 @@ "targetIp": "255.255.255.255" }, { - "time": 912, + "time": 905, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -361,7 +361,7 @@ "payload": "DHCPDECLINE\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 968, + "time": 961, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -372,7 +372,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 3663, + "time": 3656, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -383,7 +383,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 6413, + "time": 6406, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -394,7 +394,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 9164, + "time": 9157, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -405,7 +405,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11914, + "time": 11907, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -416,7 +416,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 12026, + "time": 12019, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -427,7 +427,7 @@ "payload": "DHCPOFFER\nyiaddr=192.168.0.100\nchaddr=02:00:00:00:01:12\nrouter=0.0.0.0\nsubnetmask=255.255.255.0\ndnsserver=0.0.0.0\nserverident=192.168.0.10" }, { - "time": 12138, + "time": 12132, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -438,7 +438,7 @@ "payload": "DHCPDECLINE\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 12194, + "time": 12188, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -449,19 +449,19 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 14889, + "time": 14881, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "0.0.0.0", - "targetIp": "169.254.1.191" + "targetIp": "169.254.1.107" }, { - "time": 17640, + "time": 17632, "source": "02:00:00:00:01:12", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, - "senderIp": "169.254.1.191", + "senderIp": "169.254.1.107", "targetIp": "255.255.255.255" } ] @@ -474,7 +474,7 @@ "" ], "00000000-0000-0000-0000-000000000102": [ - "169.254.1.191", + "169.254.1.107", "255.255.0.0", "", "" diff --git a/packages/core/test/scenarios/dhcp-no-server.java.json b/packages/core/test/scenarios/dhcp-no-server.java.json index 6c82241..be4ff93 100644 --- a/packages/core/test/scenarios/dhcp-no-server.java.json +++ b/packages/core/test/scenarios/dhcp-no-server.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 466, + "time": 457, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -13,7 +13,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 3199, + "time": 3189, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -24,7 +24,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 5949, + "time": 5943, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -35,7 +35,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 8699, + "time": 8690, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -46,7 +46,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11450, + "time": 11440, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -57,25 +57,25 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 14201, + "time": 14191, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "0.0.0.0", - "targetIp": "169.254.1.72" + "targetIp": "169.254.1.174" }, { - "time": 16951, + "time": 16941, "source": "02:00:00:00:01:12", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, - "senderIp": "169.254.1.72", + "senderIp": "169.254.1.174", "targetIp": "255.255.255.255" } ], "00000000-0000-0000-0000-000000000102": [ { - "time": 342, + "time": 333, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -86,7 +86,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 3087, + "time": 3078, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -97,7 +97,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 5838, + "time": 5828, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -108,7 +108,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 8588, + "time": 8578, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -119,7 +119,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11338, + "time": 11329, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -130,25 +130,25 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 14089, + "time": 14080, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "0.0.0.0", - "targetIp": "169.254.1.72" + "targetIp": "169.254.1.174" }, { - "time": 16840, + "time": 16830, "source": "02:00:00:00:01:12", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, - "senderIp": "169.254.1.72", + "senderIp": "169.254.1.174", "targetIp": "255.255.255.255" } ], "00000000-0000-0000-0000-000000000103": [ { - "time": 466, + "time": 457, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -159,7 +159,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 3199, + "time": 3189, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -170,7 +170,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 5949, + "time": 5943, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -181,7 +181,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 8699, + "time": 8690, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -192,7 +192,7 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 11450, + "time": 11440, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -203,19 +203,19 @@ "payload": "DHCPDISCOVER\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12" }, { - "time": 14201, + "time": 14191, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "0.0.0.0", - "targetIp": "169.254.1.72" + "targetIp": "169.254.1.174" }, { - "time": 16951, + "time": 16941, "source": "02:00:00:00:01:12", "destination": "ff:ff:ff:ff:ff:ff", "arp": 2, - "senderIp": "169.254.1.72", + "senderIp": "169.254.1.174", "targetIp": "255.255.255.255" } ] @@ -228,7 +228,7 @@ "" ], "00000000-0000-0000-0000-000000000102": [ - "169.254.1.72", + "169.254.1.174", "255.255.0.0", "", "" diff --git a/packages/core/test/scenarios/dhcp-two-servers.java.json b/packages/core/test/scenarios/dhcp-two-servers.java.json index 51055e7..5d1f07c 100644 --- a/packages/core/test/scenarios/dhcp-two-servers.java.json +++ b/packages/core/test/scenarios/dhcp-two-servers.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 455, + "time": 454, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -67,7 +67,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 333, + "time": 332, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -97,7 +97,7 @@ "targetIp": "192.168.0.100" }, { - "time": 627, + "time": 628, "source": "02:00:00:00:01:13", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.30", @@ -119,7 +119,7 @@ "payload": "DHCPREQUEST\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 3547, + "time": 3546, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", @@ -132,7 +132,7 @@ ], "00000000-0000-0000-0000-000000000103": [ { - "time": 455, + "time": 454, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "0.0.0.0", @@ -184,7 +184,7 @@ "payload": "DHCPREQUEST\nyiaddr=0.0.0.0\nchaddr=02:00:00:00:01:12\nrequested=192.168.0.100\nserverident=192.168.0.10" }, { - "time": 3547, + "time": 3546, "source": "02:00:00:00:01:11", "destination": "FF:FF:FF:FF:FF:FF", "sourceIp": "192.168.0.10", diff --git a/packages/core/test/scenarios/tcp-java-closedPort.java.json b/packages/core/test/scenarios/tcp-java-closedPort.java.json index 6b024fd..0829ef6 100644 --- a/packages/core/test/scenarios/tcp-java-closedPort.java.json +++ b/packages/core/test/scenarios/tcp-java-closedPort.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 852, + "time": 853, "source": "02:00:00:00:01:13", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -18,13 +18,13 @@ "targetIp": "192.168.0.30" }, { - "time": 1081, + "time": 1083, "source": "02:00:00:00:01:13", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.30", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 62251, + "sourcePort": 50448, "destinationPort": 81, "payload": null, "tcpFlags": "S", @@ -34,7 +34,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 852, + "time": 853, "source": "02:00:00:00:01:13", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -52,7 +52,7 @@ "targetIp": "192.168.0.10" }, { - "time": 965, + "time": 966, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:13", "arp": 2, @@ -66,7 +66,7 @@ "sourceIp": "192.168.0.30", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 62251, + "sourcePort": 50448, "destinationPort": 81, "payload": null, "tcpFlags": "S", @@ -96,7 +96,7 @@ "SYN_SENT" ], [ - 8979, + 8978, "CLOSED" ] ] diff --git a/packages/core/test/scenarios/tcp-java-exchange.java.json b/packages/core/test/scenarios/tcp-java-exchange.java.json index 2fe65ad..87b5d98 100644 --- a/packages/core/test/scenarios/tcp-java-exchange.java.json +++ b/packages/core/test/scenarios/tcp-java-exchange.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 857, + "time": 856, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -10,7 +10,7 @@ "targetIp": "192.168.0.10" }, { - "time": 858, + "time": 857, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -18,13 +18,13 @@ "targetIp": "192.168.0.20" }, { - "time": 1086, + "time": 1084, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -32,27 +32,27 @@ "acknowledgement": 0 }, { - "time": 1088, + "time": 1086, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1312, + "time": 1309, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -60,13 +60,13 @@ "acknowledgement": 1000001 }, { - "time": 1367, + "time": 1364, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": "GET /", "tcpFlags": "P", @@ -74,41 +74,41 @@ "acknowledgement": 0 }, { - "time": 1368, + "time": 1365, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "A", "sequence": 1000001, "acknowledgement": 2000006 }, { - "time": 1423, + "time": 1420, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "tcpFlags": "", "sequence": 1000001, "acknowledgement": 0 }, { - "time": 1646, + "time": 1643, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -116,27 +116,27 @@ "acknowledgement": 1001461 }, { - "time": 1647, + "time": 1644, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "tcpFlags": "", "sequence": 1001461, "acknowledgement": 0 }, { - "time": 1869, + "time": 1867, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -144,27 +144,27 @@ "acknowledgement": 1002921 }, { - "time": 1870, + "time": 1867, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "tcpFlags": "P", "sequence": 1002921, "acknowledgement": 0 }, { - "time": 2092, + "time": 2090, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -172,13 +172,13 @@ "acknowledgement": 1003001 }, { - "time": 2148, + "time": 2145, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -186,41 +186,41 @@ "acknowledgement": 0 }, { - "time": 2148, + "time": 2145, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "A", "sequence": 1003001, "acknowledgement": 2000007 }, { - "time": 2204, + "time": 2201, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "F", "sequence": 1003001, "acknowledgement": 0 }, { - "time": 2426, + "time": 2423, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -230,7 +230,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 733, + "time": 731, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -238,7 +238,7 @@ "targetIp": "192.168.0.10" }, { - "time": 970, + "time": 968, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -246,13 +246,13 @@ "targetIp": "192.168.0.20" }, { - "time": 972, + "time": 971, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -260,27 +260,27 @@ "acknowledgement": 0 }, { - "time": 1200, + "time": 1197, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1200, + "time": 1198, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -288,13 +288,13 @@ "acknowledgement": 1000001 }, { - "time": 1256, + "time": 1253, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": "GET /", "tcpFlags": "P", @@ -302,41 +302,41 @@ "acknowledgement": 0 }, { - "time": 1479, + "time": 1476, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "A", "sequence": 1000001, "acknowledgement": 2000006 }, { - "time": 1535, + "time": 1532, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "tcpFlags": "", "sequence": 1000001, "acknowledgement": 0 }, { - "time": 1535, + "time": 1532, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -344,27 +344,27 @@ "acknowledgement": 1001461 }, { - "time": 1758, + "time": 1755, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "tcpFlags": "", "sequence": 1001461, "acknowledgement": 0 }, { - "time": 1758, + "time": 1756, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -372,27 +372,27 @@ "acknowledgement": 1002921 }, { - "time": 1981, + "time": 1978, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "tcpFlags": "P", "sequence": 1002921, "acknowledgement": 0 }, { - "time": 1981, + "time": 1979, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -400,13 +400,13 @@ "acknowledgement": 1003001 }, { - "time": 2037, + "time": 2034, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -414,41 +414,41 @@ "acknowledgement": 0 }, { - "time": 2259, + "time": 2256, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "A", "sequence": 1003001, "acknowledgement": 2000007 }, { - "time": 2315, + "time": 2312, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61961, + "destinationPort": 49357, "payload": null, "tcpFlags": "F", "sequence": 1003001, "acknowledgement": 0 }, { - "time": 2315, + "time": 2312, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61961, + "sourcePort": 49357, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -458,7 +458,7 @@ ], "00000000-0000-0000-0000-000000000103": [ { - "time": 857, + "time": 856, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -469,104 +469,116 @@ }, "events": [ [ - 216, + 215, "server oeffnen called" ], [ - 717, + 716, "client verbinden called" ], [ - 1218, + 1217, "client verbinden returned" ], [ - 1218, + 1217, "client senden called" ], [ - 1319, + 1318, "server oeffnen returned" ], [ - 1319, + 1318, "server empfangen called" ], [ - 1368, + 1365, "server empfangen returned 5 characters" ], [ - 1368, + 1365, "server senden called" ], [ - 1479, + 1476, "client senden returned" ], [ - 1479, + 1476, "client empfangen called" ], [ - 1981, + 1979, "client empfangen returned 3000 characters" ], [ - 1981, + 1979, "client schliessen called" ], [ - 1981, + 1979, "client schliessen returned" ], [ - 2092, + 2090, "server senden returned" + ], + [ + 3146, + "server schliessen called" + ], + [ + 3146, + "server schliessen returned" ] ], "states": { "client": [ [ - 717, + 716, "CLOSED" ], [ - 729, + 727, "SYN_SENT" ], [ - 1202, + 1199, "ESTABLISHED" ], [ - 1982, + 1981, "FIN_WAIT_1" ], [ - 2260, + 2256, "FIN_WAIT_2" ], [ - 2315, + 2312, "TIME_WAIT" ], [ - 10565, + 10562, "CLOSED" ] ], "server": [ [ - 1319, + 1318, "ESTABLISHED" ], [ - 2148, + 2145, + "CLOSE_WAIT" + ], + [ + 2147, "LAST_ACK" ], [ - 2428, + 2423, "CLOSED" ] ] diff --git a/packages/core/test/scenarios/tcp-java-lostData.java.json b/packages/core/test/scenarios/tcp-java-lostData.java.json index 52f911d..29d9a69 100644 --- a/packages/core/test/scenarios/tcp-java-lostData.java.json +++ b/packages/core/test/scenarios/tcp-java-lostData.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 854, + "time": 853, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -24,7 +24,7 @@ "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -32,27 +32,27 @@ "acknowledgement": 0 }, { - "time": 1084, + "time": 1085, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61897, + "destinationPort": 51723, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1307, + "time": 1308, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -60,13 +60,13 @@ "acknowledgement": 1000001 }, { - "time": 26579, + "time": 26578, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -74,41 +74,41 @@ "acknowledgement": 0 }, { - "time": 26579, + "time": 26578, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61897, + "destinationPort": 51723, "payload": null, "tcpFlags": "A", "sequence": 1000001, "acknowledgement": 2000010 }, { - "time": 26635, + "time": 26634, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61897, + "destinationPort": 51723, "payload": null, "tcpFlags": "F", "sequence": 1000001, "acknowledgement": 0 }, { - "time": 26858, + "time": 26856, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -118,7 +118,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 731, + "time": 729, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -126,7 +126,7 @@ "targetIp": "192.168.0.10" }, { - "time": 967, + "time": 966, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -140,7 +140,7 @@ "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -155,20 +155,20 @@ "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61897, + "destinationPort": 51723, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1196, + "time": 1197, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -176,13 +176,13 @@ "acknowledgement": 1000001 }, { - "time": 1717, + "time": 1716, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": "verloren", "tcpFlags": "P", @@ -190,13 +190,13 @@ "acknowledgement": 0 }, { - "time": 9967, + "time": 9966, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": "verloren", "tcpFlags": "P", @@ -204,13 +204,13 @@ "acknowledgement": 0 }, { - "time": 18217, + "time": 18216, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": "verloren", "tcpFlags": "P", @@ -224,7 +224,7 @@ "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -232,41 +232,41 @@ "acknowledgement": 0 }, { - "time": 26691, + "time": 26690, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61897, + "destinationPort": 51723, "payload": null, "tcpFlags": "A", "sequence": 1000001, "acknowledgement": 2000010 }, { - "time": 26746, + "time": 26745, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61897, + "destinationPort": 51723, "payload": null, "tcpFlags": "F", "sequence": 1000001, "acknowledgement": 0 }, { - "time": 26747, + "time": 26745, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61897, + "sourcePort": 51723, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -276,18 +276,32 @@ ], "00000000-0000-0000-0000-000000000103": [ { - "time": 854, + "time": 853, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "192.168.0.20", "targetIp": "192.168.0.10" + }, + { + "time": 26578, + "source": "02:00:00:00:01:12", + "destination": "02:00:00:00:01:11", + "sourceIp": "192.168.0.20", + "destinationIp": "192.168.0.10", + "protocol": 6, + "sourcePort": 51723, + "destinationPort": 80, + "payload": null, + "tcpFlags": "F", + "sequence": 2000009, + "acknowledgement": 0 } ] }, "events": [ [ - 215, + 214, "server oeffnen called" ], [ @@ -295,15 +309,15 @@ "client verbinden called" ], [ - 1216, + 1215, "client verbinden returned" ], [ - 1317, + 1316, "server oeffnen returned" ], [ - 1716, + 1715, "loss on" ], [ @@ -322,19 +336,19 @@ "states": { "client": [ [ - 715, + 714, "CLOSED" ], [ - 727, + 725, "SYN_SENT" ], [ - 1197, + 1198, "ESTABLISHED" ], [ - 26469, + 26467, "FIN_WAIT_1" ], [ @@ -352,9 +366,13 @@ ], "server": [ [ - 1317, + 1316, "ESTABLISHED" ], + [ + 26578, + "CLOSE_WAIT" + ], [ 26580, "LAST_ACK" diff --git a/packages/core/test/scenarios/tcp-java-lostFin.java.json b/packages/core/test/scenarios/tcp-java-lostFin.java.json index fe81149..b45a8d4 100644 --- a/packages/core/test/scenarios/tcp-java-lostFin.java.json +++ b/packages/core/test/scenarios/tcp-java-lostFin.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 850, + "time": 855, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -10,7 +10,7 @@ "targetIp": "192.168.0.10" }, { - "time": 852, + "time": 857, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -18,13 +18,13 @@ "targetIp": "192.168.0.20" }, { - "time": 1079, + "time": 1086, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61346, + "sourcePort": 63329, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -32,27 +32,27 @@ "acknowledgement": 0 }, { - "time": 1081, + "time": 1088, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61346, + "destinationPort": 63329, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1305, + "time": 1311, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61346, + "sourcePort": 63329, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -62,7 +62,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 729, + "time": 733, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -70,7 +70,7 @@ "targetIp": "192.168.0.10" }, { - "time": 963, + "time": 969, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -78,13 +78,13 @@ "targetIp": "192.168.0.20" }, { - "time": 966, + "time": 972, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61346, + "sourcePort": 63329, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -92,27 +92,27 @@ "acknowledgement": 0 }, { - "time": 1193, + "time": 1199, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 61346, + "destinationPort": 63329, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1193, + "time": 1200, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61346, + "sourcePort": 63329, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -120,13 +120,13 @@ "acknowledgement": 1000001 }, { - "time": 1716, + "time": 1719, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 61346, + "sourcePort": 63329, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -136,7 +136,7 @@ ], "00000000-0000-0000-0000-000000000103": [ { - "time": 850, + "time": 855, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -147,56 +147,56 @@ }, "events": [ [ - 214, + 216, "server oeffnen called" ], [ - 715, + 718, "client verbinden called" ], [ - 1216, + 1218, "client verbinden returned" ], [ - 1317, + 1319, "server oeffnen returned" ], [ - 1716, + 1719, "client schliessen called" ], [ - 1716, + 1719, "client schliessen returned" ], [ - 1716, + 1719, "lost FIN" ] ], "states": { "client": [ [ - 715, + 717, "CLOSED" ], [ - 725, + 729, "SYN_SENT" ], [ - 1195, + 1201, "ESTABLISHED" ], [ - 1717, + 1719, "FIN_WAIT_1" ] ], "server": [ [ - 1317, + 1319, "ESTABLISHED" ] ] diff --git a/packages/core/test/scenarios/tcp-java-lostLastFin.java.json b/packages/core/test/scenarios/tcp-java-lostLastFin.java.json index 9a1f967..4a8b4c5 100644 --- a/packages/core/test/scenarios/tcp-java-lostLastFin.java.json +++ b/packages/core/test/scenarios/tcp-java-lostLastFin.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 856, + "time": 849, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -10,7 +10,7 @@ "targetIp": "192.168.0.10" }, { - "time": 858, + "time": 850, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -18,13 +18,13 @@ "targetIp": "192.168.0.20" }, { - "time": 1086, + "time": 1078, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 54938, + "sourcePort": 59631, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -32,27 +32,27 @@ "acknowledgement": 0 }, { - "time": 1088, + "time": 1079, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 54938, + "destinationPort": 59631, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1311, + "time": 1303, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 54938, + "sourcePort": 59631, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -60,13 +60,13 @@ "acknowledgement": 1000001 }, { - "time": 1834, + "time": 1827, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 54938, + "sourcePort": 59631, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -74,28 +74,28 @@ "acknowledgement": 0 }, { - "time": 1834, + "time": 1828, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 54938, + "destinationPort": 59631, "payload": null, "tcpFlags": "A", "sequence": 1000001, "acknowledgement": 2000002 }, { - "time": 1890, + "time": 1883, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 54938, + "destinationPort": 59631, "payload": null, "tcpFlags": "F", "sequence": 1000001, @@ -104,7 +104,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 734, + "time": 729, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -112,7 +112,7 @@ "targetIp": "192.168.0.10" }, { - "time": 970, + "time": 962, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -120,13 +120,13 @@ "targetIp": "192.168.0.20" }, { - "time": 972, + "time": 964, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 54938, + "sourcePort": 59631, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -134,27 +134,27 @@ "acknowledgement": 0 }, { - "time": 1199, + "time": 1191, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 54938, + "destinationPort": 59631, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 1200, + "time": 1191, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 54938, + "sourcePort": 59631, "destinationPort": 80, "payload": null, "tcpFlags": "A", @@ -162,13 +162,13 @@ "acknowledgement": 1000001 }, { - "time": 1722, + "time": 1716, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 54938, + "sourcePort": 59631, "destinationPort": 80, "payload": null, "tcpFlags": "F", @@ -176,14 +176,14 @@ "acknowledgement": 0 }, { - "time": 1946, + "time": 1939, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 54938, + "destinationPort": 59631, "payload": null, "tcpFlags": "A", "sequence": 1000001, @@ -192,7 +192,7 @@ ], "00000000-0000-0000-0000-000000000103": [ { - "time": 856, + "time": 849, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -203,64 +203,64 @@ }, "events": [ [ - 219, + 214, "server oeffnen called" ], [ - 720, + 714, "client verbinden called" ], [ - 1221, + 1215, "client verbinden returned" ], [ - 1321, + 1317, "server oeffnen returned" ], [ - 1721, + 1715, "client schliessen called" ], [ - 1721, + 1715, "client schliessen returned" ], [ - 1890, + 1883, "lost FIN" ] ], "states": { "client": [ [ - 720, + 714, "CLOSED" ], [ - 731, + 724, "SYN_SENT" ], [ - 1201, + 1191, "ESTABLISHED" ], [ - 1724, + 1716, "FIN_WAIT_1" ], [ - 1947, + 1940, "FIN_WAIT_2" ] ], "server": [ [ - 1322, + 1317, "ESTABLISHED" ], [ - 1835, + 1829, "LAST_ACK" ] ] diff --git a/packages/core/test/scenarios/tcp-java-lostSynAck.java.json b/packages/core/test/scenarios/tcp-java-lostSynAck.java.json index 714b4b8..a0ea244 100644 --- a/packages/core/test/scenarios/tcp-java-lostSynAck.java.json +++ b/packages/core/test/scenarios/tcp-java-lostSynAck.java.json @@ -2,7 +2,7 @@ "frames": { "00000000-0000-0000-0000-000000000101": [ { - "time": 855, + "time": 850, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -10,7 +10,7 @@ "targetIp": "192.168.0.10" }, { - "time": 856, + "time": 852, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -18,13 +18,13 @@ "targetIp": "192.168.0.20" }, { - "time": 1083, + "time": 1079, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 52633, + "sourcePort": 52891, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -32,28 +32,28 @@ "acknowledgement": 0 }, { - "time": 1085, + "time": 1081, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 52633, + "destinationPort": 52891, "payload": null, "tcpFlags": "SA", "sequence": 1000000, "acknowledgement": 2000001 }, { - "time": 17585, + "time": 17582, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 52633, + "destinationPort": 52891, "payload": null, "tcpFlags": "F", "sequence": 1000001, @@ -62,7 +62,7 @@ ], "00000000-0000-0000-0000-000000000102": [ { - "time": 733, + "time": 728, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, @@ -70,7 +70,7 @@ "targetIp": "192.168.0.10" }, { - "time": 968, + "time": 963, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "arp": 2, @@ -78,13 +78,13 @@ "targetIp": "192.168.0.20" }, { - "time": 970, + "time": 966, "source": "02:00:00:00:01:12", "destination": "02:00:00:00:01:11", "sourceIp": "192.168.0.20", "destinationIp": "192.168.0.10", "protocol": 6, - "sourcePort": 52633, + "sourcePort": 52891, "destinationPort": 80, "payload": null, "tcpFlags": "S", @@ -92,14 +92,14 @@ "acknowledgement": 0 }, { - "time": 17697, + "time": 17694, "source": "02:00:00:00:01:11", "destination": "02:00:00:00:01:12", "sourceIp": "192.168.0.10", "destinationIp": "192.168.0.20", "protocol": 6, "sourcePort": 80, - "destinationPort": 52633, + "destinationPort": 52891, "payload": null, "tcpFlags": "F", "sequence": 1000001, @@ -108,63 +108,81 @@ ], "00000000-0000-0000-0000-000000000103": [ { - "time": 855, + "time": 850, "source": "02:00:00:00:01:12", "destination": "FF:FF:FF:FF:FF:FF", "arp": 1, "senderIp": "192.168.0.20", "targetIp": "192.168.0.10" + }, + { + "time": 17696, + "source": "02:00:00:00:01:11", + "destination": "02:00:00:00:01:12", + "sourceIp": "192.168.0.10", + "destinationIp": "192.168.0.20", + "protocol": 6, + "sourcePort": 80, + "destinationPort": 52891, + "payload": null, + "tcpFlags": "F", + "sequence": 1000001, + "acknowledgement": 0 } ] }, "events": [ [ - 218, + 213, "server oeffnen called" ], [ - 719, + 714, "client verbinden called" ], [ - 1085, + 1081, "lost SYN+ACK" ], [ - 9025, + 9019, "client verbinden failed TimeOutException" ], [ - 17632, + 17626, "server oeffnen returned" ] ], "states": { "server": [ [ - 221, + 215, + "CLOSED" + ], + [ + 217, "LISTEN" ], [ - 1085, + 1081, "SYN_RCVD" ], [ - 17585, + 17582, "FIN_WAIT_1" ] ], "client": [ [ - 719, + 713, "CLOSED" ], [ - 731, + 724, "SYN_SENT" ], [ - 8980, + 8974, "CLOSED" ] ] diff --git a/packages/core/test/tcpJava.test.ts b/packages/core/test/tcpJava.test.ts index fd79971..6868a71 100644 --- a/packages/core/test/tcpJava.test.ts +++ b/packages/core/test/tcpJava.test.ts @@ -20,7 +20,7 @@ import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { TCP_FLAG, type Id, type NetworkRuntimeEngine, type TcpSegment } from '../src/index.js'; import { loseFirst } from './fixtures.js'; -import { GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame, type SeenFrame } from './javaRun.js'; +import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, withClientPortText, type JavaFrame, type SeenFrame } from './javaRun.js'; import type { Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -241,6 +241,17 @@ const JAVA_UNTIL: Record number> = { lostSynAck: (java) => java.states.client!.find(([, state]) => state === 'SYN_SENT')![0] + JAVA_TIMEOUT_MILLISECONDS - GAP_TOLERANCE_MILLISECONDS, }; +/** + * Where each card's capture is compared only up to a time (Java's), beyond + * JAVA_UNTIL. closedPort: JAVA_BUGS adds the client's two later SYNs to its + * own card, not where else they show — the switch has forgotten the server + * by then and floods them to every card. Compared up to the second SYN. + */ +const CAPTURE_UNTIL: Record number> = { + ...JAVA_UNTIL, + closedPort: (java) => java.states.client!.find(([, state]) => state === 'SYN_SENT')![0] + JAVA_TIMEOUT_MILLISECONDS - GAP_TOLERANCE_MILLISECONDS, +}; + describe('TCP as Java FILIUS runs it', () => { it('corrects Java only where it errs', () => { for (const [name, correct] of Object.entries(JAVA_BUGS)) { @@ -250,7 +261,7 @@ describe('TCP as Java FILIUS runs it', () => { }); it('cuts only where Java goes on with its error', () => { - for (const [name, until] of Object.entries(JAVA_UNTIL)) { + for (const [name, until] of Object.entries(CAPTURE_UNTIL)) { const java = JSON.parse(readFileSync(join(scenarioDirectory, `tcp-java-${name}.java.json`), 'utf8')) as JavaRun; const cut = until(java); const later = [...Object.values(java.frames).flat().map((frame) => frame.time), ...Object.values(java.states).flat().map(([time]) => time)]; @@ -288,13 +299,17 @@ describe('TCP as Java FILIUS runs it', () => { ); } + expectSameCaptures(engine, scenario, java.frames, { text: withClientPortText, until: CAPTURE_UNTIL[name]?.(measured) }); + const shift = (states: [number, string][], syn: number): [number, string][] => before(states.map(([time, state]) => [time - syn, state])); // From SYN_SENT: Java's first CLOSED is when the socket was made. const javaClient = java.states.client!.filter(([, state], index) => !(index === 0 && state === 'CLOSED')); expectSameStates('client', shift(engineStates(engine, story.client, false), engineSyn), shift(javaClient, javaSyn)); if (java.states.server !== undefined) { - // A listening server from its first change: Java's first LISTEN is when it began to listen. - const javaServer = story.serverListening === true ? java.states.server.slice(1) : java.states.server; + // A listening server from its first change: Java's first LISTEN is when it began to listen (its + // poll may have seen the socket CLOSED before). + const listening = java.states.server.findIndex(([, state]) => state === 'LISTEN'); + const javaServer = story.serverListening === true ? java.states.server.slice(listening + 1) : java.states.server; expectSameStates('server', shift(engineStates(engine, SERVER, story.serverListening !== true), engineSyn), shift(javaServer, javaSyn)); } }); diff --git a/packages/core/test/wlanJava.test.ts b/packages/core/test/wlanJava.test.ts index ca8d4e9..d0b47be 100644 --- a/packages/core/test/wlanJava.test.ts +++ b/packages/core/test/wlanJava.test.ts @@ -17,7 +17,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import type { Id, NetworkRuntimeEngine } from '../src/index.js'; -import { GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame } from './javaRun.js'; +import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, gaps, javaFrames, watchedEngine, type JavaFrame } from './javaRun.js'; import type { Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); @@ -76,6 +76,7 @@ describe('WLAN as Java FILIUS runs it', () => { expect(actual[index]?.text, `frame #${index + 1}`).toBe(expected[index]?.text); expect(Math.abs(actual[index]!.gap - expected[index]!.gap), `gap before frame #${index + 1}`).toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS); } + expectSameCaptures(engine, scenario, java.frames); }); } }); diff --git a/tools/java-oracle/README.md b/tools/java-oracle/README.md index 0a24683..ffdcd85 100644 --- a/tools/java-oracle/README.md +++ b/tools/java-oracle/README.md @@ -446,3 +446,26 @@ liest nur die Oberfläche): Swift verbindet zur Laufzeit jeden Rechner mit eingeschaltetem WLAN mit einem freien Port des Switches seiner SSID, auch ohne Kabel (`wirelessAssociations`). Der Kern folgt Java: Er kennt nur die Kabel. + +## Mitschnitt je Karte + +Was `ScenarioNetwork.writeFrames` schreibt, ist Javas Mitschnitt selbst +(`Lauscher.datenEinheiten`): je Karte jeder Frame, den sie gesendet oder +empfangen hat. Seit F2.8a vergleicht der Kern seinen Mitschnitt Karte für +Karte damit (`expectSameCaptures` in `packages/core/test/javaRun.ts`), nicht +mehr nur jeden Frame dort, wo er zuerst auftaucht. + +Dabei fiel auf (01.10.2026): `ScenarioNetwork` übernahm die Lernzeit eines +Switches (`switches..retention`) nicht; Java lief mit 300 s, der Kern +mit den 5 s der Szenarien `tcp-lan` und `dhcp-*`. Der alte Vergleich sah den +Unterschied nicht: Ein gefluteter Frame erscheint nur auf zusätzlichen +Karten. Seit F2.8a setzt das Werkzeug die Lernzeit +(`SwitchFirmware.setRetentionTime`), die TCP- und DHCP-Läufe sind neu +aufgenommen. Mit 5 s flutet auch Javas Switch in `lostData` den FIN nach +26 s an alle Karten, wie der Kern. `dhcp-lan` behält die alte Aufnahme: Die +neue unterscheidet sich nur darin, welcher der beiden Clients zuerst sendet; +nach 5 s gehen dort nur noch Broadcasts. + +Javas Switch prüft seine Tabelle einmal je Sekunde ab dem Start +(`SwitchFirmware.checkSAT`), ein Eintrag verfällt also bis zu einer Sekunde +nach der Lernzeit; der Kern genau bei ihr. Noch nicht gemessen. diff --git a/tools/java-oracle/src/ScenarioNetwork.java b/tools/java-oracle/src/ScenarioNetwork.java index b7884b6..394f678 100644 --- a/tools/java-oracle/src/ScenarioNetwork.java +++ b/tools/java-oracle/src/ScenarioNetwork.java @@ -144,10 +144,14 @@ public class ScenarioNetwork { cable.setAnschluesse(new Port[] { ports.get(link.get("sourcePort")), ports.get(link.get("targetPort")) }); cable.setWireless(Boolean.TRUE.equals(link.get("wireless"))); } - // A switch's SSID, a host's wireless card and the SSID it joins: what the - // editor reads to lay a wireless cable; the simulation does not ask. + // A switch's SSID and learning time; a host's wireless card and the SSID + // it joins. The SSIDs only the editor reads, to lay a wireless cable. for (Map.Entry entry : map(scenario.getOrDefault("switches", new HashMap<>())).entrySet()) { - ((SwitchFirmware) nodes.get(entry.getKey()).getSystemSoftware()).setSSID((String) map(entry.getValue()).get("ssid")); + Map config = map(entry.getValue()); + SwitchFirmware firmware = (SwitchFirmware) nodes.get(entry.getKey()).getSystemSoftware(); + firmware.setSSID((String) config.get("ssid")); + // How long the switch keeps a learned MAC address (Java's default 300 s). + if (config.containsKey("retention")) firmware.setRetentionTime(((Number) config.get("retention")).longValue()); } for (Map.Entry entry : map(scenario.getOrDefault("wireless", new HashMap<>())).entrySet()) { Betriebssystem os = (Betriebssystem) system(nodes.get(entry.getKey()));