feat(core): traceroute and tcpdump after Java, measured against Java

TerminalRun adds nine stories: traceroute in the LAN, through the router,
unreachable, without a route, to no one, to the host itself, errors and
Ctrl+C; tcpdump on two hosts. traceroute counts failures across hops as
Java does and takes an error that carries its probe; tcpdump tells each
frame's highest capture row with its time of day, at once.

Departures from Java: traceroute tells its summary before the end mark
(Java after it, where its window shows no text) and calls a host at the
20th hop reached. arpsend is not built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 10:46:18 +00:00
co-authored by Claude Opus 5.5
parent 495c449ef2
commit a867c33d64
16 changed files with 11983 additions and 16 deletions
+27
View File
@@ -861,3 +861,30 @@ Gemessen (02.10.2026):
über 1 s, mehr als 6 s zwischen zwei Timeouts ins Leere) wird wiederholt;
ebenso eine unter Last (andere Sitzungen auf dem Rechner: die erste
ARP-Runde dauert dann knapp 300 statt 225 ms).
## Befehlszeile, traceroute und tcpdump (F3.4c)
Weitere Abläufe von `TerminalRun` auf `terminal-net`. Gemessen (02.10.2026):
* `traceroute`: je Sprung bis zu drei Proben (Echo mit TTL), je eine RTT
auf die erste ICMP-Nachricht; eine Antwort zählt, wenn sie die Probe
trägt (Echo-Antwort mit ihrer Nummer, oder ein Fehler mit 0/0 und der
Probe darin), sonst „* “. Die Fehlzahl wird nur bei einem Treffer
zurückgesetzt: nach mehr als fünf Fehlschlägen bricht Java ab. Die
Nummern zählen ab 42 · 23, der Identifier ist der von `ping`.
* Das Fazit kommt nach der Laufmarke „fertig“, das Fenster zeigt es dann
nicht mehr (`GUIApplicationTerminalWindow.update` zeigt Text nur, solange
ein Befehl läuft). Erreicht das Ziel den 20. Sprung, nennt Java es
„nicht erreicht“ (`ttl >= maxHops`, aus dem Code; gemessen bis zwei
Sprünge).
* Eigene Adresse, ferne Router-Adresse, das „Host Unreachable“, das sich ein
Rechner selbst gibt: Java nennt 127.0.0.1 bzw. die nahe Karte.
* Hinter dem Router ohne Empfänger: Der Router hält während seiner
ARP-Suche die Sperre seines ICMP-Puffers (`ProtokollThread.run`
verarbeitet in `synchronized`); der Ethernet-Thread seiner Karte blockiert
beim Übergeben, und Rahmen dahinter liest und protokolliert die Karte erst
danach (die sechste Probe 2,5 s zu spät).
* `tcpdump`: je Rahmen der ersten Karte ab dem Start die oberste Zeile der
Tabelle (Uhrzeit, Protokoll, Quelle > Ziel, Kommentar), alle 100 ms;
Zeilenumbrüche werden zu zwei Leerzeichen; Strg+C beendet es nach dem
nächsten Blick. Ein während dessen getippter Befehl wartet bis danach.
+3 -1
View File
@@ -289,7 +289,9 @@ done
# The command line's network commands on terminal-net, typed at set times
# (TerminalRun; packages/core/test/terminalJava.test.ts), in the German locale.
for run in pingLan:8000 pingRouted:13000 pingNobody:49000 pingUnreachable:45000 pingBroadcast:27000 pingSelf:17000 \
pingErrors:14000 interrupt:16000 dns:15000 dnsTimeout:80000 arp:11000 netstat:10000; do
pingErrors:14000 interrupt:16000 dns:15000 dnsTimeout:80000 arp:11000 netstat:10000 traceLan:4000 \
traceRouted:6000 traceUnreachable:35000 traceNoRoute:4000 traceNobody:12000 traceSelf:3500 traceErrors:20000 \
traceInterrupt:12000 tcpdump:10000; do
story="${run%%:*}"
output="$root/packages/core/test/scenarios/terminal-java-$story.java.json"
rm -f "$output"
+60 -2
View File
@@ -31,6 +31,16 @@
// arp arp after a ping and a lookup, -a, -d, unknown options, no address, the broadcast row removed
// netstat netstat on the client connected to the echo server, on the echo server, on the DNS server;
// the connection closed, on both again
// traceLan traceroute .31 (one hop)
// traceRouted traceroute fern.filius.de (two hops), the router's far address
// traceUnreachable traceroute 10.0.0.99 (no one behind the router)
// traceNoRoute traceroute 172.16.0.1 (no route at the router)
// traceNobody traceroute .99 (no one in the LAN)
// traceSelf traceroute its own address, localhost
// traceErrors usage, an unknown name, no DNS server
// traceInterrupt traceroute 10.0.0.99, Ctrl+C after 3.5 s
// tcpdump tcpdump on .31 and on the DNS server while the client pings .31, looks up a name
// and connects to .31's echo server; arp typed on .31 meanwhile; Ctrl+C on both
import filius.software.clientserver.ClientBaustein;
import filius.software.clientserver.ServerBaustein;
@@ -167,6 +177,46 @@ public class TerminalRun {
acts.add(new Object[] { 8000L, CLIENT, "netstat" });
acts.add(new Object[] { 8200L, ECHO, "netstat" });
break;
case "traceLan":
acts.add(new Object[] { 500L, CLIENT, "traceroute 192.168.0.31" });
break;
case "traceRouted":
acts.add(new Object[] { 500L, CLIENT, "traceroute fern.filius.de" });
acts.add(new Object[] { 3000L, CLIENT, "traceroute 10.0.0.1" });
break;
case "traceUnreachable":
acts.add(new Object[] { 500L, CLIENT, "traceroute 10.0.0.99" });
break;
case "traceNoRoute":
acts.add(new Object[] { 500L, CLIENT, "traceroute 172.16.0.1" });
break;
case "traceNobody":
acts.add(new Object[] { 500L, CLIENT, "traceroute 192.168.0.99" });
break;
case "traceSelf":
acts.add(new Object[] { 500L, CLIENT, "traceroute 192.168.0.20" });
acts.add(new Object[] { 1500L, CLIENT, "traceroute localhost" });
break;
case "traceErrors":
acts.add(new Object[] { 500L, CLIENT, "traceroute" });
acts.add(new Object[] { 700L, CLIENT, "traceroute a b" });
acts.add(new Object[] { 900L, CLIENT, "traceroute gibtsnicht.filius.de" });
acts.add(new Object[] { 3000L, FAR, "traceroute www.filius.de" });
break;
case "traceInterrupt":
acts.add(new Object[] { 500L, CLIENT, "traceroute 10.0.0.99" });
acts.add(new Object[] { 4000L, CLIENT, "^C" });
break;
case "tcpdump":
acts.add(new Object[] { 500L, ECHO, "tcpdump" });
acts.add(new Object[] { 600L, DNS, "tcpdump" });
acts.add(new Object[] { 1000L, CLIENT, "ping 192.168.0.31" });
acts.add(new Object[] { 3000L, ECHO, "arp" });
acts.add(new Object[] { 5500L, CLIENT, "host www.filius.de" });
acts.add(new Object[] { 6500L, CLIENT, "connect" });
acts.add(new Object[] { 8000L, ECHO, "^C" });
acts.add(new Object[] { 8100L, DNS, "^C" });
break;
default:
throw new IllegalArgumentException("unknown story " + story);
}
@@ -205,7 +255,7 @@ public class TerminalRun {
terminals.put(host, terminal);
}
ClientBaustein client = null;
if (story.equals("netstat")) {
if (story.equals("netstat") || story.equals("tcpdump")) {
for (String host : new String[] { ECHO, DNS }) {
network.system(host).installApp("filius.software.clientserver.ServerBaustein", false);
ServerBaustein server = (ServerBaustein) network.system(host).holeSoftware("filius.software.clientserver.ServerBaustein");
@@ -216,7 +266,7 @@ public class TerminalRun {
client = (ClientBaustein) network.system(CLIENT).holeSoftware("filius.software.clientserver.ClientBaustein");
}
network.start();
if (client != null) {
if (story.equals("netstat")) {
sleepUntil(500);
event(CLIENT, "call", "connect 192.168.0.31:55555");
client.setZielIPAdresse("192.168.0.31");
@@ -225,6 +275,14 @@ public class TerminalRun {
}
for (Object[] act : acts) {
sleepUntil((Long) act[0]);
if (act[2].equals("connect")) {
// The echo client's window: connect to the echo server (ClientBaustein.verbinden).
event(CLIENT, "call", "connect 192.168.0.31:55555");
client.setZielIPAdresse("192.168.0.31");
client.setZielPort(55555);
client.verbinden();
continue;
}
if (act[2].equals("disconnect")) {
// The echo client's window: its connection closed (ClientBaustein.trennen).
event(CLIENT, "call", "disconnect");