test(core): Swift oracle — the filius-ipad core built on Linux as the reference

tools/swift-oracle builds the unchanged Swift runtime (pinned 3b5ded0)
in Docker and replays JSON scenarios; the TypeScript engine must produce
the same trace events, results and end state. Five scenarios, 884
events, identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 19:33:14 +00:00
co-authored by Claude Opus 5.5
parent 696334aa5a
commit 5c16cf18b4
27 changed files with 27394 additions and 4 deletions
+299
View File
@@ -0,0 +1,299 @@
// Replays every scenario in test/scenarios with the TypeScript engine and
// compares the result with what the filius-ipad Swift engine produced for the
// same file (<name>.swift.json, written by tools/swift-oracle/run.sh).
// Every trace event must match: identity, time, node, headers, detail.
import { readdirSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import {
deviceConfiguration,
firewallConfiguration,
firewallRule,
interfaceKey,
NetworkRuntimeEngine,
portSnapshot,
switchConfiguration,
topologySnapshot,
type FirewallConfiguration,
type IPv4DeliveryResult,
type IcmpObservation,
type ManualRoute,
type NodeKind,
type TopologySnapshot,
type TraceEvent,
} from '../src/index.js';
const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios');
interface Scenario {
seed: number;
speedPercent?: number;
nodes: { id: string; kind: NodeKind; ports: { id: string; label: string; mac?: string }[] }[];
links: { id: string; sourceNode: string; sourcePort: string; targetNode: string; targetPort: string }[];
devices?: Record<string, { ip: string; mask: string; gateway?: string; dns?: string }>;
interfaces?: { node: string; port: string; ip: string; mask: string }[];
routes?: Record<string, { destination: string; mask: string; gateway: string; interface: string }[]>;
switches?: Record<string, { ssid: string; retention?: number }>;
firewalls?: Record<
string,
{
active?: boolean;
defaultPolicy?: 'accept' | 'drop';
dropIcmp?: boolean;
synOnly?: boolean;
filterUdp?: boolean;
rules?: {
source?: string;
sourceMask?: string;
destination?: string;
destinationMask?: string;
port?: number;
protocol?: 'all' | 'icmp' | 'tcp' | 'udp';
action?: 'accept' | 'drop';
}[];
}
>;
steps: {
op: string;
from?: string;
node?: string;
target?: string;
ttl?: number;
identifier?: number;
sequence?: number;
maxTtl?: number;
to?: number;
enabled?: boolean;
percent?: number;
}[];
}
function snapshotOf(scenario: Scenario): TopologySnapshot {
const firewalls = new Map<string, FirewallConfiguration>();
for (const [node, config] of Object.entries(scenario.firewalls ?? {})) {
firewalls.set(
node,
firewallConfiguration({
isActive: config.active ?? false,
defaultPolicy: config.defaultPolicy ?? 'drop',
dropIcmp: config.dropIcmp ?? false,
filterSynSegmentsOnly: config.synOnly ?? true,
filterUdp: config.filterUdp ?? true,
rules: (config.rules ?? []).map((rule) =>
firewallRule({
sourceIPAddress: rule.source ?? '',
sourceSubnetMask: rule.sourceMask ?? '',
destinationIPAddress: rule.destination ?? '',
destinationSubnetMask: rule.destinationMask ?? '',
...(rule.port === undefined ? {} : { port: rule.port }),
protocol: rule.protocol ?? 'tcp',
action: rule.action ?? 'drop',
}),
),
}),
);
}
return topologySnapshot({
nodes: scenario.nodes.map((node) => ({
id: node.id,
kind: node.kind,
ports: node.ports.map((port) => portSnapshot(port.id, port.label, port.mac)),
})),
links: scenario.links.map((link) => ({
id: link.id,
sourceNodeId: link.sourceNode,
sourcePortId: link.sourcePort,
targetNodeId: link.targetNode,
targetPortId: link.targetPort,
})),
deviceConfigurations: new Map(
Object.entries(scenario.devices ?? {}).map(([node, device]) => [
node,
deviceConfiguration(device.ip, device.mask, device.gateway ?? '', device.dns ?? ''),
]),
),
interfaceConfigurations: new Map(
(scenario.interfaces ?? []).map((item) => [
interfaceKey(item.node, item.port),
{ ipAddress: item.ip, subnetMask: item.mask },
]),
),
manualRoutesByNodeId: new Map(
Object.entries(scenario.routes ?? {}).map(([node, rows]) => [
node,
rows.map(
(row): ManualRoute => ({
destinationNetwork: row.destination,
subnetMask: row.mask,
gateway: row.gateway,
interfaceIPAddress: row.interface,
}),
),
]),
),
switchConfigurationsByNodeId: new Map(
Object.entries(scenario.switches ?? {}).map(([node, config]) => [
node,
config.retention === undefined ? switchConfiguration(config.ssid) : switchConfiguration(config.ssid, config.retention),
]),
),
firewallConfigurationsByNodeId: firewalls,
});
}
const upper = (id: string) => id.toUpperCase();
function traceJson(event: TraceEvent): Record<string, unknown> {
return {
id: event.id,
time: event.timeMilliseconds,
node: upper(event.nodeId),
direction: event.direction,
layer: event.layer,
operation: event.operation,
before: event.beforeHeaders.map((field) => [field.name, field.value]),
after: event.afterHeaders.map((field) => [field.name, field.value]),
...(event.frameIdentity === undefined ? {} : { frame: event.frameIdentity }),
...(event.packetIdentity === undefined ? {} : { packet: event.packetIdentity }),
...(event.interfaceId === undefined ? {} : { interface: upper(event.interfaceId) }),
...(event.detail === undefined ? {} : { detail: event.detail }),
};
}
function deliveryJson(result: IPv4DeliveryResult): Record<string, unknown> {
switch (result.type) {
case 'delivered':
return { type: 'delivered', packet: result.packetIdentity, node: upper(result.nodeId) };
case 'icmpError':
return { type: 'icmpError', packet: result.packetIdentity, node: upper(result.nodeId), kind: result.kind };
case 'dropped':
return {
type: 'dropped',
packet: result.packetIdentity,
...(result.nodeId === undefined ? {} : { node: upper(result.nodeId) }),
};
}
}
function observationJson(item: IcmpObservation): Record<string, unknown> {
return {
kind: item.message.kind,
identifier: item.message.identifier,
sequence: item.message.sequenceNumber,
sender: item.packet.senderIPAddress,
};
}
function run(scenario: Scenario): Record<string, unknown> {
const engine = new NetworkRuntimeEngine(scenario.seed);
if (scenario.speedPercent !== undefined) engine.setSimulationSpeed(scenario.speedPercent);
engine.handle({ type: 'start', snapshot: snapshotOf(scenario), seed: scenario.seed, initialTimeMilliseconds: 0 });
const results: unknown[] = [];
for (const step of scenario.steps) {
switch (step.op) {
case 'ping':
results.push(
deliveryJson(
engine.sendIcmpEcho(step.from!, step.target!, {
timeToLive: step.ttl ?? 64,
identifier: step.identifier ?? 1,
sequenceNumber: step.sequence ?? 1,
}),
),
);
break;
case 'traceroute':
results.push(
engine
.traceIcmpEcho(step.from!, step.target!, {
maximumTimeToLive: step.maxTtl ?? 64,
identifier: step.identifier ?? 1,
})
.map(observationJson),
);
break;
case 'resolveMac':
results.push(engine.resolveMacAddress(step.node!, step.target!) ?? null);
break;
case 'advance':
engine.handle({ type: 'advance', toMilliseconds: step.to! });
results.push(engine.state.currentTimeMilliseconds);
break;
case 'packetLoss':
engine.setGlobalPacketLossEnabled(step.enabled!);
results.push(engine.state.globalPacketLossEnabled);
break;
case 'sat':
results.push(
engine.switchSatEntries(step.node!).map((entry) => ({
mac: entry.macAddress,
port: entry.portLabel,
updated: entry.updatedAtMilliseconds,
})),
);
break;
case 'speed':
engine.setSimulationSpeed(step.percent!);
results.push(engine.simulationSpeed.percent);
break;
default:
throw new Error(`unknown step ${step.op}`);
}
}
const state = engine.state;
const mapOf = <V>(map: Map<string, V>, convert: (value: V) => unknown) =>
Object.fromEntries([...map].map(([key, value]) => [upper(key), convert(value)]));
return {
results,
traces: state.packetTraces.map(traceJson),
time: state.currentTimeMilliseconds,
pendingEvents: state.pendingEvents.length,
arp: mapOf(state.arpCachesByNodeId, (cache) =>
Object.fromEntries([...cache].map(([ip, entry]) => [ip, entry.macAddress])),
),
switchTables: mapOf(state.switchForwardingTablesByNodeId, (table) =>
Object.fromEntries([...table].map(([mac, port]) => [mac, upper(port)])),
),
icmpObservations: mapOf(state.icmpObservationsByNodeId, (items) => items.map(observationJson)),
firewallDecisions: state.firewallDecisions.map((decision) => ({
packet: decision.packetIdentity,
node: upper(decision.nodeId),
accepted: decision.accepted,
...(decision.ruleIndex === undefined ? {} : { rule: decision.ruleIndex }),
})),
};
}
const scenarioNames = readdirSync(scenarioDirectory)
.filter((name) => name.endsWith('.scenario.json'))
.map((name) => name.replace('.scenario.json', ''))
.sort();
describe('same result as the Swift engine', () => {
it('has scenarios with Swift results', () => {
expect(scenarioNames.length).toBeGreaterThan(0);
});
for (const name of scenarioNames) {
it(name, () => {
const scenario = JSON.parse(readFileSync(join(scenarioDirectory, `${name}.scenario.json`), 'utf8')) as Scenario;
const expected = JSON.parse(readFileSync(join(scenarioDirectory, `${name}.swift.json`), 'utf8')) as Record<
string,
unknown
>;
const actual = run(scenario);
// Traces first and one by one: the first difference is the one that matters.
const expectedTraces = expected.traces as unknown[];
const actualTraces = actual.traces as unknown[];
for (let index = 0; index < Math.max(expectedTraces.length, actualTraces.length); index += 1) {
expect(actualTraces[index], `trace event #${index + 1}`).toEqual(expectedTraces[index]);
}
expect(actual).toEqual(expected);
});
}
});
@@ -0,0 +1,194 @@
{
"seed": 18,
"nodes": [
{
"id": "00000000-0000-0000-0000-000000000401",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000411",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000402",
"kind": "router",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000421",
"label": "rt1"
},
{
"id": "00000000-0000-0000-0000-000000000422",
"label": "rt2"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000403",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000413",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000404",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000414",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000405",
"kind": "networkSwitch",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000451",
"label": "sw1"
},
{
"id": "00000000-0000-0000-0000-000000000452",
"label": "sw2"
},
{
"id": "00000000-0000-0000-0000-000000000453",
"label": "sw3"
}
]
}
],
"links": [
{
"id": "00000000-0000-0000-0000-00000000F00A",
"sourceNode": "00000000-0000-0000-0000-000000000401",
"sourcePort": "00000000-0000-0000-0000-000000000411",
"targetNode": "00000000-0000-0000-0000-000000000405",
"targetPort": "00000000-0000-0000-0000-000000000451"
},
{
"id": "00000000-0000-0000-0000-00000000F00B",
"sourceNode": "00000000-0000-0000-0000-000000000404",
"sourcePort": "00000000-0000-0000-0000-000000000414",
"targetNode": "00000000-0000-0000-0000-000000000405",
"targetPort": "00000000-0000-0000-0000-000000000452"
},
{
"id": "00000000-0000-0000-0000-00000000F00C",
"sourceNode": "00000000-0000-0000-0000-000000000405",
"sourcePort": "00000000-0000-0000-0000-000000000453",
"targetNode": "00000000-0000-0000-0000-000000000402",
"targetPort": "00000000-0000-0000-0000-000000000421"
},
{
"id": "00000000-0000-0000-0000-00000000F00D",
"sourceNode": "00000000-0000-0000-0000-000000000402",
"sourcePort": "00000000-0000-0000-0000-000000000422",
"targetNode": "00000000-0000-0000-0000-000000000403",
"targetPort": "00000000-0000-0000-0000-000000000413"
}
],
"devices": {
"00000000-0000-0000-0000-000000000401": {
"ip": "10.1.0.2",
"mask": "255.255.255.0",
"gateway": "10.1.0.1"
},
"00000000-0000-0000-0000-000000000402": {
"ip": "10.1.0.1",
"mask": "255.255.255.0"
},
"00000000-0000-0000-0000-000000000403": {
"ip": "10.2.0.2",
"mask": "255.255.255.0",
"gateway": "10.2.0.1"
},
"00000000-0000-0000-0000-000000000404": {
"ip": "10.1.0.3",
"mask": "255.255.255.0",
"gateway": "10.1.0.1"
}
},
"interfaces": [
{
"node": "00000000-0000-0000-0000-000000000402",
"port": "00000000-0000-0000-0000-000000000421",
"ip": "10.1.0.1",
"mask": "255.255.255.0"
},
{
"node": "00000000-0000-0000-0000-000000000402",
"port": "00000000-0000-0000-0000-000000000422",
"ip": "10.2.0.1",
"mask": "255.255.255.0"
}
],
"firewalls": {
"00000000-0000-0000-0000-000000000402": {
"active": true,
"defaultPolicy": "accept",
"dropIcmp": true
},
"00000000-0000-0000-0000-000000000404": {
"active": true,
"defaultPolicy": "drop",
"dropIcmp": false
},
"00000000-0000-0000-0000-000000000401": {
"active": true,
"defaultPolicy": "drop",
"dropIcmp": true
}
},
"steps": [
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000401",
"target": "10.2.0.2",
"identifier": 30,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000401",
"target": "10.1.0.3",
"identifier": 31,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000404",
"target": "10.1.0.2",
"identifier": 32,
"sequence": 1
},
{
"op": "packetLoss",
"enabled": true
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000404",
"target": "10.1.0.1",
"identifier": 33,
"sequence": 1
},
{
"op": "packetLoss",
"enabled": false
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000404",
"target": "10.1.0.1",
"identifier": 34,
"sequence": 1
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,134 @@
{
"seed": 14,
"nodes": [
{
"id": "00000000-0000-0000-0000-000000000201",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000211",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000202",
"kind": "router",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000221",
"label": "rt1"
},
{
"id": "00000000-0000-0000-0000-000000000222",
"label": "rt2"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000203",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000213",
"label": "eth0"
}
]
}
],
"links": [
{
"id": "00000000-0000-0000-0000-00000000F004",
"sourceNode": "00000000-0000-0000-0000-000000000201",
"sourcePort": "00000000-0000-0000-0000-000000000211",
"targetNode": "00000000-0000-0000-0000-000000000202",
"targetPort": "00000000-0000-0000-0000-000000000221"
},
{
"id": "00000000-0000-0000-0000-00000000F005",
"sourceNode": "00000000-0000-0000-0000-000000000202",
"sourcePort": "00000000-0000-0000-0000-000000000222",
"targetNode": "00000000-0000-0000-0000-000000000203",
"targetPort": "00000000-0000-0000-0000-000000000213"
}
],
"devices": {
"00000000-0000-0000-0000-000000000201": {
"ip": "10.0.0.2",
"mask": "255.255.255.0",
"gateway": "10.0.0.1"
},
"00000000-0000-0000-0000-000000000202": {
"ip": "10.0.0.1",
"mask": "255.255.255.0"
},
"00000000-0000-0000-0000-000000000203": {
"ip": "20.0.0.2",
"mask": "255.255.255.0",
"gateway": "20.0.0.1"
}
},
"interfaces": [
{
"node": "00000000-0000-0000-0000-000000000202",
"port": "00000000-0000-0000-0000-000000000221",
"ip": "10.0.0.1",
"mask": "255.255.255.0"
},
{
"node": "00000000-0000-0000-0000-000000000202",
"port": "00000000-0000-0000-0000-000000000222",
"ip": "20.0.0.1",
"mask": "255.255.255.0"
}
],
"steps": [
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000201",
"target": "20.0.0.2",
"identifier": 10,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000201",
"target": "20.0.0.2",
"ttl": 1,
"identifier": 11,
"sequence": 1
},
{
"op": "traceroute",
"from": "00000000-0000-0000-0000-000000000201",
"target": "20.0.0.2",
"identifier": 12
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000201",
"target": "30.0.0.2",
"identifier": 13,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000201",
"target": "20.0.0.99",
"identifier": 14,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000203",
"target": "10.0.0.1",
"identifier": 15,
"sequence": 1
},
{
"op": "resolveMac",
"node": "00000000-0000-0000-0000-000000000203",
"target": "20.0.0.1"
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,195 @@
{
"seed": 3,
"speedPercent": 50,
"nodes": [
{
"id": "00000000-0000-0000-0000-000000000301",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000311",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000302",
"kind": "router",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000321",
"label": "rt1"
},
{
"id": "00000000-0000-0000-0000-000000000322",
"label": "rt2"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000303",
"kind": "router",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000331",
"label": "rt1"
},
{
"id": "00000000-0000-0000-0000-000000000332",
"label": "rt2"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000304",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000314",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000305",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000315",
"label": "eth0"
}
]
}
],
"links": [
{
"id": "00000000-0000-0000-0000-00000000F006",
"sourceNode": "00000000-0000-0000-0000-000000000301",
"sourcePort": "00000000-0000-0000-0000-000000000311",
"targetNode": "00000000-0000-0000-0000-000000000302",
"targetPort": "00000000-0000-0000-0000-000000000321"
},
{
"id": "00000000-0000-0000-0000-00000000F007",
"sourceNode": "00000000-0000-0000-0000-000000000302",
"sourcePort": "00000000-0000-0000-0000-000000000322",
"targetNode": "00000000-0000-0000-0000-000000000303",
"targetPort": "00000000-0000-0000-0000-000000000331"
},
{
"id": "00000000-0000-0000-0000-00000000F008",
"sourceNode": "00000000-0000-0000-0000-000000000303",
"sourcePort": "00000000-0000-0000-0000-000000000332",
"targetNode": "00000000-0000-0000-0000-000000000304",
"targetPort": "00000000-0000-0000-0000-000000000314"
},
{
"id": "00000000-0000-0000-0000-00000000F009",
"sourceNode": "00000000-0000-0000-0000-000000000305",
"sourcePort": "00000000-0000-0000-0000-000000000315",
"targetNode": "00000000-0000-0000-0000-000000000303",
"targetPort": "00000000-0000-0000-0000-000000000332"
}
],
"devices": {
"00000000-0000-0000-0000-000000000301": {
"ip": "192.168.1.10",
"mask": "255.255.255.0",
"gateway": "192.168.1.1"
},
"00000000-0000-0000-0000-000000000302": {
"ip": "192.168.1.1",
"mask": "255.255.255.0"
},
"00000000-0000-0000-0000-000000000303": {
"ip": "10.10.10.2",
"mask": "255.255.255.0",
"gateway": "10.10.10.1"
},
"00000000-0000-0000-0000-000000000304": {
"ip": "172.16.0.10",
"mask": "255.255.0.0",
"gateway": "172.16.0.1"
},
"00000000-0000-0000-0000-000000000305": {
"ip": "172.16.0.20",
"mask": "255.255.0.0"
}
},
"interfaces": [
{
"node": "00000000-0000-0000-0000-000000000302",
"port": "00000000-0000-0000-0000-000000000321",
"ip": "192.168.1.1",
"mask": "255.255.255.0"
},
{
"node": "00000000-0000-0000-0000-000000000302",
"port": "00000000-0000-0000-0000-000000000322",
"ip": "10.10.10.1",
"mask": "255.255.255.252"
},
{
"node": "00000000-0000-0000-0000-000000000303",
"port": "00000000-0000-0000-0000-000000000331",
"ip": "10.10.10.2",
"mask": "255.255.255.252"
},
{
"node": "00000000-0000-0000-0000-000000000303",
"port": "00000000-0000-0000-0000-000000000332",
"ip": "172.16.0.1",
"mask": "255.255.0.0"
}
],
"routes": {
"00000000-0000-0000-0000-000000000302": [
{
"destination": "172.16.0.0",
"mask": "255.255.0.0",
"gateway": "10.10.10.2",
"interface": "10.10.10.1"
}
]
},
"steps": [
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000301",
"target": "172.16.0.10",
"identifier": 20,
"sequence": 1
},
{
"op": "traceroute",
"from": "00000000-0000-0000-0000-000000000301",
"target": "172.16.0.10",
"identifier": 21
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000305",
"target": "192.168.1.10",
"identifier": 22,
"sequence": 1
},
{
"op": "speed",
"percent": 100
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000301",
"target": "172.16.0.10",
"identifier": 23,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000301",
"target": "10.10.10.2",
"identifier": 24,
"sequence": 1
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,568 @@
{
"seed": 5,
"nodes": [
{
"id": "00000000-0000-0000-0000-000000000501",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000511",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000502",
"kind": "notebook",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000512",
"label": "eth0",
"mac": "0a:1b:2c:3d:4e:5f"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000503",
"kind": "networkSwitch",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000521",
"label": "sw1"
},
{
"id": "00000000-0000-0000-0000-000000000522",
"label": "sw2"
}
]
}
],
"links": [
{
"id": "00000000-0000-0000-0000-00000000F501",
"sourceNode": "00000000-0000-0000-0000-000000000501",
"sourcePort": "00000000-0000-0000-0000-000000000511",
"targetNode": "00000000-0000-0000-0000-000000000503",
"targetPort": "00000000-0000-0000-0000-000000000521"
},
{
"id": "00000000-0000-0000-0000-00000000F502",
"sourceNode": "00000000-0000-0000-0000-000000000503",
"sourcePort": "00000000-0000-0000-0000-000000000522",
"targetNode": "00000000-0000-0000-0000-000000000502",
"targetPort": "00000000-0000-0000-0000-000000000512"
}
],
"devices": {
"00000000-0000-0000-0000-000000000501": {
"ip": "10.5.0.1",
"mask": "255.255.255.0"
},
"00000000-0000-0000-0000-000000000502": {
"ip": "10.5.0.2",
"mask": "255.255.255.0"
}
},
"switches": {
"00000000-0000-0000-0000-000000000503": {
"ssid": "age",
"retention": 1000
}
},
"steps": [
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000501",
"target": "10.5.0.2",
"identifier": 50,
"sequence": 1
},
{
"op": "advance",
"to": 1000
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1005
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1010
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1015
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1020
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1025
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1030
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1035
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1040
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1045
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1050
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1055
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1060
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1065
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1070
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1075
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1080
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1085
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1090
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1095
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1100
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1105
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1110
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1115
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1120
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1125
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1130
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1135
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1140
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1145
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1150
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1155
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1160
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1165
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1170
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1175
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1180
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1185
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1190
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1195
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1200
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1205
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1210
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1215
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1220
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1225
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1230
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1235
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1240
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1245
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1250
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1255
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1260
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1265
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1270
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1275
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1280
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1285
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1290
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "advance",
"to": 1295
},
{
"op": "sat",
"node": "00000000-0000-0000-0000-000000000503"
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000502",
"target": "10.5.0.1",
"identifier": 51,
"sequence": 1
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,155 @@
{
"seed": 14,
"nodes": [
{
"id": "00000000-0000-0000-0000-000000000101",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000111",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000102",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000112",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000103",
"kind": "pc",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000113",
"label": "eth0"
}
]
},
{
"id": "00000000-0000-0000-0000-000000000104",
"kind": "networkSwitch",
"ports": [
{
"id": "00000000-0000-0000-0000-000000000121",
"label": "sw1"
},
{
"id": "00000000-0000-0000-0000-000000000122",
"label": "sw2"
},
{
"id": "00000000-0000-0000-0000-000000000123",
"label": "sw3"
},
{
"id": "00000000-0000-0000-0000-000000000124",
"label": "sw4"
}
]
}
],
"links": [
{
"id": "00000000-0000-0000-0000-00000000F001",
"sourceNode": "00000000-0000-0000-0000-000000000101",
"sourcePort": "00000000-0000-0000-0000-000000000111",
"targetNode": "00000000-0000-0000-0000-000000000104",
"targetPort": "00000000-0000-0000-0000-000000000121"
},
{
"id": "00000000-0000-0000-0000-00000000F002",
"sourceNode": "00000000-0000-0000-0000-000000000102",
"sourcePort": "00000000-0000-0000-0000-000000000112",
"targetNode": "00000000-0000-0000-0000-000000000104",
"targetPort": "00000000-0000-0000-0000-000000000122"
},
{
"id": "00000000-0000-0000-0000-00000000F003",
"sourceNode": "00000000-0000-0000-0000-000000000103",
"sourcePort": "00000000-0000-0000-0000-000000000113",
"targetNode": "00000000-0000-0000-0000-000000000104",
"targetPort": "00000000-0000-0000-0000-000000000123"
}
],
"devices": {
"00000000-0000-0000-0000-000000000101": {
"ip": "192.168.0.10",
"mask": "255.255.255.0"
},
"00000000-0000-0000-0000-000000000102": {
"ip": "192.168.0.20",
"mask": "255.255.255.0"
},
"00000000-0000-0000-0000-000000000103": {
"ip": "192.168.0.30",
"mask": "255.255.255.0"
}
},
"switches": {
"00000000-0000-0000-0000-000000000104": {
"ssid": "lab",
"retention": 5000
}
},
"steps": [
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000101",
"target": "192.168.0.20",
"identifier": 1,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000101",
"target": "192.168.0.20",
"identifier": 1,
"sequence": 2
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000103",
"target": "192.168.0.10",
"identifier": 7,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000102",
"target": "192.168.0.99",
"identifier": 9,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000101",
"target": "192.168.0.10",
"identifier": 2,
"sequence": 1
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000101",
"target": "127.0.0.1",
"identifier": 3,
"sequence": 1
},
{
"op": "advance",
"to": 20000
},
{
"op": "ping",
"from": "00000000-0000-0000-0000-000000000102",
"target": "192.168.0.30",
"identifier": 4,
"sequence": 1
}
]
}
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"noEmit": true,
"composite": false,
"declaration": false,
"types": ["node"]
},
"include": ["."]
}