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:
@@ -17,6 +17,7 @@ export * from './runtime/speed.js';
|
||||
export * from './runtime/trace.js';
|
||||
export * from './runtime/capture.js';
|
||||
export * from './runtime/captureRows.js';
|
||||
export * from './runtime/viewers.js';
|
||||
export * from './runtime/threads.js';
|
||||
export * from './runtime/apps/program.js';
|
||||
export * from './runtime/apps/installation.js';
|
||||
|
||||
@@ -68,6 +68,11 @@ class CaptureList {
|
||||
forgetIdentities(): void {
|
||||
this.identities.clear();
|
||||
}
|
||||
|
||||
/** Every frame forgotten, the numbers going on (the dropped frames' list, which keeps no identities). */
|
||||
clear(): void {
|
||||
this.frames.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketCapture {
|
||||
@@ -107,6 +112,11 @@ export class PacketCapture {
|
||||
return this.dropped.frames;
|
||||
}
|
||||
|
||||
/** The dropped frames forgotten (SimulationControl begins its list anew), their numbers going on. */
|
||||
clearDropped(): void {
|
||||
this.dropped.clear();
|
||||
}
|
||||
|
||||
/** The simulation starts again: the lists stay, frames of the new run are new. */
|
||||
beginRun(): void {
|
||||
for (const list of this.listsByPortId.values()) list.forgetIdentities();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// for an unknown transport protocol (all empty) does not occur.
|
||||
|
||||
import type { CapturedFrame } from './capture.js';
|
||||
import { IPV4_PROTOCOL, TCP_FLAG, type ArpPacket, type IcmpMessage, type IPv4Packet } from './packets.js';
|
||||
import { IPV4_PROTOCOL, TCP_FLAG, type ArpPacket, type EthernetFrame, type IcmpMessage, type IPv4Packet } from './packets.js';
|
||||
|
||||
/** Java's four layers (Lauscher.PROTOKOLL_SCHICHTEN): network access, internet, transport, application. */
|
||||
export type CaptureLayer = 'networkAccess' | 'internet' | 'transport' | 'application';
|
||||
@@ -182,6 +182,17 @@ function packetText(packet: IPv4Packet): string {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* EthernetFrame.toString: addresses, Java's type (0x800 for IP, 0x806 for
|
||||
* ARP) and what the frame carries — an IP packet without its segment, an
|
||||
* ICMP packet whole, an ARP packet. SimulationControl lists dropped frames so.
|
||||
*/
|
||||
export function javaFrameText(frame: EthernetFrame): string {
|
||||
const payload = frame.payload;
|
||||
const data = payload.kind === 'arp' ? arpText(payload.packet) : packetText(payload.packet);
|
||||
return `[src=${frame.sourceMacAddress}, dest=${frame.destinationMacAddress}, type=${payload.kind === 'arp' ? '0x806' : '0x800'} | ${data}]`;
|
||||
}
|
||||
|
||||
/** Java's String.trim: cuts characters up to U+0020 at both ends. */
|
||||
function javaTrim(text: string): string {
|
||||
let start = 0;
|
||||
|
||||
@@ -276,6 +276,8 @@ export interface SwitchSatEntry {
|
||||
readonly macAddress: string;
|
||||
readonly portId: Id;
|
||||
readonly portLabel: string;
|
||||
/** The port's place among the switch's, from 1 (SwitchFirmware.holeSAT: "Port <n>"). */
|
||||
readonly portNumber: number;
|
||||
readonly updatedAtMilliseconds: number;
|
||||
}
|
||||
|
||||
@@ -4060,6 +4062,12 @@ export class NetworkRuntimeEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/** GatewayFirmware.loescheNAT (the NAT viewer's "reset"): the dynamic entries go, the port forwarding's stay. */
|
||||
clearDynamicNatEntries(nodeId: Id): void {
|
||||
const table = this.stateValue.natTablesByNodeId.get(nodeId);
|
||||
if (table !== undefined) this.stateValue.natTablesByNodeId.set(nodeId, table.filter((entry) => entry.type === 'static'));
|
||||
}
|
||||
|
||||
/** The home router's NAT table as it stands: static entries, then the dynamic ones that have not expired. */
|
||||
natEntries(nodeId: Id): readonly NatEntry[] {
|
||||
this.expireNatEntries(nodeId);
|
||||
@@ -4729,6 +4737,7 @@ export class NetworkRuntimeEngine {
|
||||
const node = this.nodeSnapshot(nodeId);
|
||||
if (node === undefined || node.kind !== 'networkSwitch') return [];
|
||||
const labelsByPortId = new Map(node.ports.map((port) => [port.id, port.label]));
|
||||
const numbersByPortId = new Map(node.ports.map((port, index) => [port.id, index + 1]));
|
||||
const updatedAtByMac = this.stateValue.switchForwardingUpdatedAtMillisecondsByNodeId.get(nodeId) ?? new Map();
|
||||
const table = this.stateValue.switchForwardingTablesByNodeId.get(nodeId) ?? new Map<string, Id>();
|
||||
return [...table]
|
||||
@@ -4736,6 +4745,7 @@ export class NetworkRuntimeEngine {
|
||||
macAddress,
|
||||
portId,
|
||||
portLabel: labelsByPortId.get(portId) ?? '?',
|
||||
portNumber: numbersByPortId.get(portId) ?? 0,
|
||||
updatedAtMilliseconds: updatedAtByMac.get(macAddress) ?? 0,
|
||||
}))
|
||||
.sort((lhs, rhs) =>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// The rows of FILIUS' viewers of a running simulation, as Java writes them:
|
||||
// a home router's NAT table (GatewayFirmware.holeNAT, createEintrag, shown by
|
||||
// NatViewer). A switch's table (SwitchFirmware.holeSAT) is the engine's
|
||||
// switchSatEntries with the port's number; the time of an entry is the
|
||||
// viewer's to write, in its clock.
|
||||
|
||||
import type { NatEntry } from './engine.js';
|
||||
|
||||
/** A NAT entry's cells: protocol, WAN port, remote address, LAN address, LAN port; and its last update, none for the port forwarding's. */
|
||||
export interface NatViewRow {
|
||||
readonly cells: readonly [string, string, string, string, string];
|
||||
readonly updatedAtMilliseconds?: number;
|
||||
}
|
||||
|
||||
const PROTOCOL_NAMES: Readonly<Record<NatEntry['protocol'], string>> = { tcp: 'TCP', udp: 'UDP', icmp: 'ICMP' };
|
||||
|
||||
/** createEintrag: an ICMP entry's "port" is its identifier ("0 (Identifier: 1)"); the port forwarding's remote end is "*". */
|
||||
export function natViewRow(entry: NatEntry): NatViewRow {
|
||||
const port = (value: number) => (entry.protocol === 'icmp' ? `0 (Identifier: ${value})` : String(value));
|
||||
const cells = [PROTOCOL_NAMES[entry.protocol], port(entry.port), entry.type === 'static' ? '*' : entry.remoteIPAddress, entry.lan.ipAddress, port(entry.lan.port)] as const;
|
||||
return entry.type === 'static' ? { cells } : { cells, updatedAtMilliseconds: entry.updatedAtMilliseconds };
|
||||
}
|
||||
Reference in New Issue
Block a user