feat(fls): import switch, wireless, routes, RIP and firewall settings

importProject carries the system software settings into the model the
core runs: switch SSID and learning time (an SSID Java would roll comes
from the node id, as in Swift), a host's wireless network, manual routes,
RIP and the firewall of every router and home router. The Swift oracle
writes these settings too; they match on the 13 files Swift reads
completely.

A router's default gateway — its first interface's, which Java routes to
(Weiterleitungstabelle.holeTabelle) — is imported into the router's
device configuration. Swift's model drops it; in internetwork_komplett
the way back from 141.99.5.10 depends on it. A test pings across three
routers in the core, with and without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 21:00:42 +00:00
co-authored by Claude Opus 5.5
parent 9a389810f9
commit ef3bb94cb9
6 changed files with 1277 additions and 206 deletions
@@ -323,6 +323,7 @@ func describeImportedState(_ state: TopologyEditorState) -> JSON {
},
"device": device,
"interfaces": interfaces,
"system": describeSystem(state, node),
]
}
let links: [[Int]] = state.graph.links.map { link in
@@ -347,6 +348,40 @@ func describeImportedState(_ state: TopologyEditorState) -> JSON {
return ["nodes": nodes, "links": links, "documentation": documentation]
}
/// The system software settings of a node in the imported state. A switch
/// SSID Swift derives from the (random) node id is written as a marker.
func describeSystem(_ state: TopologyEditorState, _ node: TopologyNode) -> JSON {
var json: JSON = [:]
if let configuration = state.switchConfigurationsByNodeID[node.id] {
let derived = TopologySwitchConfiguration.defaultConfiguration(nodeID: node.id).ssid
json["switch"] = [
"ssid": configuration.ssid == derived ? "<from node id>" : configuration.ssid,
"retentionTimeMilliseconds": Int(configuration.retentionTimeMilliseconds),
] as JSON
}
if let wireless = state.hostWirelessConfigurationsByNodeID[node.id] {
json["hostWireless"] = ["isEnabled": wireless.isEnabled, "ssid": wireless.ssid] as JSON
}
if let routes = state.runtimeManualRoutesByNodeID[node.id] {
json["manualRoutes"] = routes.map { [$0.destinationNetwork, $0.subnetMask, $0.gateway, $0.interfaceIPAddress] }
}
if let rip = state.runtimeRIPEnabledByNodeID[node.id] { json["ripEnabled"] = rip }
if let firewall = state.runtimeFirewallConfigurationsByNodeID[node.id] {
json["firewall"] = [
"isActive": firewall.isActive,
"defaultPolicy": firewall.defaultPolicy.rawValue,
"dropICMP": firewall.dropICMP,
"filterSYNSegmentsOnly": firewall.filterSYNSegmentsOnly,
"filterUDP": firewall.filterUDP,
"rules": firewall.rules.map { rule -> [Any] in
[rule.sourceIPAddress, rule.sourceSubnetMask, rule.destinationIPAddress, rule.destinationSubnetMask,
rule.port, rule.protocolType.rawValue, rule.action.rawValue]
},
] as JSON
}
return json
}
// MARK: - Archive path folding
struct PathCase: Decodable { let name: String; let first: String; let second: String }