Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: echo-reply sent when receiving an echo-request. #105

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/packets/icmp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ abstract class IcmpPacket implements IpPayload {
}

protected abstract _dataToBytes(): Uint8Array;

getPacketType(): string {
return `ICMP-${this.type}`;
}
}

class EchoMessage extends IcmpPacket {
Expand Down Expand Up @@ -79,10 +83,10 @@ class EchoMessage extends IcmpPacket {
}
}

export class EchoReply extends EchoMessage {
type = 0;
}

export class EchoRequest extends EchoMessage {
type = 8;
}

export class EchoReply extends EchoMessage {
type = 0;
}
5 changes: 5 additions & 0 deletions src/packets/ip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export class EmptyPayload implements IpPayload {
// This number is reserved for experimental protocols
return 0xfd;
}
getPacketType(): string {
return "EMPTY-PROTOCOL";
}
}

/// Internet Protocol (IP) address
Expand Down Expand Up @@ -114,6 +117,8 @@ export interface IpPayload {
toBytes(): Uint8Array;
// The number of the protocol
protocol(): number;
// Packet protocol name
getPacketType(): string;
Comment on lines +120 to +121
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the place for this.

}

// Info taken from the original RFC: https://datatracker.ietf.org/doc/html/rfc791#section-3.1
Expand Down
4 changes: 4 additions & 0 deletions src/packets/tcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ export class TcpSegment implements IpPayload {
this.data = data;
}

getPacketType(): string {
return "TCP";
}

computeChecksum(): number {
const segmentBytes = this.toBytes({ withChecksum: false });

Expand Down
41 changes: 39 additions & 2 deletions src/types/devices/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import { RightBar } from "../../graphics/right_bar";
import { Colors, ZIndexLevels } from "../../utils";
import { Position } from "../common";
import { DeviceInfo } from "../../graphics/renderables/device_info";
import { IpAddress } from "../../packets/ip";
import { DragDeviceMove, AddEdgeMove } from "../undo-redo";
import { IpAddress, IPv4Packet } from "../../packets/ip";
import { DeviceId } from "../graphs/datagraph";
import { DragDeviceMove, AddEdgeMove } from "../undo-redo";
import { Layer } from "./layer";
import { Packet, sendPacket } from "../packet";
import { EchoReply } from "../../packets/icmp";
import { CreateDevice } from "./utils";

export { Layer } from "./layer";
Expand Down Expand Up @@ -55,6 +57,12 @@ export abstract class Device extends Sprite {
ip: IpAddress;
ipMask: IpAddress;

// Each type of device has different ways of handling a received packet.
// Returns the DevicedId for the next device to send the packet to, or
// null if there’s no next device to send the packet.
// TODO: Might be general for all device in the future.
abstract receivePacket(packet: Packet): DeviceId | null;

constructor(
id: DeviceId,
svg: string,
Expand Down Expand Up @@ -123,6 +131,35 @@ export abstract class Device extends Sprite {
sprite.height = sprite.height / DEVICE_SIZE;
}

// TODO: Most probably it will be different for each type of device
handlePacket(packet: Packet) {
switch (packet.type) {
case "ICMP-8": {
const destinationDevice = this.viewgraph.getDeviceByIP(
packet.rawPacket.sourceAddress,
);
if (destinationDevice) {
const echoReply = new EchoReply(0);
const ipPacket = new IPv4Packet(
this.ip,
destinationDevice.ip,
echoReply,
);
sendPacket(
this.viewgraph,
ipPacket,
echoReply.getPacketType(),
this.id,
destinationDevice.id,
);
}
break;
}
default:
console.warn("Packet’s type unrecognized");
}
}

delete(): void {
this.viewgraph.removeDevice(this.id);
// Clear connections
Expand Down
62 changes: 47 additions & 15 deletions src/types/devices/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Device, DeviceType } from "./device";
import { ViewGraph } from "../graphs/viewgraph";
import PcImage from "../../assets/pc.svg";
import { Position } from "../common";
import { IpAddress } from "../../packets/ip";
import { IpAddress, IPv4Packet } from "../../packets/ip";
import { createDropdown, DeviceInfo, RightBar } from "../../graphics/right_bar";
import { ProgramInfo } from "../../graphics/renderables/device_info";
import { sendPacket } from "../packet";
import { Packet, sendPacket } from "../packet";
import { Ticker } from "pixi.js";
import { DeviceId } from "../graphs/datagraph";
import { Layer } from "./layer";
import { EchoRequest } from "../../packets/icmp";
import { isHost, RunningProgram } from "../graphs/datagraph";

const DEFAULT_ECHO_DELAY = 250; // ms
Expand Down Expand Up @@ -48,6 +49,13 @@ export class Host extends Device {
return DeviceType.Host;
}

receivePacket(packet: Packet): DeviceId | null {
if (this.ip.equals(packet.rawPacket.destinationAddress)) {
this.handlePacket(packet);
}
return null;
}

getProgramList() {
const adjacentDevices = this.viewgraph
.getDeviceIds()
Expand All @@ -64,7 +72,7 @@ export class Host extends Device {
// TODO: extract into classes
const programList: ProgramInfo[] = [
{
name: "Send ICMP echo",
name: "Send ping",
inputs: [dropdownContainer],
start: () => this.sendSingleEcho(destination.value),
},
Expand Down Expand Up @@ -104,27 +112,51 @@ export class Host extends Device {

private sendSingleEcho(id: string) {
const dst = parseInt(id);
sendPacket(this.viewgraph, "ICMP", this.id, dst);
const dstDevice = this.viewgraph.getDevice(dst);
if (dstDevice) {
const echoRequest = new EchoRequest(0);
const ipPacket = new IPv4Packet(this.ip, dstDevice.ip, echoRequest);
sendPacket(
this.viewgraph,
ipPacket,
echoRequest.getPacketType(),
this.id,
dst,
);
}
}

private startNewEchoServer(id: string) {
this.addRunningProgram({ name: "Echo server", inputs: [id] });
this.startEchoServer(id);
}

// TODO: Receive ip address instead of id?
private startEchoServer(id: string) {
const dst = parseInt(id);
let progress = 0;
const send = (ticker: Ticker) => {
const delay = DEFAULT_ECHO_DELAY;
progress += ticker.deltaMS;
if (progress < delay) {
return;
}
sendPacket(this.viewgraph, "ICMP", this.id, dst);
progress -= delay;
};
this.startProgram(send);
const dstDevice = this.viewgraph.getDevice(dst);
// If ip address received instead of id, device may not exist.
if (dstDevice) {
let progress = 0;
const echoRequest = new EchoRequest(0);
const ipPacket = new IPv4Packet(this.ip, dstDevice.ip, echoRequest);
const send = (ticker: Ticker) => {
const delay = DEFAULT_ECHO_DELAY;
progress += ticker.deltaMS;
if (progress < delay) {
return;
}
sendPacket(
this.viewgraph,
ipPacket,
echoRequest.getPacketType(),
this.id,
dst,
);
progress -= delay;
};
this.startProgram(send);
}
}

private startProgram(tick: (ticker: Ticker) => void): Pid {
Expand Down
26 changes: 25 additions & 1 deletion src/types/devices/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import RouterImage from "../../assets/router.svg";
import { Position } from "../common";
import { DeviceInfo, RightBar } from "../../graphics/right_bar";
import { IpAddress } from "../../packets/ip";
import { DeviceId } from "../graphs/datagraph";
import { DeviceId, isRouter } from "../graphs/datagraph";
import { Packet } from "../packet";

export class Router extends Device {
constructor(
Expand Down Expand Up @@ -32,4 +33,27 @@ export class Router extends Device {
getType(): DeviceType {
return DeviceType.Router;
}

routePacket(packet: Packet): DeviceId | null {
const device = this.viewgraph.getDataGraph().getDevice(this.id);
if (!device || !isRouter(device)) {
return null;
}
const result = device.routingTable.find((entry) => {
const ip = IpAddress.parse(entry.ip);
const mask = IpAddress.parse(entry.mask);
console.log("considering entry:", entry);
return packet.rawPacket.destinationAddress.isInSubnet(ip, mask);
});
console.log("result:", result);
return result === undefined ? null : result.iface;
}

receivePacket(packet: Packet): DeviceId | null {
if (this.ip.equals(packet.rawPacket.destinationAddress)) {
this.handlePacket(packet);
return null;
}
return this.routePacket(packet);
}
}
7 changes: 7 additions & 0 deletions src/types/graphs/viewgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Viewport } from "../../graphics/viewport";
import { Layer, layerIncluded } from "../devices/layer";
import { CreateDevice, createDevice } from "../devices/utils";
import { layerFromType } from "../devices/device";
import { IpAddress } from "../../packets/ip";

export type EdgeId = string;

Expand Down Expand Up @@ -292,6 +293,12 @@ export class ViewGraph {
return this.datagraph;
}

getDeviceByIP(ipAddress: IpAddress) {
return this.getDevices().find((device) => {
return device.ip == ipAddress;
});
}

/// Returns the IDs of the edges connecting the two devices
getPathBetween(idA: DeviceId, idB: DeviceId): EdgeId[] {
if (idA === idB) {
Expand Down
Loading