Skip to content

Commit 923274d

Browse files
authored
feat: echo-reply sent when receiving an echo-request. (#105)
1 parent 6da8f97 commit 923274d

File tree

9 files changed

+175
-83
lines changed

9 files changed

+175
-83
lines changed

src/packets/icmp.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ abstract class IcmpPacket implements IpPayload {
4545
}
4646

4747
protected abstract _dataToBytes(): Uint8Array;
48+
49+
getPacketType(): string {
50+
return `ICMP-${this.type}`;
51+
}
4852
}
4953

5054
class EchoMessage extends IcmpPacket {
@@ -79,10 +83,10 @@ class EchoMessage extends IcmpPacket {
7983
}
8084
}
8185

82-
export class EchoReply extends EchoMessage {
83-
type = 0;
84-
}
85-
8686
export class EchoRequest extends EchoMessage {
8787
type = 8;
8888
}
89+
90+
export class EchoReply extends EchoMessage {
91+
type = 0;
92+
}

src/packets/ip.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export class EmptyPayload implements IpPayload {
1313
// This number is reserved for experimental protocols
1414
return 0xfd;
1515
}
16+
getPacketType(): string {
17+
return "EMPTY-PROTOCOL";
18+
}
1619
}
1720

1821
/// Internet Protocol (IP) address
@@ -114,6 +117,8 @@ export interface IpPayload {
114117
toBytes(): Uint8Array;
115118
// The number of the protocol
116119
protocol(): number;
120+
// Packet protocol name
121+
getPacketType(): string;
117122
}
118123

119124
// Info taken from the original RFC: https://datatracker.ietf.org/doc/html/rfc791#section-3.1

src/packets/tcp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ export class TcpSegment implements IpPayload {
161161
this.data = data;
162162
}
163163

164+
getPacketType(): string {
165+
return "TCP";
166+
}
167+
164168
computeChecksum(): number {
165169
const segmentBytes = this.toBytes({ withChecksum: false });
166170

src/types/devices/device.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ import { RightBar } from "../../graphics/right_bar";
1717
import { Colors, ZIndexLevels } from "../../utils";
1818
import { Position } from "../common";
1919
import { DeviceInfo } from "../../graphics/renderables/device_info";
20-
import { IpAddress } from "../../packets/ip";
21-
import { DragDeviceMove, AddEdgeMove } from "../undo-redo";
20+
import { IpAddress, IPv4Packet } from "../../packets/ip";
2221
import { DeviceId } from "../graphs/datagraph";
22+
import { DragDeviceMove, AddEdgeMove } from "../undo-redo";
2323
import { Layer } from "./layer";
24+
import { Packet, sendPacket } from "../packet";
25+
import { EchoReply } from "../../packets/icmp";
2426
import { CreateDevice } from "./utils";
2527

2628
export { Layer } from "./layer";
@@ -55,6 +57,12 @@ export abstract class Device extends Sprite {
5557
ip: IpAddress;
5658
ipMask: IpAddress;
5759

60+
// Each type of device has different ways of handling a received packet.
61+
// Returns the DevicedId for the next device to send the packet to, or
62+
// null if there’s no next device to send the packet.
63+
// TODO: Might be general for all device in the future.
64+
abstract receivePacket(packet: Packet): DeviceId | null;
65+
5866
constructor(
5967
id: DeviceId,
6068
svg: string,
@@ -123,6 +131,35 @@ export abstract class Device extends Sprite {
123131
sprite.height = sprite.height / DEVICE_SIZE;
124132
}
125133

134+
// TODO: Most probably it will be different for each type of device
135+
handlePacket(packet: Packet) {
136+
switch (packet.type) {
137+
case "ICMP-8": {
138+
const destinationDevice = this.viewgraph.getDeviceByIP(
139+
packet.rawPacket.sourceAddress,
140+
);
141+
if (destinationDevice) {
142+
const echoReply = new EchoReply(0);
143+
const ipPacket = new IPv4Packet(
144+
this.ip,
145+
destinationDevice.ip,
146+
echoReply,
147+
);
148+
sendPacket(
149+
this.viewgraph,
150+
ipPacket,
151+
echoReply.getPacketType(),
152+
this.id,
153+
destinationDevice.id,
154+
);
155+
}
156+
break;
157+
}
158+
default:
159+
console.warn("Packet’s type unrecognized");
160+
}
161+
}
162+
126163
delete(): void {
127164
this.viewgraph.removeDevice(this.id);
128165
// Clear connections

src/types/devices/host.ts

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { Device, DeviceType } from "./device";
22
import { ViewGraph } from "../graphs/viewgraph";
33
import PcImage from "../../assets/pc.svg";
44
import { Position } from "../common";
5-
import { IpAddress } from "../../packets/ip";
5+
import { IpAddress, IPv4Packet } from "../../packets/ip";
66
import { createDropdown, DeviceInfo, RightBar } from "../../graphics/right_bar";
77
import { ProgramInfo } from "../../graphics/renderables/device_info";
8-
import { sendPacket } from "../packet";
8+
import { Packet, sendPacket } from "../packet";
99
import { Ticker } from "pixi.js";
1010
import { DeviceId } from "../graphs/datagraph";
1111
import { Layer } from "./layer";
12+
import { EchoRequest } from "../../packets/icmp";
1213
import { isHost, RunningProgram } from "../graphs/datagraph";
1314

1415
const DEFAULT_ECHO_DELAY = 250; // ms
@@ -48,6 +49,13 @@ export class Host extends Device {
4849
return DeviceType.Host;
4950
}
5051

52+
receivePacket(packet: Packet): DeviceId | null {
53+
if (this.ip.equals(packet.rawPacket.destinationAddress)) {
54+
this.handlePacket(packet);
55+
}
56+
return null;
57+
}
58+
5159
getProgramList() {
5260
const adjacentDevices = this.viewgraph
5361
.getDeviceIds()
@@ -64,7 +72,7 @@ export class Host extends Device {
6472
// TODO: extract into classes
6573
const programList: ProgramInfo[] = [
6674
{
67-
name: "Send ICMP echo",
75+
name: "Send ping",
6876
inputs: [dropdownContainer],
6977
start: () => this.sendSingleEcho(destination.value),
7078
},
@@ -104,27 +112,51 @@ export class Host extends Device {
104112

105113
private sendSingleEcho(id: string) {
106114
const dst = parseInt(id);
107-
sendPacket(this.viewgraph, "ICMP", this.id, dst);
115+
const dstDevice = this.viewgraph.getDevice(dst);
116+
if (dstDevice) {
117+
const echoRequest = new EchoRequest(0);
118+
const ipPacket = new IPv4Packet(this.ip, dstDevice.ip, echoRequest);
119+
sendPacket(
120+
this.viewgraph,
121+
ipPacket,
122+
echoRequest.getPacketType(),
123+
this.id,
124+
dst,
125+
);
126+
}
108127
}
109128

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

134+
// TODO: Receive ip address instead of id?
115135
private startEchoServer(id: string) {
116136
const dst = parseInt(id);
117-
let progress = 0;
118-
const send = (ticker: Ticker) => {
119-
const delay = DEFAULT_ECHO_DELAY;
120-
progress += ticker.deltaMS;
121-
if (progress < delay) {
122-
return;
123-
}
124-
sendPacket(this.viewgraph, "ICMP", this.id, dst);
125-
progress -= delay;
126-
};
127-
this.startProgram(send);
137+
const dstDevice = this.viewgraph.getDevice(dst);
138+
// If ip address received instead of id, device may not exist.
139+
if (dstDevice) {
140+
let progress = 0;
141+
const echoRequest = new EchoRequest(0);
142+
const ipPacket = new IPv4Packet(this.ip, dstDevice.ip, echoRequest);
143+
const send = (ticker: Ticker) => {
144+
const delay = DEFAULT_ECHO_DELAY;
145+
progress += ticker.deltaMS;
146+
if (progress < delay) {
147+
return;
148+
}
149+
sendPacket(
150+
this.viewgraph,
151+
ipPacket,
152+
echoRequest.getPacketType(),
153+
this.id,
154+
dst,
155+
);
156+
progress -= delay;
157+
};
158+
this.startProgram(send);
159+
}
128160
}
129161

130162
private startProgram(tick: (ticker: Ticker) => void): Pid {

src/types/devices/router.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import RouterImage from "../../assets/router.svg";
44
import { Position } from "../common";
55
import { DeviceInfo, RightBar } from "../../graphics/right_bar";
66
import { IpAddress } from "../../packets/ip";
7-
import { DeviceId } from "../graphs/datagraph";
7+
import { DeviceId, isRouter } from "../graphs/datagraph";
8+
import { Packet } from "../packet";
89

910
export class Router extends Device {
1011
constructor(
@@ -34,4 +35,27 @@ export class Router extends Device {
3435
getType(): DeviceType {
3536
return DeviceType.Router;
3637
}
38+
39+
routePacket(packet: Packet): DeviceId | null {
40+
const device = this.viewgraph.getDataGraph().getDevice(this.id);
41+
if (!device || !isRouter(device)) {
42+
return null;
43+
}
44+
const result = device.routingTable.find((entry) => {
45+
const ip = IpAddress.parse(entry.ip);
46+
const mask = IpAddress.parse(entry.mask);
47+
console.log("considering entry:", entry);
48+
return packet.rawPacket.destinationAddress.isInSubnet(ip, mask);
49+
});
50+
console.log("result:", result);
51+
return result === undefined ? null : result.iface;
52+
}
53+
54+
receivePacket(packet: Packet): DeviceId | null {
55+
if (this.ip.equals(packet.rawPacket.destinationAddress)) {
56+
this.handlePacket(packet);
57+
return null;
58+
}
59+
return this.routePacket(packet);
60+
}
3761
}

src/types/graphs/viewgraph.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Layer, layerIncluded } from "../devices/layer";
66
import { SpeedMultiplier } from "../devices/speedMultiplier";
77
import { CreateDevice, createDevice } from "../devices/utils";
88
import { layerFromType } from "../devices/device";
9+
import { IpAddress } from "../../packets/ip";
910

1011
export type EdgeId = string;
1112

@@ -320,6 +321,12 @@ export class ViewGraph {
320321
return this.datagraph;
321322
}
322323

324+
getDeviceByIP(ipAddress: IpAddress) {
325+
return this.getDevices().find((device) => {
326+
return device.ip == ipAddress;
327+
});
328+
}
329+
323330
/// Returns the IDs of the edges connecting the two devices
324331
getPathBetween(idA: DeviceId, idB: DeviceId): EdgeId[] {
325332
if (idA === idB) {

0 commit comments

Comments
 (0)