diff --git a/src/context.ts b/src/context.ts index b3d84a46..88564b10 100644 --- a/src/context.ts +++ b/src/context.ts @@ -19,8 +19,8 @@ export class GlobalContext { initialize(viewport: Viewport) { this.viewport = viewport; - const baseIp = "192.168.1.0"; - const mask = "255.255.255.0"; + const baseIp = "10.0.0.0"; + const mask = "255.255.255.255"; this.ipGenerator = new IpAddressGenerator(baseIp, mask); loadFromLocalStorage(this); } diff --git a/src/graphics/renderables/device_info.ts b/src/graphics/renderables/device_info.ts index 6a304cad..ecb426ad 100644 --- a/src/graphics/renderables/device_info.ts +++ b/src/graphics/renderables/device_info.ts @@ -71,7 +71,11 @@ export class DeviceInfo extends StyledInfo { } addRoutingTable(entries: RoutingTableEntry[]) { - const rows = entries.map((entry) => [entry.ip, entry.mask, entry.iface]); + const rows = entries.map((entry) => [ + entry.ip, + entry.mask, + `eth${entry.iface}`, + ]); const dynamicTable = createToggleTable( "Routing Table", // Title diff --git a/src/types/devices/device.ts b/src/types/devices/device.ts index c60634df..9dafc435 100644 --- a/src/types/devices/device.ts +++ b/src/types/devices/device.ts @@ -36,7 +36,7 @@ export enum DeviceType { export abstract class Device extends Sprite { readonly id: DeviceId; readonly viewgraph: ViewGraph; - connections = new Map(); + connections = new Map(); highlightMarker: Graphics | null = null; // Marker to indicate selection @@ -47,7 +47,7 @@ export abstract class Device extends Sprite { ipMask: IpAddress; constructor( - id: number, + id: DeviceId, svg: string, viewgraph: ViewGraph, position: Position, diff --git a/src/types/edge.ts b/src/types/edge.ts index 2f18c2d2..47e88fa9 100644 --- a/src/types/edge.ts +++ b/src/types/edge.ts @@ -1,26 +1,27 @@ import { Graphics, Point } from "pixi.js"; -import { ViewGraph } from "./graphs/viewgraph"; +import { EdgeId, ViewGraph } from "./graphs/viewgraph"; import { Device } from "./devices/index"; // Import the Device class import { deselectElement, selectElement } from "./viewportManager"; import { RightBar, StyledInfo } from "../graphics/right_bar"; import { Colors, ZIndexLevels } from "../utils"; import { Packet } from "./packet"; +import { DeviceId } from "./graphs/datagraph"; export interface EdgeEdges { - n1: number; - n2: number; + n1: DeviceId; + n2: DeviceId; } export class Edge extends Graphics { - id: number; - connectedNodes: { n1: number; n2: number }; + id: EdgeId; + connectedNodes: { n1: DeviceId; n2: DeviceId }; startPos: Point; endPos: Point; viewgraph: ViewGraph; rightbar: RightBar; constructor( - id: number, + id: EdgeId, connectedNodes: EdgeEdges, device1: Device, device2: Device, @@ -41,7 +42,7 @@ export class Edge extends Graphics { this.on("click", () => selectElement(this)); } - nodePosition(nodeId: number): Point | undefined { + nodePosition(nodeId: DeviceId): Point | undefined { return this.connectedNodes.n1 === nodeId ? this.startPos : this.connectedNodes.n2 === nodeId @@ -49,7 +50,7 @@ export class Edge extends Graphics { : undefined; } - otherEnd(nodeId: number): number | undefined { + otherEnd(nodeId: DeviceId): DeviceId | undefined { return this.connectedNodes.n1 === nodeId ? this.connectedNodes.n2 : this.connectedNodes.n2 === nodeId diff --git a/src/types/graphs/datagraph.ts b/src/types/graphs/datagraph.ts index 48d94189..60e10d26 100644 --- a/src/types/graphs/datagraph.ts +++ b/src/types/graphs/datagraph.ts @@ -19,7 +19,7 @@ interface RouterGraphNode extends CommonGraphNode { export interface RoutingTableEntry { ip: string; mask: string; - iface: string; + iface: DeviceId; } // Typescript type guard @@ -162,7 +162,7 @@ export class DataGraph { device1.routingTable.push({ ip: device2.ip.toString(), mask: device2.mask, - iface: `eth${n2Id}`, + iface: n2Id, }); } @@ -173,7 +173,7 @@ export class DataGraph { device2.routingTable.push({ ip: device1.ip.toString(), mask: device1.mask, - iface: `eth${n1Id}`, + iface: n1Id, }); } @@ -181,12 +181,13 @@ export class DataGraph { `Connection created between devices ID: ${n1Id} and ID: ${n2Id}`, ); this.notifyChanges(); + this.regenerateAllRoutingTables(); } updateDevicePosition(id: DeviceId, newValues: { x?: number; y?: number }) { const deviceGraphNode = this.devices.get(id); if (!deviceGraphNode) { - console.warn("Device’s id is not registered"); + console.warn("Device's id is not registered"); return; } this.devices.set(id, { ...deviceGraphNode, ...newValues }); @@ -237,6 +238,7 @@ export class DataGraph { this.devices.delete(id); console.log(`Device with ID ${id} and its connections were removed.`); this.notifyChanges(); + this.regenerateAllRoutingTables(); } // Method to remove a connection (edge) between two devices by their IDs @@ -270,6 +272,7 @@ export class DataGraph { `Connection removed between devices ID: ${n1Id} and ID: ${n2Id}`, ); this.notifyChanges(); + this.regenerateAllRoutingTables(); } subscribeChanges(callback: () => void) { @@ -279,4 +282,56 @@ export class DataGraph { notifyChanges() { this.onChanges.forEach((callback) => callback()); } + + regenerateAllRoutingTables() { + console.log("Regenerating all routing tables"); + this.devices.forEach((_, id) => this.regenerateRoutingTable(id)); + } + + regenerateRoutingTable(id: DeviceId) { + const router = this.devices.get(id); + if (!isRouter(router)) { + return; + } + console.log(`Regenerating routing table for ID ${id}`); + const parents = new Map(); + parents.set(id, id); + const queue = Array.from([id]); + while (queue.length > 0) { + const currentId = queue.shift(); + const current = this.devices.get(currentId); + if (!isRouter(current)) { + // Don't route packets on hosts + continue; + } + current.connections.forEach((connectedId) => { + if (!parents.has(connectedId)) { + parents.set(connectedId, currentId); + queue.push(connectedId); + } + }); + } + + console.log(parents); + + const table: RoutingTableEntry[] = []; + parents.forEach((currentId, childId) => { + const dstId = childId; + if (dstId === id) { + return; + } + + while (currentId !== id) { + const parentId = parents.get(currentId); + childId = currentId; + currentId = parentId; + } + // Here the currentId is the router, and the childId + // is the first step towards dstId + const dst = this.devices.get(dstId); + const entry = { ip: dst.ip, mask: dst.mask, iface: childId }; + table.push(entry); + }); + router.routingTable = table; + } } diff --git a/src/types/graphs/viewgraph.ts b/src/types/graphs/viewgraph.ts index de345b36..e58252fc 100644 --- a/src/types/graphs/viewgraph.ts +++ b/src/types/graphs/viewgraph.ts @@ -22,7 +22,7 @@ export class ViewGraph { private devices: Map = new Map(); private edges: Map = new Map(); private idCounter: EdgeId = 1; - private datagraph: DataGraph; + datagraph: DataGraph; private layer: Layer; viewport: Viewport; @@ -171,7 +171,11 @@ export class ViewGraph { // Get all connections of a device getConnections(id: DeviceId): Edge[] { const device = this.devices.get(id); - return device ? Array.from(this.edges.values()) : []; + return device + ? Array.from(device.connections.keys()).map((edgeId) => + this.edges.get(edgeId), + ) + : []; } // Get a specific device by its ID diff --git a/src/types/packet.ts b/src/types/packet.ts index 3f35e1f7..3cb2cce0 100644 --- a/src/types/packet.ts +++ b/src/types/packet.ts @@ -10,8 +10,9 @@ import { circleGraphicsContext, Colors, ZIndexLevels } from "../utils"; import { RightBar, StyledInfo } from "../graphics/right_bar"; import { Position } from "./common"; import { ViewGraph } from "./graphs/viewgraph"; -import { EmptyPayload, IPv4Packet } from "../packets/ip"; +import { EmptyPayload, IpAddress, IPv4Packet } from "../packets/ip"; import { EchoRequest } from "../packets/icmp"; +import { DeviceId, isRouter } from "./graphs/datagraph"; const contextPerPacketType: Record = { IP: circleGraphicsContext(Colors.Green, 0, 0, 5), @@ -23,7 +24,7 @@ const highlightedPacketContext = circleGraphicsContext(Colors.Violet, 0, 0, 6); export class Packet extends Graphics { speed = 200; progress = 0; - currentPath: Edge[]; + viewgraph: ViewGraph; currentEdge: Edge; currentStart: number; color: number; @@ -44,6 +45,7 @@ export class Packet extends Graphics { } constructor( + viewgraph: ViewGraph, type: string, rawPacket: IPv4Packet, sourceid: number, @@ -51,6 +53,7 @@ export class Packet extends Graphics { ) { super(); + this.viewgraph = viewgraph; this.type = type; this.context = contextPerPacketType[this.type]; @@ -71,12 +74,12 @@ export class Packet extends Graphics { } select() { - this.highlight(); // Calls highlight on select + this.highlight(); this.showInfo(); } deselect() { - this.removeHighlight(); // Calls removeHighlight on deselect + this.removeHighlight(); } showInfo() { @@ -111,17 +114,9 @@ export class Packet extends Graphics { this.context = contextPerPacketType[this.type]; } - animateAlongPath(path: Edge[], start: number): void { - if (path.length === 0) { - console.error( - "No se puede animar un paquete a lo largo de un camino vacío", - ); - this.destroy(); - return; - } - console.log(path); - this.currentPath = path; - this.currentEdge = this.currentPath.shift(); + traverseEdge(edge: Edge, start: DeviceId): void { + this.progress = 0; + this.currentEdge = edge; this.currentStart = start; // Add packet as a child of the current edge this.currentEdge.addChild(this); @@ -129,20 +124,49 @@ export class Packet extends Graphics { Ticker.shared.add(this.animationTick, this); } + routePacket(id: DeviceId): DeviceId | null { + const device = this.viewgraph.datagraph.getDevice(id); + if (isRouter(device)) { + const result = device.routingTable.find((entry) => { + const ip = IpAddress.parse(entry.ip); + const mask = IpAddress.parse(entry.mask); + console.log("considering entry:", entry); + return this.rawPacket.destinationAddress.isInSubnet(ip, mask); + }); + console.log("result:", result); + return result === undefined ? null : result.iface; + } + return null; + } + animationTick(ticker: Ticker) { if (this.progress >= 1) { this.progress = 0; this.removeFromParent(); - if (this.currentPath.length == 0) { + const newStart = this.currentEdge.otherEnd(this.currentStart); + this.currentStart = newStart; + const newEdgeId = this.routePacket(newStart); + + const deleteSelf = () => { + this.destroy(); ticker.remove(this.animationTick, this); if (isSelected(this)) { deselectElement(); } - this.destroy(); + }; + + if (newEdgeId === null) { + deleteSelf(); + return; + } + const currentNodeEdges = this.viewgraph.getConnections(newStart); + this.currentEdge = currentNodeEdges.find((edge) => { + return edge.otherEnd(newStart) === newEdgeId; + }); + if (this.currentEdge === undefined) { + deleteSelf(); return; } - this.currentStart = this.currentEdge.otherEnd(this.currentStart); - this.currentEdge = this.currentPath.shift(); this.currentEdge.addChild(this); } if (!Packet.animationPaused) { @@ -166,7 +190,6 @@ export class Packet extends Graphics { const dx = end.x - start.x; const dy = end.y - start.y; - // Move packet this.x = start.x + progress * dx; this.y = start.y + progress * dy; } @@ -194,27 +217,16 @@ export class Packet extends Graphics { export function sendPacket( viewgraph: ViewGraph, packetType: string, - originId: number, - destinationId: number, + originId: DeviceId, + destinationId: DeviceId, ) { console.log( `Sending ${packetType} packet from ${originId} to ${destinationId}`, ); - const pathEdgeIds = viewgraph.getPathBetween(originId, destinationId); - - if (pathEdgeIds.length === 0) { - console.warn( - `No se encontró un camino entre ${originId} y ${destinationId}.`, - ); - return; - } - const originDevice = viewgraph.getDevice(originId); const destinationDevice = viewgraph.getDevice(destinationId); - const pathEdges = pathEdgeIds.map((id) => viewgraph.getEdge(id)); - // TODO: allow user to choose which payload to send let payload; switch (packetType) { @@ -233,6 +245,23 @@ export function sendPacket( destinationDevice.ip, payload, ); - const packet = new Packet(packetType, rawPacket, originId, destinationId); - packet.animateAlongPath(pathEdges, originId); + const packet = new Packet( + viewgraph, + packetType, + rawPacket, + originId, + destinationId, + ); + const originConnections = viewgraph.getConnections(originId); + if (originConnections.length === 0) { + console.warn(`No se encontró un dispositivo con ID ${originId}.`); + return; + } + let firstEdge = originConnections.find((edge) => { + return edge.otherEnd(originId) === destinationId; + }); + if (firstEdge === undefined) { + firstEdge = originConnections[0]; + } + packet.traverseEdge(firstEdge, originId); }