Skip to content

Commit 526383f

Browse files
feat: support sending packets from misconfigured router (#271)
This PR allows sending packets from a misconfigured router, enabling us to show the TTL making routers discard packets. When a router is misconfigured, the table entry matching with the packet doesn't lead to the destination. In those cases, routers now try to resolve the next hop from the interface configured on the entry, instead of dropping the packet. Packets will still be dropped if the next hop is not a `NetworkDevice`. --------- Co-authored-by: Pedro Gallino <[email protected]>
1 parent ba367f1 commit 526383f

File tree

6 files changed

+81
-26
lines changed

6 files changed

+81
-26
lines changed

src/packets/ip.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ export class IPv4Packet implements FramePayload {
251251
protocol: number;
252252

253253
// 16 bits
254-
// TODO: compute
255254
get headerChecksum() {
256255
return this.computeChecksum();
257256
}

src/types/graphs/datagraph.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ interface CommonDataNode {
3232
x: number;
3333
y: number;
3434
type: DeviceType;
35-
// TODO: remove this
3635
interfaces: NetworkInterfaceData[];
3736
tag?: string;
3837
}
@@ -53,7 +52,6 @@ export interface SwitchDataNode extends CommonDataNode {
5352
}
5453

5554
export interface NetworkDataNode extends CommonDataNode {
56-
// TODO: remove this
5755
mask: string;
5856
arpTable: [string, string, boolean][];
5957
}

src/types/graphs/viewgraph.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,14 +422,12 @@ export class ViewGraph {
422422
return this.datagraph;
423423
}
424424

425-
// TODO: This should eventually be changed to use interfaces instead
426425
getDeviceByIP(ipAddress: IpAddress) {
427426
return this.getDevices().find((device) => {
428427
return device instanceof ViewNetworkDevice && device.ownIp(ipAddress);
429428
});
430429
}
431430

432-
// TODO: This should eventually be changed to use interfaces instead
433431
getDeviceByMac(destination: MacAddress): ViewDevice {
434432
return this.getDevices().find((device) => {
435433
return device.ownMac(destination);

src/types/view-devices/vDevice.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export enum DeviceType {
4646
export interface NetworkInterface {
4747
name: string;
4848
mac: MacAddress;
49-
// TODO: add IP address
5049
ip?: IpAddress;
5150
}
5251

src/types/view-devices/vNetworkDevice.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ export abstract class ViewNetworkDevice extends ViewDevice {
168168
: undefined;
169169
}
170170

171-
// TODO: Most probably it will be different for each type of device
172171
handleDatagram(datagram: IPv4Packet, iface: number) {
173172
console.debug("Packet has reach its destination!");
174173
const dstDevice = this.viewgraph.getDeviceByIP(datagram.sourceAddress);

src/types/view-devices/vRouter.ts

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Position } from "../common";
77
import { IpAddress, IPv4Packet } from "../../packets/ip";
88
import { DeviceId, NetworkInterfaceData } from "../graphs/datagraph";
99
import { Texture, Ticker } from "pixi.js";
10-
import { EthernetFrame } from "../../packets/ethernet";
10+
import { EthernetFrame, MacAddress } from "../../packets/ethernet";
1111
import { GlobalContext } from "../../context";
1212
import { DataRouter } from "../data-devices";
1313
import { dropPacket, sendViewPacket } from "../packet";
@@ -171,17 +171,18 @@ export class ViewRouter extends ViewNetworkDevice {
171171
this.handleDatagram(datagram, iface);
172172
return;
173173
}
174-
this.addPacketToQueue(datagram);
174+
this.addPacketToQueue(datagram, iface);
175175
}
176176

177-
addPacketToQueue(datagram: IPv4Packet) {
177+
addPacketToQueue(datagram: IPv4Packet, iface: number) {
178178
const wasEmpty = this.packetQueue.isEmpty();
179179
datagram.timeToLive -= 1;
180180
if (datagram.timeToLive <= 0) {
181181
console.debug(`Device ${this.id} dropped packet with TTL 0`);
182182
this.dropPacket(datagram);
183+
return;
183184
}
184-
if (!this.packetQueue.enqueue(datagram)) {
185+
if (!this.packetQueue.enqueue(datagram, iface)) {
185186
console.debug("Packet queue full, dropping packet");
186187
this.showDeviceIconFor("queueFull", "❗", "Queue full");
187188
this.dropPacket(datagram);
@@ -201,10 +202,11 @@ export class ViewRouter extends ViewNetworkDevice {
201202

202203
processPacket(ticker: Ticker) {
203204
const elapsedTime = ticker.deltaMS * this.viewgraph.getSpeed();
204-
const datagram = this.getPacketsToProcess(elapsedTime);
205-
if (!datagram) {
205+
const packetWithIface = this.getPacketsToProcess(elapsedTime);
206+
if (!packetWithIface) {
206207
return;
207208
}
209+
const datagram = packetWithIface.packet;
208210

209211
const iface = this.routePacket(datagram);
210212

@@ -215,18 +217,67 @@ export class ViewRouter extends ViewNetworkDevice {
215217
dstDevice?.id,
216218
this.viewgraph,
217219
);
218-
if (forwardingData && forwardingData.sendingIface === iface) {
220+
if (forwardingData) {
219221
const { src, nextHop } = forwardingData;
220-
221-
const newFrame = new EthernetFrame(src.mac, nextHop.mac, datagram);
222+
let nextHopMac: MacAddress;
223+
if (forwardingData.sendingIface === iface) {
224+
nextHopMac = nextHop.mac;
225+
} else {
226+
// Try to deduce next hop from the routing table
227+
// If the interface connects to a router, just send it
228+
nextHopMac = this.deduceNextHopMac(datagram, iface);
229+
if (!nextHopMac) {
230+
return;
231+
}
232+
}
233+
const newFrame = new EthernetFrame(src.mac, nextHopMac, datagram);
222234
sendViewPacket(this.viewgraph, this.id, newFrame, iface);
223-
} else console.debug(`Router ${this.id} could not forward packet.`);
235+
} else {
236+
console.debug(`Router ${this.id} could not forward packet.`);
237+
}
224238

225239
if (this.packetQueue.isEmpty()) {
226240
this.stopPacketProcessor();
227241
}
228242
}
229243

244+
private deduceNextHopMac(datagram: IPv4Packet, iface: number): MacAddress {
245+
const connections = this.viewgraph
246+
.getDataGraph()
247+
.getConnectionsInInterface(this.id, iface);
248+
if (connections.length === 0) {
249+
console.warn(
250+
`Device ${this.id} has no connections in interface ${iface}, dropping packet`,
251+
);
252+
this.dropPacket(datagram);
253+
return;
254+
}
255+
const nextHopId = connections[0];
256+
const edge = this.viewgraph.getEdge(this.id, nextHopId);
257+
if (!edge) {
258+
console.error(`Edge ${this.id} has no edge to next hop ${nextHopId}`);
259+
return;
260+
}
261+
const nextHopIface = edge.getDeviceInterface(nextHopId);
262+
if (nextHopIface === undefined) {
263+
console.error(
264+
`Edge ${this.id} has no interface for next hop ${nextHopId}`,
265+
);
266+
return;
267+
}
268+
const nextHop = this.viewgraph.getDevice(nextHopId);
269+
if (!nextHop) {
270+
console.error(`Next hop ${nextHopId} not found`);
271+
return;
272+
}
273+
if (!(nextHop instanceof ViewNetworkDevice)) {
274+
console.error(`Next hop ${nextHopId} is not a network device`);
275+
this.dropPacket(datagram);
276+
return;
277+
}
278+
return nextHop.interfaces[nextHopIface].mac;
279+
}
280+
230281
startPacketProcessor() {
231282
this.processingProgress = 0;
232283
Ticker.shared.add(this.processPacket, this);
@@ -237,9 +288,9 @@ export class ViewRouter extends ViewNetworkDevice {
237288
Ticker.shared.remove(this.processPacket, this);
238289
}
239290

240-
getPacketsToProcess(timeMs: number): IPv4Packet | null {
291+
getPacketsToProcess(timeMs: number): PacketWithIface | null {
241292
this.processingProgress += (this.bytesPerSecond * timeMs) / 1000;
242-
const packetLength = this.packetQueue.getHead()?.totalLength;
293+
const packetLength = this.packetQueue.getHead()?.packet?.totalLength;
243294
if (this.processingProgress < packetLength) {
244295
return null;
245296
}
@@ -271,8 +322,19 @@ export class ViewRouter extends ViewNetworkDevice {
271322
}
272323
}
273324

325+
/**
326+
* A packet with the interface it was received on.
327+
* This is used to keep track of which interface the packet was received on
328+
* when it is enqueued in the packet queue.
329+
*/
330+
interface PacketWithIface {
331+
packet: IPv4Packet;
332+
iface: number;
333+
}
334+
274335
class PacketQueue {
275-
private queue: IPv4Packet[] = [];
336+
// Queue of packets with the interface they were received on
337+
private queue: PacketWithIface[] = [];
276338
private queueSizeBytes = 0;
277339
private maxQueueSizeBytes: number;
278340

@@ -308,27 +370,27 @@ class PacketQueue {
308370
}
309371
}
310372

311-
enqueue(packet: IPv4Packet) {
373+
enqueue(packet: IPv4Packet, iface: number) {
312374
if (this.queueSizeBytes + packet.totalLength > this.maxQueueSizeBytes) {
313375
return false;
314376
}
315-
this.queue.push(packet);
377+
this.queue.push({ packet, iface });
316378
this.queueSizeBytes += packet.totalLength;
317379
this.notifyObservers();
318380
return true;
319381
}
320382

321-
dequeue(): IPv4Packet | undefined {
383+
dequeue(): PacketWithIface | undefined {
322384
if (this.queue.length === 0) {
323385
return;
324386
}
325-
const packet = this.queue.shift();
387+
const { packet, iface } = this.queue.shift();
326388
this.queueSizeBytes -= packet.totalLength;
327389
this.notifyObservers();
328-
return packet;
390+
return { packet, iface };
329391
}
330392

331-
getHead(): IPv4Packet | undefined {
393+
getHead(): PacketWithIface | undefined {
332394
return this.queue[0];
333395
}
334396

0 commit comments

Comments
 (0)