feat(core): modems run — the pair in the project, and a modem of another project hands its frames on (K0–K2)
Measured first: tools/java-oracle's ModemRun joins two networks by a pair of Java modems over TCP on localhost. The modem has no card and adds no delay of its own — a frame takes the cables on both sides, nothing more. The core pairs the two enabled modems on a port (Swift's inProject, with its conditions and cycle suppression) on Java's timing: no 20 ms. The modem example of FILIUS now pings through instead of failing the core. A modem of another project (relay) gives its frames to whoever runs the engine and puts on its cable what comes back, with this engine's frame and packet numbers; a frame dies after MAXIMUM_RELAY_HOPS trips. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -345,6 +345,35 @@ seit F2.4b-2: ein Senden kehrt sofort zurück wie in Java (Swifts Senden wartete
|
||||
alle Frames angekommen waren), und je Knoten und Protokoll sendet ein Transport-Thread
|
||||
ein Segment nach dem anderen.
|
||||
|
||||
## Modems zur Laufzeit
|
||||
|
||||
`ModemRun` verbindet auf dem Netz von `modem-lan` zwei Netze über ein
|
||||
Modem-Paar: ein Server und ein Client auf `localhost`, Javas
|
||||
`ModemFirmware` über einen echten TCP-Socket. `ScenarioNetwork` baut dazu
|
||||
`Modem`s und stellt sie nach `modems` ein (Modus, Port).
|
||||
`connectModems()` drückt den Knopf des Einstellungsfelds: erst lauschen die
|
||||
Server, dann verbinden sich die Clients. 10.0.0.1 und 10.0.0.3 hängen an
|
||||
einem Switch mit dem Server-Modem, 10.0.0.2 direkt am Client-Modem. Das
|
||||
Werkzeug schreibt nach `modem-java-<ablauf>.java.json`. Abläufe: `ping`
|
||||
(die Modems verbunden, zwei Pings von 10.0.0.1 an 10.0.0.2) und
|
||||
`unconnected` (nicht verbunden, ein Ping).
|
||||
|
||||
Gemessen (11.10.2026):
|
||||
|
||||
* Das Modem hat **keine Karte**: Es erscheint in keinem Mitschnitt.
|
||||
* Das Paar fügt **keine eigene Verzögerung** hinzu. Die ARP-Anfrage
|
||||
braucht zu 10.0.0.3 zwei Kabel (124 ms), zu 10.0.0.2 drei (181 ms). Die
|
||||
TCP-Strecke zwischen den Modems ist dagegen nicht messbar. Swifts 20 ms
|
||||
je Richtung übernimmt der Kern deshalb nicht.
|
||||
* Ohne Verbindung fragt ARP zweimal und bekommt keine Antwort, der Ping
|
||||
endet nach 5,5 s mit „Host unreachable“ vom eigenen Rechner. Auf der
|
||||
anderen Seite kommt nichts an.
|
||||
|
||||
Der Kern spielt beide Abläufe wie Java (`packages/core/test/modemJava.test.ts`).
|
||||
Er paart die beiden Modems an einem Port, und das Modem ohne Partner
|
||||
verwirft. Dass die Verzögerung genau null ist, prüft ein eigener Test: Die
|
||||
Toleranz des Vergleichs (120 ms) sähe 20 ms nicht.
|
||||
|
||||
## RIP zur Laufzeit
|
||||
|
||||
`RipRun` startet das Netz von `rip-chain` (PC 10.0.1.2 – R1 – R2 – R3 – PC
|
||||
|
||||
@@ -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" "$here/src/ViewersRun.java" "$here/src/ExchangeWindowRun.java" "$here/src/DocuRun.java" "$here/src/ExportRun.java" "$here/src/ReportRun.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/ModemRun.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" "$here/src/ExchangeWindowRun.java" "$here/src/DocuRun.java" "$here/src/ExportRun.java" "$here/src/ReportRun.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.
|
||||
@@ -135,6 +135,18 @@ for run in dhcp-lan:8000 dhcp-two-servers:8000 dhcp-conflict:25000 dhcp-no-serve
|
||||
test -s "$output"
|
||||
done
|
||||
|
||||
# Modems as Java FILIUS runs them: modem-lan's two networks joined by a pair
|
||||
# of modems (server and client over TCP on localhost); frames and the calls,
|
||||
# for the core's modem comparison (packages/core/test/modemJava.test.ts).
|
||||
for run in ping:8000 unconnected:9000; do
|
||||
story="${run%%:*}"
|
||||
output="$root/packages/core/test/scenarios/modem-java-$story.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" ModemRun \
|
||||
"$root/packages/core/test/scenarios/modem-lan.scenario.json" "$story" "${run##*:}" "$output" 2>&1 | grep -v 'logback\|^$' || true
|
||||
test -s "$output"
|
||||
done
|
||||
|
||||
# TCP as Java FILIUS runs it: stories of socket calls on tcp-lan's network
|
||||
# (TcpRun); frames, calls and socket states, for the core's TCP comparison.
|
||||
for run in exchange:30000 closedPort:30000 lostData:45000 lostSynAck:70000 lostFin:60000 lostLastFin:60000; do
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Runs a story on modem-lan's network in Java FILIUS (ScenarioNetwork): two
|
||||
// networks joined by a pair of modems, a server and a client on localhost
|
||||
// (ModemFirmware over a real TCP socket). Records every frame each card saw
|
||||
// and when each call started and ended.
|
||||
//
|
||||
// ModemRun <scenario.json> <story> <milliseconds> <out.json>
|
||||
//
|
||||
// 10.0.0.1 and 10.0.0.3 sit on a switch with the server modem; 10.0.0.2 is
|
||||
// cabled straight to the client modem. The stories:
|
||||
// ping the modems connected, 10.0.0.1 pings 10.0.0.2 twice
|
||||
// unconnected the modems not connected, 10.0.0.1 pings 10.0.0.2: ARP
|
||||
// goes into the modem and no further
|
||||
|
||||
import filius.software.vermittlungsschicht.IcmpPaket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ModemRun {
|
||||
static final String HOST = "00000000-0000-0000-0000-000000000401";
|
||||
|
||||
static ScenarioNetwork network;
|
||||
static final List<Object[]> events = new ArrayList<>();
|
||||
|
||||
interface Call {
|
||||
String run() throws Exception;
|
||||
}
|
||||
|
||||
static synchronized void event(String what) {
|
||||
events.add(new Object[] { System.currentTimeMillis() - network.start, what });
|
||||
}
|
||||
|
||||
static void call(String label, Call call) {
|
||||
event(label + " called");
|
||||
try {
|
||||
event(label + " returned " + call.run());
|
||||
} catch (Exception e) {
|
||||
event(label + " failed " + e.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
static String icmp(IcmpPaket packet) {
|
||||
return packet == null ? "null" : "ICMP " + packet.getIcmpType() + "/" + packet.getIcmpCode() + " from " + packet.getSender();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
network = ScenarioNetwork.read(args[0]);
|
||||
String story = args[1];
|
||||
long end = Long.parseLong(args[2]);
|
||||
// Connected in the design, as one does with the settings panel's button before starting.
|
||||
if (!story.equals("unconnected")) network.connectModems();
|
||||
network.start();
|
||||
Thread.sleep(500);
|
||||
switch (story) {
|
||||
case "ping":
|
||||
call("sendProbe 10.0.0.2", () -> icmp(network.system(HOST).holeICMP().sendProbe("10.0.0.2", 64, 1, 1)));
|
||||
call("sendProbe 10.0.0.2", () -> icmp(network.system(HOST).holeICMP().sendProbe("10.0.0.2", 64, 1, 2)));
|
||||
break;
|
||||
case "unconnected":
|
||||
call("sendProbe 10.0.0.2", () -> icmp(network.system(HOST).holeICMP().sendProbe("10.0.0.2", 64, 1, 1)));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown story " + story);
|
||||
}
|
||||
long rest = network.start + end - System.currentTimeMillis();
|
||||
if (rest > 0) Thread.sleep(rest);
|
||||
|
||||
ScenarioNetwork.Json out = new ScenarioNetwork.Json();
|
||||
out.open('{');
|
||||
network.writeFrames(out);
|
||||
synchronized (ModemRun.class) {
|
||||
out.key("events").open('[');
|
||||
for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).close(']');
|
||||
out.close(']');
|
||||
}
|
||||
out.close('}');
|
||||
network.stop();
|
||||
ScenarioNetwork.write(args[3], out);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import filius.hardware.knoten.Gateway;
|
||||
import filius.hardware.knoten.InternetKnoten;
|
||||
import filius.hardware.knoten.Knoten;
|
||||
import filius.hardware.knoten.LokalerKnoten;
|
||||
import filius.hardware.knoten.Modem;
|
||||
import filius.hardware.knoten.Notebook;
|
||||
import filius.hardware.knoten.Rechner;
|
||||
import filius.hardware.knoten.Switch;
|
||||
@@ -30,6 +31,7 @@ import filius.software.netzzugangsschicht.EthernetFrame;
|
||||
import filius.software.system.Betriebssystem;
|
||||
import filius.software.system.GatewayFirmware;
|
||||
import filius.software.system.InternetKnotenBetriebssystem;
|
||||
import filius.software.system.ModemFirmware;
|
||||
import filius.software.system.SwitchFirmware;
|
||||
import filius.software.system.VermittlungsrechnerBetriebssystem;
|
||||
import filius.software.transportschicht.Segment;
|
||||
@@ -58,6 +60,8 @@ public class ScenarioNetwork {
|
||||
/** Each card's MAC address, under its node's id (the first card) or `id#index` (the others). */
|
||||
final Map<String, String> macByNode = new LinkedHashMap<>();
|
||||
final List<String> servers = new ArrayList<>();
|
||||
/** The modems of the scenario, servers and clients. */
|
||||
final List<String> modems = new ArrayList<>();
|
||||
/** Loaded from a .fls file (fromFile): started as the action mode starts it, in the file's order. */
|
||||
boolean fromFile;
|
||||
/** fromFile: each card's MAC address and its node's name with the card's index, `name/index`. */
|
||||
@@ -136,6 +140,10 @@ public class ScenarioNetwork {
|
||||
// Its first card is the WAN port, its second the LAN port.
|
||||
knoten = new Gateway();
|
||||
break;
|
||||
case "remoteLink":
|
||||
// Java's modem: one port, no card; it joins its partner over TCP (modems below).
|
||||
knoten = new Modem();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("node kind " + kind + " is not built by this tool");
|
||||
}
|
||||
@@ -235,6 +243,17 @@ public class ScenarioNetwork {
|
||||
for (Object each : list(scenario.getOrDefault("rip", new ArrayList<>()))) {
|
||||
((VermittlungsrechnerBetriebssystem) system(nodes.get((String) each))).setRipEnabled(true);
|
||||
}
|
||||
// Modems: Java pairs a server and a client over TCP on the port (the client
|
||||
// connects to ipAdresse); connectModems() makes the connection, as the
|
||||
// button of the settings panel does.
|
||||
for (Map.Entry<String, Object> entry : map(scenario.getOrDefault("modems", new HashMap<>())).entrySet()) {
|
||||
Map<String, Object> config = map(entry.getValue());
|
||||
ModemFirmware firmware = (ModemFirmware) nodes.get(entry.getKey()).getSystemSoftware();
|
||||
firmware.setMode("server".equals(config.get("mode")) ? ModemFirmware.SERVER : ModemFirmware.CLIENT);
|
||||
firmware.setPort(((Number) config.get("port")).intValue());
|
||||
firmware.setIpAdresse("localhost");
|
||||
modems.add(entry.getKey());
|
||||
}
|
||||
// Firewalls: the core's defaults (inactive, drop, ICMP through, SYN only, UDP filtered) and its rules.
|
||||
for (Map.Entry<String, Object> entry : map(scenario.getOrDefault("firewalls", new HashMap<>())).entrySet()) {
|
||||
Map<String, Object> config = map(entry.getValue());
|
||||
@@ -285,6 +304,20 @@ public class ScenarioNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
/** Every server modem listens, then every client connects (JModemKonfiguration's button). */
|
||||
void connectModems() throws InterruptedException {
|
||||
for (String id : modems) {
|
||||
ModemFirmware firmware = (ModemFirmware) nodes.get(id).getSystemSoftware();
|
||||
if (firmware.getMode() == ModemFirmware.SERVER) firmware.starteServer();
|
||||
}
|
||||
Thread.sleep(300);
|
||||
for (String id : modems) {
|
||||
ModemFirmware firmware = (ModemFirmware) nodes.get(id).getSystemSoftware();
|
||||
if (firmware.getMode() == ModemFirmware.CLIENT) firmware.starteClient();
|
||||
}
|
||||
Thread.sleep(300);
|
||||
}
|
||||
|
||||
InternetKnotenBetriebssystem system(String nodeId) {
|
||||
return (InternetKnotenBetriebssystem) nodes.get(nodeId).getSystemSoftware();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user