First slice of the TypeScript port of the filius-ipad runtime engine, with 29 tests ported from the Swift suite. Parts not ported yet throw NotPortedError instead of guessing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
3.4 KiB
TypeScript
125 lines
3.4 KiB
TypeScript
// Ported from filius-ipad (GPLv2/GPLv3):
|
|
// State/TopologyNetworkRuntimeEngine.swift ("Java-observable packet and route models")
|
|
// Frames and packets are typed values, not bytes — what the Java packet viewer
|
|
// shows, not what a real wire would carry.
|
|
|
|
export interface EthernetFrame {
|
|
readonly identity: number;
|
|
readonly sourceMacAddress: string;
|
|
readonly destinationMacAddress: string;
|
|
readonly payload: EthernetPayload;
|
|
}
|
|
|
|
export type EthernetPayload =
|
|
| { readonly kind: 'arp'; readonly packet: ArpPacket }
|
|
| { readonly kind: 'ipv4'; readonly packet: IPv4Packet };
|
|
|
|
export type ArpOperation = 'request' | 'reply';
|
|
|
|
export interface ArpPacket {
|
|
readonly operation: ArpOperation;
|
|
readonly senderMacAddress: string;
|
|
readonly senderIPAddress: string;
|
|
readonly targetMacAddress: string;
|
|
readonly targetIPAddress: string;
|
|
}
|
|
|
|
export const IPV4_PROTOCOL = { icmp: 1, tcp: 6, udp: 17 } as const;
|
|
export type IPv4Protocol = keyof typeof IPV4_PROTOCOL;
|
|
|
|
export interface IPv4Packet {
|
|
readonly identity: number;
|
|
readonly senderIPAddress: string;
|
|
readonly receiverIPAddress: string;
|
|
/** 0…255 */
|
|
readonly timeToLive: number;
|
|
readonly protocol: IPv4Protocol;
|
|
readonly payload: IPv4Payload;
|
|
}
|
|
|
|
export type IPv4Payload =
|
|
| { readonly kind: 'icmp'; readonly message: IcmpMessage }
|
|
| { readonly kind: 'tcp'; readonly segment: TcpSegment }
|
|
| { readonly kind: 'udp'; readonly datagram: UdpDatagram };
|
|
|
|
/** The same packet one hop further: identity kept, TTL lowered, never below 0. */
|
|
export function forwardingClone(packet: IPv4Packet, decrementingTtlBy = 1): IPv4Packet {
|
|
return {
|
|
...packet,
|
|
timeToLive: decrementingTtlBy >= packet.timeToLive ? 0 : packet.timeToLive - decrementingTtlBy,
|
|
};
|
|
}
|
|
|
|
export type IcmpMessageKind =
|
|
| 'echoRequest'
|
|
| 'echoReply'
|
|
| 'destinationNetworkUnreachable'
|
|
| 'destinationHostUnreachable'
|
|
| 'timeExceeded';
|
|
|
|
export interface IcmpMessage {
|
|
readonly kind: IcmpMessageKind;
|
|
/** 0…65535 */
|
|
readonly identifier: number;
|
|
/** 0…65535 */
|
|
readonly sequenceNumber: number;
|
|
readonly data: Uint8Array;
|
|
readonly embeddedOriginalPacket?: IPv4Packet;
|
|
}
|
|
|
|
export function icmpMessage(
|
|
kind: IcmpMessageKind,
|
|
fields: Partial<Omit<IcmpMessage, 'kind'>> = {},
|
|
): IcmpMessage {
|
|
const message: IcmpMessage = {
|
|
kind,
|
|
identifier: fields.identifier ?? 0,
|
|
sequenceNumber: fields.sequenceNumber ?? 0,
|
|
data: fields.data ?? new Uint8Array(),
|
|
};
|
|
return fields.embeddedOriginalPacket === undefined
|
|
? message
|
|
: { ...message, embeddedOriginalPacket: fields.embeddedOriginalPacket };
|
|
}
|
|
|
|
export const TCP_FLAG = {
|
|
finish: 1 << 0,
|
|
synchronize: 1 << 1,
|
|
reset: 1 << 2,
|
|
push: 1 << 3,
|
|
acknowledgement: 1 << 4,
|
|
} as const;
|
|
|
|
export type TcpSocketState =
|
|
| 'CLOSED'
|
|
| 'LISTEN'
|
|
| 'SYN_RCVD'
|
|
| 'SYN_SENT'
|
|
| 'ESTABLISHED'
|
|
| 'CLOSE_WAIT'
|
|
| 'LAST_ACK'
|
|
| 'FIN_WAIT_1'
|
|
| 'FIN_WAIT_2'
|
|
| 'CLOSING'
|
|
| 'TIME_WAIT';
|
|
|
|
export interface TcpSegment {
|
|
readonly sourcePort: number;
|
|
readonly destinationPort: number;
|
|
readonly sequenceNumber: number;
|
|
readonly acknowledgementNumber: number;
|
|
/** Bit set of TCP_FLAG values. */
|
|
readonly flags: number;
|
|
readonly payload: Uint8Array;
|
|
}
|
|
|
|
export interface UdpDatagram {
|
|
readonly sourcePort: number;
|
|
readonly destinationPort: number;
|
|
readonly payload: Uint8Array;
|
|
}
|
|
|
|
export function packetIdentityInFrame(frame: EthernetFrame): number | undefined {
|
|
return frame.payload.kind === 'ipv4' ? frame.payload.packet.identity : undefined;
|
|
}
|