Skip to content

Commit 1b2680a

Browse files
authored
Introduce devices interfaces into rounting tables and forwarding packets logic (#230)
MAC and IP addresses are now just in device interfaces and the forwarding logic is based on the sending interfaces instead of the next hop device.
1 parent 9680b26 commit 1b2680a

File tree

26 files changed

+449
-389
lines changed

26 files changed

+449
-389
lines changed

src/context.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ export class GlobalContext {
4141
return this.ipGenerator.getNextIp();
4242
}
4343

44+
getMask(): string {
45+
return this.ipGenerator.getMask();
46+
}
4447
getNextMac(): string {
4548
return this.macGenerator.getNextMac();
4649
}
@@ -178,9 +181,11 @@ export class GlobalContext {
178181

179182
for (const [, device] of this.datagraph.getDevices()) {
180183
if (device instanceof DataNetworkDevice) {
181-
if (compareIps(maxIp, device.ip) < 0) {
182-
maxIp = device.ip;
183-
}
184+
device.interfaces.forEach((iface) => {
185+
if (iface.ip && compareIps(maxIp, iface.ip) < 0) {
186+
maxIp = iface.ip;
187+
}
188+
});
184189
}
185190
}
186191
const baseIp = maxIp.toString();
@@ -191,9 +196,11 @@ export class GlobalContext {
191196
private setMacGenerator() {
192197
let maxMac = MacAddress.parse("00:00:10:00:00:00");
193198
for (const [, device] of this.datagraph.getDevices()) {
194-
if (compareMacs(maxMac, device.mac) < 0) {
195-
maxMac = device.mac;
196-
}
199+
device.interfaces.forEach((iface) => {
200+
if (compareMacs(maxMac, iface.mac) < 0) {
201+
maxMac = iface.mac;
202+
}
203+
});
197204
}
198205
// TODO: we should use MacAddress instead of string here and in Datagraph
199206
const baseMac = maxMac.toString();

src/graphics/renderables/device_info.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class DeviceInfo extends BaseInfo {
2929
}
3030

3131
protected addCommonInfoFields(): void {
32-
const { id, mac } = this.device;
32+
const { id } = this.device;
3333
const connections = this.device.viewgraph.getVisibleConnectedDeviceIds(id);
3434

3535
this.information.addField(TOOLTIP_KEYS.ID, id.toString(), TOOLTIP_KEYS.ID);
@@ -42,10 +42,12 @@ export class DeviceInfo extends BaseInfo {
4242
const layer = this.device.viewgraph.getLayer();
4343

4444
if (layer == Layer.Link) {
45-
this.information.addField(
46-
TOOLTIP_KEYS.MAC_ADDRESS,
47-
mac.toString(),
48-
TOOLTIP_KEYS.MAC_ADDRESS,
45+
this.device.interfaces.forEach((iface) =>
46+
this.information.addField(
47+
TOOLTIP_KEYS.MAC_ADDRESS,
48+
iface.mac.toString(),
49+
TOOLTIP_KEYS.MAC_ADDRESS,
50+
),
4951
);
5052
}
5153
}

src/graphics/renderables/edge_info.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ export class EdgeInfo extends BaseInfo {
5353
// Add MAC addresses as separate fields
5454
this.information.addField(
5555
`MAC Address iface (Device ${from})`,
56-
fromInterface.mac.octets.join(":"),
56+
fromInterface.mac.toString(),
5757
TOOLTIP_KEYS.MAC_ADDRESS_IFACE,
5858
);
5959
this.information.addField(
6060
`MAC Address iface (Device ${to})`,
61-
toInterface.mac.octets.join(":"),
61+
toInterface.mac.toString(),
6262
TOOLTIP_KEYS.MAC_ADDRESS_IFACE,
6363
);
6464
}

src/graphics/renderables/program_info.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,13 @@ function otherDevices(viewgraph: ViewGraph, srcId: DeviceId) {
7171
.map((id) => ({ value: id.toString(), text: `Device ${id}`, id }));
7272
}
7373

74-
function otherDevicesIp(viewgraph: ViewGraph, srcId: DeviceId) {
74+
function otherDevicesIp(
75+
viewgraph: ViewGraph,
76+
srcId: DeviceId,
77+
): {
78+
value: string;
79+
text: string;
80+
}[] {
7581
return viewgraph
7682
.getDevices()
7783
.filter(
@@ -80,12 +86,10 @@ function otherDevicesIp(viewgraph: ViewGraph, srcId: DeviceId) {
8086
device.getType() !== DeviceType.Switch &&
8187
device.id !== srcId,
8288
)
83-
.map((device) => {
84-
if ("ip" in device) {
85-
const ipString = device.ip.toString();
86-
return { value: ipString, text: `${ipString} (Device ${device.id})` };
87-
}
88-
// Shouldn’t get here
89-
return { value: "", text: "" };
89+
.flatMap((device) => {
90+
return device.interfaces.map((iface) => ({
91+
value: iface.ip.toString(),
92+
text: `${iface.ip.toString()} (Device ${device.id})`,
93+
}));
9094
});
9195
}

src/packets/ip.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ export class IpAddressGenerator {
111111
return { ip: nextIp, mask: this.mask };
112112
}
113113

114+
/**
115+
* Return the mask used by the IpAddressGenerator
116+
*/
117+
getMask(): string {
118+
return this.mask;
119+
}
120+
114121
// Turn IP into a number
115122
static ipToNumber(ip: string): number {
116123
return ip

src/programs/arp_protocol.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,20 @@ export class ArpProtocol extends ProgramBase {
4646
private sendRequest() {
4747
const srcDevice = this.viewgraph.getDevice(this.srcId);
4848
if (!(srcDevice instanceof ViewNetworkDevice)) {
49-
console.log(
50-
"At least one device between source and destination is not a network device",
51-
);
49+
console.error("Source device is not a network device");
5250
return;
5351
}
5452
const connections = this.viewgraph.getConnections(this.srcId);
5553
connections.forEach((edge) => {
56-
const payload: FramePayload = new ArpRequest(
57-
srcDevice.mac,
58-
srcDevice.ip,
59-
this.dstIp,
60-
);
54+
const ifaceNum = edge.getDeviceInterface(this.srcId);
55+
const { mac, ip } = srcDevice.interfaces[ifaceNum];
56+
const payload: FramePayload = new ArpRequest(mac, ip, this.dstIp);
6157
const ethernetFrame = new EthernetFrame(
62-
srcDevice.mac,
58+
mac,
6359
MacAddress.broadcastAddress(),
6460
payload,
6561
);
66-
sendViewPacket(
67-
this.viewgraph,
68-
this.srcId,
69-
ethernetFrame,
70-
edge.otherEnd(this.srcId),
71-
);
62+
sendViewPacket(this.viewgraph, this.srcId, ethernetFrame, ifaceNum);
7263
});
7364
}
7465

src/programs/echo_sender.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,32 +50,27 @@ export class SingleEcho extends ProgramBase {
5050
);
5151
return;
5252
}
53+
const { src, dst, sendingIface } = ViewNetworkDevice.getForwardingData(
54+
this.srcId,
55+
this.dstId,
56+
this.viewgraph,
57+
);
5358
const echoRequest = new EchoRequest(0);
5459
// Wrap in IP datagram
55-
const ipPacket = new IPv4Packet(srcDevice.ip, dstDevice.ip, echoRequest);
60+
const ipPacket = new IPv4Packet(src.ip, dst.ip, echoRequest);
5661

5762
// Resolve destination MAC address
58-
let dstMac = srcDevice.resolveAddress(dstDevice.ip);
63+
const dstMac = srcDevice.resolveAddress(dst.ip);
5964
if (!dstMac) {
6065
console.warn(
61-
`Device ${this.srcId} couldn't resolve MAC address for device with IP ${dstDevice.ip.toString()}. Program cancelled`,
66+
`Device ${this.srcId} couldn't resolve MAC address for device with IP ${dst.ip.toString()}. Program cancelled`,
6267
);
6368
return;
6469
}
65-
const path = this.viewgraph.getPathBetween(this.srcId, this.dstId);
66-
if (!path) return;
67-
for (const id of path.slice(1)) {
68-
const device = this.viewgraph.getDevice(id);
69-
// if there’s a router in the middle, first send frame to router mac
70-
if (device instanceof ViewNetworkDevice) {
71-
dstMac = device.mac;
72-
break;
73-
}
74-
}
7570

7671
// Wrap in Ethernet frame
77-
const ethernetFrame = new EthernetFrame(srcDevice.mac, dstMac, ipPacket);
78-
sendViewPacket(this.viewgraph, this.srcId, ethernetFrame);
72+
const ethernetFrame = new EthernetFrame(src.mac, dst.mac, ipPacket);
73+
sendViewPacket(this.viewgraph, this.srcId, ethernetFrame, sendingIface);
7974
}
8075

8176
static getProgramInfo(viewgraph: ViewGraph, srcId: DeviceId): ProgramInfo {

src/programs/http_client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ export class HttpClient extends ProgramBase {
7474
}
7575

7676
// Encode HTTP request
77+
// NOTE: For now, as hosts have just one interface, destination ip is hardcoded
7778
const httpRequest = getContentRequest(
78-
this.runner.ip.toString(),
79+
this.runner.interfaces[0].toString(),
7980
this.resource,
8081
);
8182

src/types/data-devices/dDevice.ts

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

67
export abstract class DataDevice {
78
private static idCounter = 1;
89

910
id: number;
1011
x: number;
1112
y: number;
12-
mac: MacAddress;
1313
datagraph: DataGraph;
1414
interfaces: NetworkInterface[] = [];
1515

@@ -22,7 +22,6 @@ export abstract class DataDevice {
2222
constructor(graphData: DataNode, datagraph: DataGraph) {
2323
this.x = graphData.x;
2424
this.y = graphData.y;
25-
this.mac = MacAddress.parse(graphData.mac);
2625
if (graphData.id) {
2726
this.id = graphData.id;
2827
DataDevice.setIdCounter(graphData.id);
@@ -33,6 +32,7 @@ export abstract class DataDevice {
3332
this.interfaces.push({
3433
name: iface.name,
3534
mac: MacAddress.parse(iface.mac),
35+
ip: iface.ip !== undefined ? IpAddress.parse(iface.ip) : undefined,
3636
});
3737
});
3838
this.datagraph = datagraph;
@@ -48,16 +48,20 @@ export abstract class DataDevice {
4848
type: this.getType(),
4949
x: this.x,
5050
y: this.y,
51-
mac: this.mac.toString(),
5251
interfaces: this.interfaces.map((iface) => ({
5352
name: iface.name,
5453
mac: iface.mac.toString(),
54+
ip: iface.ip !== undefined ? iface.ip.toString() : undefined,
5555
})),
5656
};
5757
}
5858

5959
abstract getType(): DeviceType;
6060

61+
ownMac(mac: MacAddress): boolean {
62+
return this.interfaces.some((iface) => iface.mac.equals(mac));
63+
}
64+
6165
getPosition(): Position {
6266
return { x: this.x, y: this.y };
6367
}
@@ -66,5 +70,5 @@ export abstract class DataDevice {
6670
* Returns the id for the next device to send the packet to, or
6771
* null if there’s no next device to send the packet.
6872
* */
69-
abstract receiveFrame(frame: EthernetFrame, senderId: DeviceId): void;
73+
abstract receiveFrame(frame: EthernetFrame, iface: number): void;
7074
}

src/types/data-devices/dHost.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class DataHost extends DataNetworkDevice {
2525
if (!(datagram instanceof IPv4Packet)) {
2626
return null;
2727
}
28-
if (this.ip.equals(datagram.destinationAddress)) {
28+
if (this.ownIp(datagram.destinationAddress)) {
2929
this.handlePacket(datagram);
3030
}
3131
}

0 commit comments

Comments
 (0)