feat(fls): system software settings as Java loads them
readSystemSoftware adds to each node what the topology needs from its system software: a switch's SSID and learning time, a host's wireless SSID, a router's RIP switch, the manual routes and the firewall of a router or home router. All 23 corpus files match Java. - A router's firewall is one object its constructor hangs in two places; the corpus writes it under the web server's "konfig" plug-in. The places are merged. - The decoder keeps entries a constructor made in a map, reached by get(key) (installed applications), like the elements of a list. - The Java oracle loads every file twice and writes values that differ as null: what FILIUS rolls (MACs, a switch's SSID) without text search. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,14 @@ import filius.hardware.knoten.InternetKnoten;
|
||||
import filius.hardware.knoten.Knoten;
|
||||
import filius.hardware.knoten.LokalerKnoten;
|
||||
import filius.rahmenprogramm.SzenarioVerwaltung;
|
||||
import filius.software.firewall.Firewall;
|
||||
import filius.software.firewall.FirewallRule;
|
||||
import filius.software.system.Betriebssystem;
|
||||
import filius.software.system.GatewayFirmware;
|
||||
import filius.software.system.InternetKnotenBetriebssystem;
|
||||
import filius.software.system.SwitchFirmware;
|
||||
import filius.software.system.SystemSoftware;
|
||||
import filius.software.system.VermittlungsrechnerBetriebssystem;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
@@ -27,12 +35,12 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
@@ -73,8 +81,17 @@ public class FlsOracle {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the file twice. What FILIUS does not find in a file it rolls at
|
||||
* random (MAC addresses, a switch's SSID) — such values differ between
|
||||
* the two loads and are written as null, so that two runs are byte-equal.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> read(Path file) throws Exception {
|
||||
return (Map<String, Object>) stable(load(file), load(file));
|
||||
}
|
||||
|
||||
static Map<String, Object> load(Path file) throws Exception {
|
||||
Path xml = Files.createTempFile("konfiguration", ".xml");
|
||||
try {
|
||||
extractConfiguration(file, xml);
|
||||
@@ -85,12 +102,27 @@ public class FlsOracle {
|
||||
List.class, List.class);
|
||||
load.setAccessible(true);
|
||||
boolean success = (Boolean) load.invoke(null, xml.toString(), nodes, cables, docs);
|
||||
return describe(success, nodes, cables, docs, Files.readString(xml, StandardCharsets.UTF_8));
|
||||
return describe(success, nodes, cables, docs);
|
||||
} finally {
|
||||
Files.deleteIfExists(xml);
|
||||
}
|
||||
}
|
||||
|
||||
/** The two descriptions with every value that differs between them replaced by null. */
|
||||
static Object stable(Object first, Object second) {
|
||||
if (first instanceof Map<?, ?> left && second instanceof Map<?, ?> right && left.keySet().equals(right.keySet())) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Object key : left.keySet()) result.put(key.toString(), stable(left.get(key), right.get(key)));
|
||||
return result;
|
||||
}
|
||||
if (first instanceof List<?> left && second instanceof List<?> right && left.size() == right.size()) {
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (int index = 0; index < left.size(); index++) result.add(stable(left.get(index), right.get(index)));
|
||||
return result;
|
||||
}
|
||||
return java.util.Objects.equals(first, second) ? first : null;
|
||||
}
|
||||
|
||||
static void extractConfiguration(Path archive, Path target) throws Exception {
|
||||
try (ZipInputStream zip = new ZipInputStream(Files.newInputStream(archive))) {
|
||||
for (ZipEntry entry; (entry = zip.getNextEntry()) != null;) {
|
||||
@@ -104,7 +136,7 @@ public class FlsOracle {
|
||||
}
|
||||
|
||||
static Map<String, Object> describe(boolean success, List<GUIKnotenItem> nodes, List<GUIKabelItem> cables,
|
||||
List<GUIDocuItem> docs, String xmlText) {
|
||||
List<GUIDocuItem> docs) {
|
||||
Map<Port, int[]> portOwners = new IdentityHashMap<>();
|
||||
List<Object> nodeList = new ArrayList<>();
|
||||
for (int nodeIndex = 0; nodeIndex < nodes.size(); nodeIndex++) {
|
||||
@@ -128,13 +160,8 @@ public class FlsOracle {
|
||||
Map<String, Object> port = new LinkedHashMap<>();
|
||||
port.put("ip", nic.getIp());
|
||||
port.put("mask", nic.getSubnetzMaske());
|
||||
// Without a MAC address in the file, FILIUS rolls a random one when it
|
||||
// creates the interface; only a MAC that stands in the file is stable.
|
||||
String mac = nic.getMac();
|
||||
// getMac() upper-cases; the file may not.
|
||||
boolean macInFile = mac != null && Pattern
|
||||
.compile(">" + Pattern.quote(mac) + "<", Pattern.CASE_INSENSITIVE).matcher(xmlText).find();
|
||||
port.put("mac", macInFile ? mac : null);
|
||||
// Random unless the file has one; see read().
|
||||
port.put("mac", nic.getMac());
|
||||
port.put("gateway", nic.getGateway());
|
||||
port.put("dns", nic.getDns());
|
||||
ports.add(port);
|
||||
@@ -148,6 +175,7 @@ public class FlsOracle {
|
||||
}
|
||||
}
|
||||
json.put("ports", ports);
|
||||
json.put("system", systemSoftware(node));
|
||||
nodeList.add(json);
|
||||
}
|
||||
|
||||
@@ -189,6 +217,45 @@ public class FlsOracle {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** The settings of a node's system software the topology layer reads. */
|
||||
static Map<String, Object> systemSoftware(Knoten node) {
|
||||
Map<String, Object> json = new LinkedHashMap<>();
|
||||
SystemSoftware software = node.getSystemSoftware();
|
||||
if (software instanceof SwitchFirmware firmware) {
|
||||
json.put("ssid", firmware.getSSID());
|
||||
json.put("retentionTime", firmware.getRetentionTime());
|
||||
}
|
||||
if (software instanceof Betriebssystem system) json.put("ssid", system.getSsid());
|
||||
if (software instanceof VermittlungsrechnerBetriebssystem system) json.put("ripEnabled", system.isRipEnabled());
|
||||
if (software instanceof InternetKnotenBetriebssystem system) {
|
||||
List<Object> routes = new ArrayList<>();
|
||||
if (system.getWeiterleitungstabelle() != null && system.getWeiterleitungstabelle().getManuelleTabelle() != null) {
|
||||
for (String[] route : system.getWeiterleitungstabelle().getManuelleTabelle()) {
|
||||
routes.add(route == null ? null : Arrays.asList(route));
|
||||
}
|
||||
}
|
||||
json.put("manualRoutes", routes);
|
||||
}
|
||||
Firewall firewall = software instanceof VermittlungsrechnerBetriebssystem system ? system.holeFirewall()
|
||||
: software instanceof GatewayFirmware firmware ? firmware.holeFirewall() : null;
|
||||
if (firewall != null) {
|
||||
Map<String, Object> settings = new LinkedHashMap<>();
|
||||
settings.put("activated", firewall.isActivated());
|
||||
settings.put("defaultPolicy", (int) firewall.getDefaultPolicy());
|
||||
settings.put("dropICMP", firewall.getDropICMP());
|
||||
settings.put("filterSYNSegmentsOnly", firewall.getFilterSYNSegmentsOnly());
|
||||
settings.put("filterUdp", firewall.getFilterUdp());
|
||||
List<Object> rules = new ArrayList<>();
|
||||
for (FirewallRule rule : firewall.getRuleset()) {
|
||||
rules.add(Arrays.asList(rule.srcIP, rule.srcMask, rule.destIP, rule.destMask, rule.port,
|
||||
(int) rule.protocol, (int) rule.action));
|
||||
}
|
||||
settings.put("rules", rules);
|
||||
json.put("firewall", settings);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/** Just enough JSON: maps, lists, strings, numbers, booleans, null. */
|
||||
static final class Json {
|
||||
static String write(Object value, String indent) {
|
||||
|
||||
Reference in New Issue
Block a user