feat: F4.6g5 the switch and NAT tables and "Pakete verwerfen", measured against Java's

ViewersRun takes the rows of Java's viewers from the firmware: a switch's
table, a home router's NAT table, each after its reset, and the frames
"Pakete verwerfen" drops while it is held. The core writes a frame as Java
does and resets the NAT's dynamic entries; the room sends both tables as
views, drops while any client holds the button, and sends the dropped
frames, begun anew with each start, with the clock their times of day are
written from; the editor opens a table with a tap on a switch or home
router and holds "Pakete verwerfen" in its toolbar, which now wraps on a
phone. This completes F4.6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 22:13:48 +00:00
co-authored by Claude Opus 5.5
parent 5224e1a28b
commit 6809a71300
31 changed files with 1370 additions and 31 deletions
+25
View File
@@ -1598,3 +1598,28 @@ Gemessen (03.10.2026):
das Fenster auffrischt.
* Die Dateitabelle zeigt eine heruntergeladene Datei erst, wenn das
Programm das nächste Mal Bescheid gibt; eine daneben angelegte gar nicht.
### SAT- und NAT-Anzeige, „Pakete verwerfen“ (F4.6g5)
`ViewersRun` misst, was Javas Anzeigen der laufenden Simulation zeigen. Die
Anzeigen (`SatViewer`, `NatViewer`, `SimulationControl`) zeigen nur Zeilen
der Firmware; die Sonde nimmt sie dort ab (`holeSAT`, `holeNAT`,
`getDroppedDataUnits`). Geschichte `switch` auf `switch-lan` (Haltezeit 5 s):
zwei Pings, die Tabelle, „Tabelle löschen“, ein Ping, nach der Haltezeit;
dann „Pakete verwerfen“ gedrückt (`Verbindung.setDrop`) während eines Pings
und losgelassen. Geschichte `nat` auf `nat-home`: die Portweiterleitung, ein
Ping und ein Datagramm hinaus, „Tabelle zurücksetzen“
(`packages/core/test/viewers-*.java.json`).
Gemessen (03.10.2026):
* Die Switch-Tabelle zeigt die Adresse groß, „Port <n>“ nach der Stelle des
Anschlusses und die Uhrzeit der letzten Meldung (`HH:mm:ss`), in der
Reihenfolge von Javas Hash-Tabelle; nach der Haltezeit ist sie leer.
* Die NAT-Tabelle zeigt die Portweiterleitung mit „*“ und „-“, einen Ping
als `0 (Identifier: 1)`, ein Datagramm mit gewürfeltem WAN-Port; „Tabelle
zurücksetzen“ lässt nur die Portweiterleitung stehen.
* „Pakete verwerfen“ wirkt nur, solange der Knopf gedrückt ist; die Liste
zeigt `HH:mm:ss.SSS : ` und den Frame in Javas Textform
(`[src=…, dest=…, type=0x800 | [id=…; ttl=64; …]]`; ein IP-Paket ohne sein
Segment).
+14 -1
View File
@@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then
fi
classpath="$upstream/target/filius.jar:$upstream/target/lib/*"
mkdir -p "$here/.build"
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java" "$here/src/EchoWindowRun.java" "$here/src/DnsWindowRun.java" "$here/src/FirewallWindowRun.java" "$here/src/GnutellaWindowRun.java"
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java" "$here/src/EchoWindowRun.java" "$here/src/DnsWindowRun.java" "$here/src/FirewallWindowRun.java" "$here/src/GnutellaWindowRun.java" "$here/src/ViewersRun.java"
# FILIUS writes its settings below $HOME; keep them out of the real one.
mkdir -p "$here/.build/home"
# Removed first: a crashed run must not leave the previous result standing.
@@ -464,6 +464,19 @@ HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -
"$root/packages/core/test/scenarios/terminal-net.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at \|scrollpane' || true
test -s "$output"
# What FILIUS' viewers of a running simulation show — a switch's table, a
# home router's NAT table, each after its reset — and the frames "Pakete
# verwerfen" drops while it is held (ViewersRun): the viewers
# (packages/core/test/viewers.test.ts).
for run in switch:switch-lan nat:nat-home; do
story="${run%%:*}"
output="$root/packages/core/test/viewers-$story.java.json"
rm -f "$output"
HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -cp "$classpath:$here/.build" ViewersRun \
"$root/packages/core/test/scenarios/${run##*:}.scenario.json" "$story" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
test -s "$output"
done
# FILIUS' own examples as Java FILIUS runs them, opened and started as FILIUS
# does (ExampleRun): the acceptance of F2 (packages/fls/test/examplesJava.test.ts)
# and the classics of F3 (web, email, gnutella; packages/fls/test/classicsJava.test.ts).
+169
View File
@@ -0,0 +1,169 @@
// What FILIUS' viewers of a running simulation show, in Java FILIUS: a
// switch's source address table (SwitchFirmware.holeSAT, shown by
// SatViewer), a home router's NAT table (GatewayFirmware.holeNAT, shown by
// NatViewer), each also after its "reset" (loescheSAT, loescheNAT), and the
// frames "Pakete verwerfen" drops while it is held (Verbindung.setDrop,
// Lauscher.getDroppedDataUnits, shown by SimulationControl). The viewers only
// show these rows, so they are taken from the firmware directly. Times are
// written with the run's start (the rows hold wall-clock times).
//
// ViewersRun <scenario.json> <story> <out.json>
//
// switch switch-lan (retention 5 s): .10 pings .20 and .30; the table;
// reset; .10 pings .20 again; the table; aged after the retention;
// "Pakete verwerfen" held while .10 pings .30; the dropped frames
// nat nat-home (port forwarding TCP 8080 to 192.168.0.10:80):
// 192.168.0.10 pings 42.0.0.20 and sends a datagram to its port 7;
// the table; reset; the table
import filius.hardware.Verbindung;
import filius.hardware.knoten.Switch;
import filius.rahmenprogramm.nachrichten.Lauscher;
import filius.software.system.GatewayFirmware;
import filius.software.system.InternetKnotenBetriebssystem;
import filius.software.system.SwitchFirmware;
import filius.software.transportschicht.UDPSocket;
import filius.software.vermittlungsschicht.IcmpPaket;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
public class ViewersRun {
static final String SWITCH = "00000000-0000-0000-0000-000000000104";
static final String PC10 = "00000000-0000-0000-0000-000000000101";
static final String GATEWAY = "00000000-0000-0000-0000-000000000502";
static final String LAN_PC = "00000000-0000-0000-0000-000000000501";
static final String SERVER = "00000000-0000-0000-0000-000000000504";
static ScenarioNetwork network;
static final List<Object> steps = new ArrayList<>();
static InternetKnotenBetriebssystem os(String id) {
return (InternetKnotenBetriebssystem) network.nodes.get(id).getSystemSoftware();
}
static String ping(String from, String target, int sequence) {
IcmpPaket answer = os(from).holeICMP().sendProbe(target, 64, 1, sequence);
return answer == null ? "null" : "ICMP " + answer.getIcmpType() + "/" + answer.getIcmpCode();
}
static List<List<String>> rows(Vector<Vector<String>> table) {
List<List<String>> rows = new ArrayList<>();
for (Vector<String> row : table) rows.add(new ArrayList<>(row));
return rows;
}
/** Each learned address's last update, in milliseconds since the start. */
@SuppressWarnings("unchecked")
static Map<String, Long> satTimes(SwitchFirmware firmware) throws Exception {
Field field = SwitchFirmware.class.getDeclaredField("sat");
field.setAccessible(true);
Map<String, Object> sat = (Map<String, Object>) field.get(firmware);
Map<String, Long> times = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : sat.entrySet()) {
Method update = entry.getValue().getClass().getMethod("holeLetztesUpdate");
update.setAccessible(true);
times.put(entry.getKey().toUpperCase(), ((java.util.Date) update.invoke(entry.getValue())).getTime() - network.start);
}
return times;
}
static void step(String name, Map<String, Object> shown) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("step", name);
result.put("at", System.currentTimeMillis() - network.start);
result.putAll(shown);
steps.add(result);
}
static Map<String, Object> sat() throws Exception {
SwitchFirmware firmware = (SwitchFirmware) network.nodes.get(SWITCH).getSystemSoftware();
firmware.checkSAT();
Map<String, Object> shown = new LinkedHashMap<>();
shown.put("rows", rows(firmware.holeSAT()));
shown.put("updated", satTimes(firmware));
shown.put("ports", ((Switch) network.nodes.get(SWITCH)).getAnschluesse().size());
return shown;
}
static Map<String, Object> nat() {
Map<String, Object> shown = new LinkedHashMap<>();
shown.put("rows", rows(((GatewayFirmware) network.nodes.get(GATEWAY).getSystemSoftware()).holeNAT()));
return shown;
}
public static void main(String[] args) throws Exception {
try {
run(args);
} catch (Throwable error) {
error.printStackTrace();
System.exit(1);
}
System.exit(0);
}
static void run(String[] args) throws Exception {
network = ScenarioNetwork.read(args[0]);
String story = args[1];
network.start();
Thread.sleep(500);
Map<String, Object> output = new LinkedHashMap<>();
output.put("startEpoch", network.start);
if (story.equals("switch")) {
Map<String, Object> answers = new LinkedHashMap<>();
answers.put("ping .20", ping(PC10, "192.168.0.20", 1));
answers.put("ping .30", ping(PC10, "192.168.0.30", 2));
Map<String, Object> shown = sat();
shown.put("answers", answers);
step("after pings", shown);
((SwitchFirmware) network.nodes.get(SWITCH).getSystemSoftware()).loescheSAT();
step("reset", sat());
Map<String, Object> again = sat();
again.put("answers", Map.of("ping .20", ping(PC10, "192.168.0.20", 3)));
again.putAll(sat());
step("ping .20 again", again);
Thread.sleep(6000);
step("after the retention", sat());
Lauscher.getLauscher().resetDroppedDataUnits();
Verbindung.setDrop(true);
String dropped = ping(PC10, "192.168.0.30", 4);
Verbindung.setDrop(false);
Map<String, Object> drop = new LinkedHashMap<>();
drop.put("answer", dropped);
drop.put("dropped", Lauscher.getLauscher().getDroppedDataUnits());
step("dropped while held", drop);
Map<String, Object> after = new LinkedHashMap<>();
after.put("answer", ping(PC10, "192.168.0.30", 5));
after.put("dropped", Lauscher.getLauscher().getDroppedDataUnits());
step("released", after);
} else {
step("start", nat());
Map<String, Object> answers = new LinkedHashMap<>();
answers.put("ping 42.0.0.20", ping(LAN_PC, "42.0.0.20", 1));
UDPSocket socket = new UDPSocket(os(LAN_PC), "42.0.0.20", 7);
socket.verbinden();
socket.senden("one");
answers.put("udp port", socket.holeLokalenPort());
Thread.sleep(1500);
Map<String, Object> shown = nat();
shown.put("answers", answers);
step("after ping and datagram", shown);
((GatewayFirmware) network.nodes.get(GATEWAY).getSystemSoftware()).loescheNAT();
step("reset", nat());
}
output.put("steps", steps);
try (FileOutputStream out = new FileOutputStream(args[2])) {
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
}
network.stop();
System.out.println("viewer steps: " + steps.size());
}
}