Skip to content

Commit 25f1fea

Browse files
Manuel-PolpgallinoMegaRedHand
authored
Switching tables (#196)
This PR introduces the implementation of switching tables in the network simulator, enabling more accurate simulation of switch behavior. The main updates include: - **Switching Table Creation and Update:** Switches now maintain a dynamic switching table that stores MAC address to `DeviceId` (for now, later it should be changed to interfaces) mappings. These tables are automatically updated upon receiving packets from connected devices. - **Packet Forwarding Logic:** Switches use the entries in their switching tables to forward packets to the correct output interface. If an entry isn’t found, packets are broadcast as per standard switching protocols, if it found one but it happens that its output interface (`DeviceId`) is the same to the input interface, the packet is dropped, and finally, if an entry with different output interface is found, the packet is forward to that interface. - **Refactor of NetworkInterface Location:** The NetworkInterface component has been moved from DataDevices to ViewDevices, this should help to integrate interfaces logic to the forwarding packet logic. --------- Co-authored-by: Pedro Gallino <[email protected]> Co-authored-by: Tomás Grüner <[email protected]>
1 parent bc458de commit 25f1fea

File tree

15 files changed

+319
-67
lines changed

15 files changed

+319
-67
lines changed

src/programs/dummy_link_program.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { ProgramInfo } from "../graphics/renderables/device_info";
2+
import { DeviceId } from "../types/graphs/datagraph";
3+
import { ViewGraph } from "../types/graphs/viewgraph";
4+
import { ProgramBase } from "./program_base";
5+
import { ViewNetworkDevice } from "../types/view-devices/vNetworkDevice";
6+
import { EthernetFrame } from "../packets/ethernet";
7+
import { sendViewPacket } from "../types/packet";
8+
import { EmptyPayload, IPv4Packet } from "../packets/ip";
9+
10+
// Dummy program to test the link layer packets forwarding, like ARP Request/Response
11+
export class DummyLinkProgram extends ProgramBase {
12+
static readonly PROGRAM_NAME = "Dummy link program";
13+
14+
protected dstId: DeviceId;
15+
16+
constructor(viewgraph: ViewGraph, srcId: DeviceId, inputs: string[]) {
17+
super(viewgraph, srcId, inputs);
18+
this._parseInputs(inputs);
19+
}
20+
21+
protected _parseInputs(inputs: string[]): void {
22+
if (inputs.length !== 1) {
23+
console.error(
24+
"DummyLinkProgram requires 1 input. " + inputs.length + " were given.",
25+
);
26+
return;
27+
}
28+
this.dstId = parseInt(inputs[0]);
29+
}
30+
31+
protected _run() {
32+
this.sendSinglePacket();
33+
this.signalStop();
34+
}
35+
36+
private sendSinglePacket() {
37+
const dstDevice = this.viewgraph.getDevice(this.dstId);
38+
const srcDevice = this.viewgraph.getDevice(this.srcId);
39+
if (!dstDevice) {
40+
console.error("Destination device not found");
41+
return;
42+
}
43+
if (
44+
!(srcDevice instanceof ViewNetworkDevice) ||
45+
!(dstDevice instanceof ViewNetworkDevice)
46+
) {
47+
console.log(
48+
"At least one device between source and destination is not a network device",
49+
);
50+
return;
51+
}
52+
const path = this.viewgraph.getPathBetween(this.srcId, this.dstId);
53+
let dstMac = dstDevice.mac;
54+
if (!path) return;
55+
for (const id of path.slice(1)) {
56+
const device = this.viewgraph.getDevice(id);
57+
// if there’s a router in the middle, first send frame to router mac
58+
if (device instanceof ViewNetworkDevice) {
59+
dstMac = device.mac;
60+
break;
61+
}
62+
}
63+
const payload = new IPv4Packet(
64+
srcDevice.ip,
65+
dstDevice.ip,
66+
new EmptyPayload(),
67+
);
68+
const ethernetFrame = new EthernetFrame(srcDevice.mac, dstMac, payload);
69+
sendViewPacket(this.viewgraph, this.srcId, ethernetFrame);
70+
}
71+
72+
protected _stop() {
73+
// nothing to do
74+
}
75+
76+
static getProgramInfo(viewgraph: ViewGraph, srcId: DeviceId): ProgramInfo {
77+
const programInfo = new ProgramInfo(this.PROGRAM_NAME);
78+
programInfo.withDestinationDropdown(viewgraph, srcId);
79+
return programInfo;
80+
}
81+
82+
static getProgramName(): string {
83+
return this.PROGRAM_NAME;
84+
}
85+
86+
static getProgramDescription(): string {
87+
return "Sends a dummy packet every X ms";
88+
}
89+
90+
static getProgramInputs(): string[] {
91+
return ["Delay in ms"];
92+
}
93+
94+
static getProgramOutputs(): string[] {
95+
return ["Dummy packet"];
96+
}
97+
}

src/programs/echo_sender.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export class EchoServer extends ProgramBase {
113113
}
114114

115115
protected _stop() {
116+
console.debug("Stopping EchoServer");
116117
Ticker.shared.remove(this.tick, this);
117118
}
118119

src/programs/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ProgramInfo } from "../graphics/renderables/program_info";
22
import { DeviceId } from "../types/graphs/datagraph";
33
import { ViewGraph } from "../types/graphs/viewgraph";
4+
import { DummyLinkProgram } from "./dummy_link_program";
45
import { EchoServer, SingleEcho } from "./echo_sender";
56
import { HttpClient } from "./http_client";
67

@@ -64,7 +65,7 @@ type ProgramConstructor = new (
6465
// - Have a static readonly PROGRAM_NAME property
6566
// - Have a constructor with the signature (viewgraph, srcId, inputs)
6667
// - Have a getProgramInfo static method
67-
const programList = [SingleEcho, EchoServer, HttpClient];
68+
const programList = [SingleEcho, EchoServer, HttpClient, DummyLinkProgram];
6869

6970
// Map of program name to program constructor
7071
const programMap = new Map<string, ProgramConstructor>(

src/types/data-devices/dDevice.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
import { EthernetFrame, MacAddress } from "../../packets/ethernet";
2-
import { DataGraph, DataNode } from "../graphs/datagraph";
3-
import { DeviceType } from "../view-devices/vDevice";
2+
import { DataGraph, DataNode, DeviceId } from "../graphs/datagraph";
3+
import { DeviceType, NetworkInterface } from "../view-devices/vDevice";
44
import { Position } from "../common";
55

6-
interface NetworkInterface {
7-
name: string;
8-
mac: MacAddress;
9-
// TODO: add IP address
10-
// ip?: string;
11-
}
12-
136
export abstract class DataDevice {
147
private static idCounter = 1;
158

@@ -73,5 +66,5 @@ export abstract class DataDevice {
7366
* Returns the id for the next device to send the packet to, or
7467
* null if there’s no next device to send the packet.
7568
* */
76-
abstract receiveFrame(frame: EthernetFrame): void;
69+
abstract receiveFrame(frame: EthernetFrame, senderId: DeviceId): void;
7770
}

src/types/data-devices/dSwitch.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
11
import { DeviceType } from "../view-devices/vDevice";
22
import { DataDevice } from "./dDevice";
3-
import { EthernetFrame } from "../../packets/ethernet";
3+
import { EthernetFrame, MacAddress } from "../../packets/ethernet";
4+
import { DataGraph, DeviceId, SwitchDataNode } from "../graphs/datagraph";
45

56
export class DataSwitch extends DataDevice {
6-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
7-
receiveFrame(frame: EthernetFrame): void {
8-
// TODO: this is unused
7+
// would be interface
8+
switchingTable: Map<string, DeviceId> = new Map<string, DeviceId>();
9+
10+
constructor(graphData: SwitchDataNode, datagraph: DataGraph) {
11+
super(graphData, datagraph);
12+
this.switchingTable = new Map<string, number>(graphData.switchingTable);
913
}
1014

1115
getType(): DeviceType {
1216
return DeviceType.Switch;
1317
}
18+
19+
updateSwitchingTable(mac: MacAddress, deviceId: DeviceId): void {
20+
if (!this.switchingTable.has(mac.toString())) {
21+
console.debug(`Adding ${mac.toString()} to the switching table`);
22+
this.switchingTable.set(mac.toString(), deviceId);
23+
}
24+
}
25+
26+
getDataNode(): SwitchDataNode {
27+
return {
28+
...super.getDataNode(),
29+
switchingTable: Array.from(this.switchingTable.entries()),
30+
type: DeviceType.Switch,
31+
};
32+
}
33+
34+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
35+
private forwardFrame(
36+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
37+
frame: EthernetFrame,
38+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
39+
nextHopId: DeviceId, // will be the interface where to send the packet
40+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
41+
senderId: DeviceId, // will be the interface where the packet came from
42+
) {
43+
// TODO: this is unused
44+
}
45+
46+
// TODO: change all related senderId features to the receiver interface
47+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
48+
receiveFrame(_frame: EthernetFrame, _senderId: DeviceId): void {
49+
// TODO: this is unused
50+
}
1451
}

src/types/edge.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ import { Packet } from "./packet";
88
import { EdgeInfo } from "../graphics/renderables/edge_info";
99
import { DataEdge, DeviceId } from "./graphs/datagraph";
1010

11-
export interface EdgeEdges {
12-
n1: DeviceId;
13-
n2: DeviceId;
14-
}
15-
1611
export class Edge extends Graphics {
1712
data: DataEdge;
1813
private startPos: Point;
@@ -66,6 +61,10 @@ export class Edge extends Graphics {
6661
: undefined;
6762
}
6863

64+
getData(): DataEdge {
65+
return this.data;
66+
}
67+
6968
// Method to draw the line
7069
drawEdge(startPos: Point, endPos: Point, color: number) {
7170
this.clear();

src/types/graphs/datagraph.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ interface CommonDataNode {
3434
// TODO: remove this
3535
mac: string;
3636
interfaces: NetworkInterfaceData[];
37-
arpTable?: Map<string, string>;
37+
arpTable?: [string, string][];
3838
}
3939

4040
export interface NetworkInterfaceData {
@@ -48,6 +48,7 @@ export interface NetworkInterfaceData {
4848
}
4949

5050
export interface SwitchDataNode extends CommonDataNode {
51+
switchingTable: [string, number][];
5152
type: DeviceType.Switch;
5253
}
5354

@@ -67,7 +68,7 @@ export interface RouterDataNode extends NetworkDataNode {
6768
export interface RoutingTableEntry {
6869
ip: string;
6970
mask: string;
70-
iface: DeviceId;
71+
iface: number;
7172
manuallyEdited?: boolean;
7273
deleted?: boolean;
7374
}
@@ -150,7 +151,6 @@ export class DataGraph {
150151

151152
// Serialize nodes
152153
for (const [, device] of this.deviceGraph.getAllVertices()) {
153-
device.getDataNode();
154154
// parse to serializable format
155155
const dataNode: DataNode = device.getDataNode();
156156
nodes.push(dataNode);
@@ -250,7 +250,7 @@ export class DataGraph {
250250
showWarning(ALERT_MESSAGES.NO_FREE_INTERFACES(unavailableDevices));
251251
return null;
252252
}
253-
const edge = {
253+
const edge: DataEdge = {
254254
from: { id: n1Id, iface: n1Iface },
255255
to: { id: n2Id, iface: n2Iface },
256256
};

src/types/packet.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export class Packet extends Graphics {
184184
if (!newStartDevice) {
185185
return;
186186
}
187-
newStartDevice.receiveFrame(this.rawPacket);
187+
newStartDevice.receiveFrame(this.rawPacket, this.currStart);
188188
}
189189

190190
traverseEdge(startId: DeviceId, endId: DeviceId): void {
@@ -316,10 +316,13 @@ export class Packet extends Graphics {
316316
}
317317
}
318318

319+
// TODO: Replace and nextHopId with the sending interface. Like this, the function
320+
// can manage to send the packet to each one of the interface connection.
319321
export function sendViewPacket(
320322
viewgraph: ViewGraph,
321323
srcId: DeviceId,
322324
rawPacket: EthernetFrame,
325+
nextHopId?: DeviceId,
323326
) {
324327
const srcMac = rawPacket.source;
325328
const dstMac = rawPacket.destination;
@@ -353,7 +356,7 @@ export function sendViewPacket(
353356
return;
354357
}
355358
const packet = new Packet(viewgraph.ctx, viewgraph, rawPacket);
356-
packet.traverseEdge(srcId, firstEdge.otherEnd(srcId));
359+
packet.traverseEdge(srcId, nextHopId ? nextHopId : firstEdge.otherEnd(srcId));
357360
}
358361

359362
export function dropPacket(

src/types/view-devices/utils.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,34 @@ export function createViewDevice(
3333
packetQueueSize = deviceInfo.packetQueueSize;
3434
timePerByte = deviceInfo.timePerByte;
3535
}
36+
const { id, interfaces } = deviceInfo;
37+
3638
switch (deviceInfo.type) {
3739
case DeviceType.Router:
3840
return new ViewRouter(
39-
deviceInfo.id,
41+
id,
4042
viewgraph,
4143
ctx,
4244
position,
4345
mac,
46+
interfaces,
4447
ip,
4548
mask,
4649
packetQueueSize,
4750
timePerByte,
4851
);
4952
case DeviceType.Host:
5053
return new ViewHost(
51-
deviceInfo.id,
54+
id,
5255
viewgraph,
5356
ctx,
5457
position,
5558
mac,
59+
interfaces,
5660
ip,
5761
mask,
5862
);
5963
case DeviceType.Switch:
60-
return new ViewSwitch(deviceInfo.id, viewgraph, ctx, position, mac);
64+
return new ViewSwitch(id, viewgraph, ctx, position, mac, interfaces);
6165
}
6266
}

0 commit comments

Comments
 (0)