@webwriter/network
Version:
Visualization of network topologies. Can represent different kinds of networks.
558 lines (516 loc) • 25.7 kB
text/typescript
import { NetworkComponent } from '../../..';
import { PacketSimulator } from '../../event-handlers/packet-simulator';
import { AddressingHelper } from '../../utils/AdressingHelper';
import { AlertHelper } from '../../utils/AlertHelper';
import { AnimationHelper } from '../../utils/AnimationHelper';
import { RoutingData } from '../../utils/routingData';
import { TableHelper } from '../../utils/TableHelper';
import { GraphEdge } from '../GraphEdge';
import { Data, Packet, Frame } from '../logicalNodes/DataNode';
import { Net } from '../logicalNodes/Net';
import { Router } from '../physicalNodes/Connector';
import { PhysicalNode } from '../physicalNodes/PhysicalNode';
import { DataHandlingDecorator } from './DataHandlingDecorator';
export class RoutableDecorator extends DataHandlingDecorator {
arpTableMacIp: Map<string, string> = new Map(); //(mac, ip)
arpTableIpMac: Map<string, string> = new Map();
routingTable: Map<string, RoutingData> = new Map(); // (destination-address, routingAttributes)
pendingPackets: Map<string, string[]> = new Map<string, string[]>(); //ip of receiver / list of ids of pending packets
pathsToOtherRouters?: Map<string, string[]> = new Map<string, any>(); //(ip of other router, path from this router to other router)
nets?: Net[];
portNetMapping?: Map<number, Net> = new Map();
constructor(component: PhysicalNode, network: NetworkComponent) {
super(component);
this.cssClass.push('routable-decorated');
if (component instanceof Router) {
this.nets = component.nets;
this.portNetMapping = component.portNetMapping;
}
TableHelper.initTable(this.id, 'ArpTable', network);
TableHelper.initTable(this.id, 'RoutingTable', network);
}
handleDataIn(dataNode: any, previousNode: any, network: NetworkComponent): void {
let data = dataNode.data();
let receiverMac: string = data.layer2header.macReceiver;
let receiverIp: string = data instanceof Packet ? data.layer3header.ipReceiver : data.layer2header.ipReceiver;
let portIn: number = this.getPortIn(previousNode.data('id'), network)!;
let thisMac: string = this.portData.get(portIn)!.get('MAC').address;
let thisIp: string = this.portData.get(portIn)!.get('IPv4').address;
if (receiverMac != '' && receiverMac != 'FF:FF:FF:FF:FF:FF' && receiverMac != thisMac) {
if (data instanceof Frame)
AnimationHelper.blinkingThenRemoveNode('discard-data-node-2part', dataNode.id(), network);
if (data instanceof Packet)
AnimationHelper.blinkingThenRemoveNode('discard-data-node-3part', dataNode.id(), network);
return;
}
if (data instanceof Data) {
if (data instanceof Frame && data.name.includes('ARP res')) {
console.log('arp res', this);
this.populateRoutingTableOnNewData(previousNode.id(), dataNode, network);
this.populateArpTable(data.layer2header.ipSender, data.layer2header.macSender, network);
} else if (data instanceof Frame && data.name.includes('ARP req')) {
if (thisIp != receiverIp) {
AnimationHelper.blinkingThenRemoveNode('discard-data-node-2part', dataNode.id(), network);
return;
} else {
console.log('arp req', this);
this.populateRoutingTableOnNewData(previousNode.id(), dataNode, network);
this.receiveArpRequest(
portIn,
data.layer2header.macSender,
data.layer2header.ipSender,
data.layer2header.ipReceiver,
network
);
}
} else if (data instanceof Packet) {
if (receiverIp == thisIp) {
this.populateRoutingTableOnNewData(previousNode.id(), dataNode, network);
if (this.checkIfSameNetwork(thisIp, data.layer3header.ipSender, network)) {
this.populateArpTable(data.layer3header.ipSender, data.layer2header.macSender, network);
}
AnimationHelper.blinkingThenRemoveNode('processing-data-node-3part', dataNode.id(), network);
AlertHelper.toastAlert('success', 'check2-all', '', 'Your receiver has received the message!');
} else if (this.cssClass.includes('router-node')) {
this.removeLayer2Header(data, network);
let portToSend = this.findPortToSend(data.layer3header.ipReceiver);
if (portToSend != null) {
let ipReceiver = data.layer3header.ipReceiver;
let macSender = this.portData.get(portToSend)!.get('MAC').address;
if (this.arpTableIpMac.has(ipReceiver)) {
if (data instanceof Packet) {
let macReceiver: string = this.arpTableIpMac.get(ipReceiver)!;
this.addLayer2Header(data, macSender, macReceiver, network);
network.packetSimulator.findNextHopThenSend(
portIn,
network._graph.$('#' + this.id),
dataNode,
network
);
}
} else {
let nextGateway = this.getNextGateway(data.layer3header.ipReceiver);
if (nextGateway != null) {
console.log(nextGateway);
// PacketSimulator.directSend(
// network._graph.$('#' + this.id),
// network._graph.$('#' + network.ipv4Database.get(nextGateway)),
// dataNode,
// network
// );
const nextGatewayData = network._graph
.$('#' + network.ipv4Database.get(nextGateway))
.data() as Router;
const gateway = nextGatewayData as Router;
this.sendDataToAnotherNetwork(
portIn,
dataNode,
macSender,
this.portData.get(portToSend)!.get('IPv4').address,
'',
data.layer3header.ipReceiver,
nextGateway,
network
);
} else {
data.layer2header.macSender = this.portData.get(portToSend)!.get('MAC').address;
this.sendDataInSameNetwork(
portIn,
dataNode,
macSender,
data.layer3header.ipSender,
'',
ipReceiver,
network
);
}
}
}
}
}
}
}
sendData(dataNode: any, network: NetworkComponent, senderNode?: any): void {
let data: Packet = dataNode.data();
let receiverIp = data.layer3header.ipReceiver;
let net: Net = senderNode.parent().data() as Net;
//check if in same network
if (
net.networkAddress.binaryOctets.join('').slice(0, net.bitmask) ==
AddressingHelper.decimalStringWithDotToBinary(receiverIp).slice(0, net.bitmask)
) {
this.sendDataInSameNetwork(
null,
dataNode,
this.getMacProvidedIp(data.layer3header.ipSender),
data.layer3header.ipSender,
'',
data.layer3header.ipReceiver,
network
);
} else {
console.log('sending to another network', dataNode.id(), this.defaultGateway);
this.sendDataToAnotherNetwork(
null,
dataNode,
this.getMacProvidedIp(data.layer3header.ipSender),
data.layer3header.ipSender,
'',
data.layer3header.ipReceiver,
network._graph
.$('#' + this.defaultGateway[0])
.data()
.portData.get(+this.defaultGateway[1])
.get('IPv4').address,
network
);
}
}
sendArpRequest(
portIn: number,
macSender: string,
ipSender: string,
ipReceiver: string,
network: NetworkComponent
): void {
let arpRequest: Frame = Frame.createArpRequest(network.currentColor, macSender, ipSender, ipReceiver);
console.log('sending arp request', arpRequest, ipSender, network.ipv4Database.get(ipSender));
network.packetSimulator.initMessage(
network._graph.$('#' + network.ipv4Database.get(ipSender)),
arpRequest,
network
);
let arpNode = network._graph.$('#' + arpRequest.id);
this.sendDataInSameNetwork(
portIn,
arpNode,
macSender,
ipSender,
arpRequest.layer2header.macReceiver,
ipReceiver,
network
);
}
receiveArpRequest(
portIn: number,
macSender: string,
ipSender: string,
ipReceiver: string,
network: NetworkComponent
): void {
let macReceiver: string = this.portData.get(portIn).get('MAC').address;
this.populateArpTable(ipSender, macSender, network);
let arpRes: Frame = Frame.createArpResponse(network.currentColor, macReceiver, macSender, ipReceiver, ipSender);
network.packetSimulator.initMessage(network._graph.$('#' + this.id), arpRes, network);
let arpNode = network._graph.$('#' + arpRes.id);
this.sendDataInSameNetwork(portIn, arpNode, macReceiver, ipReceiver, macSender, ipSender, network);
}
sendDataInSameNetwork(
lastPortIn: number,
dataNode: any,
macSender: string,
ipSender: string,
macReceiver: string,
ipReceiver: string,
network: NetworkComponent
): void {
let thisNode = network._graph.$('#' + this.id);
let data = dataNode.data();
console.log(
'sending data in same network',
dataNode.id(),
this.id,
macSender,
ipSender,
macReceiver,
ipReceiver,
thisNode.data()
);
if (
!(thisNode.data() as RoutableDecorator).checkIfSameNetwork(
this.getIpProvidedMac(macSender),
ipReceiver,
network
)
) {
console.log('not in same network');
return;
}
if (macReceiver == 'FF:FF:FF:FF:FF:FF') {
this.flood(dataNode, null, lastPortIn, network);
} else if (this.arpTableMacIp.has(macReceiver) || this.arpTableIpMac.has(ipReceiver)) {
if (data instanceof Frame) {
network.packetSimulator.findNextHopThenSend(lastPortIn, thisNode, dataNode, network);
} else if (data instanceof Packet) {
let macReceiver: string = this.arpTableIpMac.get(ipReceiver);
let port = this.findPortToSend(ipReceiver);
let macSender: string = this.portData.get(port).get('MAC').address;
this.addLayer2Header(data, macSender, macReceiver, network);
network.packetSimulator.findNextHopThenSend(lastPortIn, thisNode, dataNode, network);
}
} else if (this.routingTable.has(ipReceiver) && data.name.includes('ARP res')) {
if (data instanceof Frame) {
network.packetSimulator.findNextHopThenSend(lastPortIn, thisNode, dataNode, network);
} else if (data instanceof Packet) {
let macReceiver: string = this.arpTableIpMac.get(ipReceiver);
let port = this.findPortToSend(ipReceiver);
let macSender: string = this.portData.get(port).get('MAC').address;
this.addLayer2Header(data, macSender, macReceiver, network);
network.packetSimulator.findNextHopThenSend(lastPortIn, thisNode, dataNode, network);
}
} else {
this.sendArpRequest(lastPortIn, macSender, this.getIpProvidedMac(macSender), ipReceiver, network);
if (!this.pendingPackets.has(ipReceiver)) this.pendingPackets.set(ipReceiver, []);
this.pendingPackets.get(ipReceiver).push(dataNode.id());
}
}
sendDataToAnotherNetwork(
lastPortIn: number,
dataNode: any,
macSender: string,
ipSender: string,
macReceiver: string,
ipReceiver: string,
gatewayIp: string,
network: NetworkComponent
): void {
let senderNode = network._graph.$('#' + network.ipv4Database.get(ipSender));
let receiverNode = network._graph.$('#' + network.ipv4Database.get(ipReceiver));
if ((senderNode.data() as RoutableDecorator).checkIfSameNetwork(ipSender, ipReceiver, network)) return;
if (this.arpTableIpMac.has(gatewayIp)) {
this.sendDataInSameNetwork(
lastPortIn,
dataNode,
macSender,
ipSender,
this.arpTableMacIp.get(gatewayIp),
gatewayIp,
network
);
} else {
this.sendArpRequest(lastPortIn, macSender, ipSender, gatewayIp, network);
if (!this.pendingPackets.has(gatewayIp)) this.pendingPackets.set(gatewayIp, []);
this.pendingPackets.get(gatewayIp).push(dataNode.id());
}
}
addLayer2Header(data: Packet, macSender: string, macReceiver: string, network: NetworkComponent): void {
data.layer2header.macSender = macSender;
data.layer2header.macReceiver = macReceiver;
data.name = 'L2 L3 DATA';
network._graph.$('#' + data.id).toggleClass('data-node-layer2-layer3', true);
network._graph.$('#' + data.id).toggleClass('data-node-layer3', false);
}
removeLayer2Header(data: Packet, network: NetworkComponent): void {
data.layer2header.macSender = '';
data.layer2header.macReceiver = '';
this.name = 'L3 DATA';
network._graph.$('#' + data.id).toggleClass('data-node-layer2-layer3', false);
network._graph.$('#' + data.id).toggleClass('data-node-layer3', true);
}
populateArpTable(ip: string, mac: string, network: NetworkComponent): void {
this.arpTableMacIp.set(mac, ip);
this.arpTableIpMac.set(ip, mac);
//TableHelper.addRow('arp-table-' + this.id, "ArpTable", network, [ip, mac]);
TableHelper.reloadTable('arp-table-' + this.id, 'ArpTable', this.arpTableIpMac, network);
//send out pending Packets after getting ARP table populated
if (this.pendingPackets.has(ip)) {
this.pendingPackets.get(ip).forEach((dataId) => {
let dataNode = network._graph.$('#' + dataId);
let data: Packet = dataNode.data() as Packet;
let source: PhysicalNode = network._graph
.$('#' + network.ipv4Database.get(data.layer3header.ipSender))
.data();
let portToSend: number = null;
source.portData.forEach((value, port) => {
if (value.get('IPv4').address == data.layer3header.ipSender) {
portToSend = port;
}
});
this.sendDataInSameNetwork(
portToSend,
dataNode,
data.layer2header.macSender,
data.layer3header.ipSender,
mac,
ip,
network
);
});
this.pendingPackets.set(ip, []);
}
}
populateRoutingTableOnNewData(previousId: string, dataNode: any, network: NetworkComponent): void {
let data = dataNode.data();
let portIn = this.getPortIn(previousId, network);
if (this.cssClass.includes('router-node')) {
if (data instanceof Frame && data.name.includes('ARP res')) {
AnimationHelper.blinkingThenRemoveNode('discard-data-node-2part', dataNode.id(), network);
} else if (data instanceof Frame && data.name.includes('ARP req')) {
AnimationHelper.blinkingThenRemoveNode('processing-data-node-2part', dataNode.id(), network);
} else {
AnimationHelper.blinkingThenRemoveNode('discard-data-node-3part', dataNode.id(), network);
}
return;
}
console.log('populate routing table', previousId, dataNode, data.constructor.name);
if (data instanceof Frame) {
let destination: string = dataNode.data().layer2header.ipSender;
if (this.checkIfSameNetwork(this.portData.get(portIn).get('IPv4').address, destination, network)) {
let routingData = new RoutingData(
destination,
'on-link',
32,
this.portData.get(portIn).get('Name'),
portIn
);
this.routingTable.set(destination, routingData);
// TableHelper.addRow('routing-table-' + this.id, "RoutingTable", network, [routingData.destination, routingData.gateway, routingData.bitmask,
// routingData.port]);
TableHelper.reloadTable('routing-table-' + this.id, 'RoutingTable', this.routingTable, network);
}
AnimationHelper.blinkingThenRemoveNode('processing-data-node-2part', dataNode.id(), network);
} else if (data instanceof Packet) {
let senderIp = data.layer3header.ipSender;
let gatewayIp = this.arpTableMacIp.get(data.layer2header.macSender);
let routingData: RoutingData;
if (this.checkIfSameNetwork(data.layer3header.ipReceiver, senderIp, network)) {
routingData = new RoutingData(senderIp, 'on-link', 32, this.portData.get(portIn).get('Name'), portIn);
} else {
routingData = new RoutingData(senderIp, gatewayIp, 32, this.portData.get(portIn).get('Name'), portIn);
}
this.routingTable.set(senderIp, routingData);
// TableHelper.addRow('routing-table-' + this.id, "RoutingTable", network, [routingData.destination, routingData.gateway, routingData.bitmask,
// routingData.port]);
TableHelper.reloadTable('routing-table-' + this.id, 'RoutingTable', this.routingTable, network);
AnimationHelper.blinkingThenRemoveNode('processing-data-node-3part', dataNode.id(), network);
}
}
//longest match
findPortToSend(ip: string): number {
console.log('finding port to send', ip, this);
let longestPrefixMatch: number = 0;
let port: number;
this.routingTable.forEach((attributes, address) => {
let numOfMatchedPrefix = AddressingHelper.getPrefix([
AddressingHelper.decimalStringWithDotToBinary(address),
AddressingHelper.decimalStringWithDotToBinary(ip),
]).length;
if (
numOfMatchedPrefix >= attributes.bitmask &&
(attributes.bitmask > longestPrefixMatch || attributes.bitmask == longestPrefixMatch)
) {
longestPrefixMatch = attributes.bitmask;
port = attributes.port;
}
});
console.log('found port', port);
return port;
}
getNextGateway(ip: string): string {
let nextHop: string = null;
let longestPrefixMatch: number = 0;
let port: number;
this.routingTable.forEach((attributes, address) => {
let numOfMatchedPrefix = AddressingHelper.getPrefix([
AddressingHelper.decimalStringWithDotToBinary(address),
AddressingHelper.decimalStringWithDotToBinary(ip),
]).length;
if (
numOfMatchedPrefix >= attributes.bitmask &&
(attributes.bitmask > longestPrefixMatch || attributes.bitmask == longestPrefixMatch)
) {
if (attributes.gateway != 'on-link' && attributes.gateway != '') nextHop = attributes.gateway;
longestPrefixMatch = attributes.bitmask;
port = attributes.port;
}
});
return nextHop;
}
getMacProvidedIp(ip: string): string {
let mac = null;
this.portData.forEach((value) => {
if (value.get('IPv4').address == ip) mac = value.get('MAC').address;
});
return mac;
}
getIpProvidedMac(mac: string): string {
let ip = null;
this.portData.forEach((value) => {
if (value.get('MAC').address == mac) ip = value.get('IPv4').address;
});
return ip;
}
checkIfSameNetwork(source: string, destination: string, network: NetworkComponent): boolean {
let destId = network.ipv4Database.get(destination);
let thisNode = network._graph.$('#' + this.id);
let destNode = network._graph.$('#' + destId);
let net: Net;
console.log('checking if same network', source, destination, thisNode.data(), destNode.data());
if (!thisNode.isOrphan()) {
net = network._graph
.$('#' + this.id)
.parent()
.data() as Net;
if (
net.networkAddress.binaryOctets.join('').slice(0, net.bitmask) ==
AddressingHelper.decimalStringWithDotToBinary(destination).slice(0, net.bitmask)
) {
return true;
} else {
return false;
}
} else if (!destNode.isOrphan()) {
net = network._graph
.$('#' + destId)
.parent()
.data() as Net;
if (
net.networkAddress.binaryOctets.join('').slice(0, net.bitmask) ==
AddressingHelper.decimalStringWithDotToBinary(source).slice(0, net.bitmask)
) {
return true;
} else {
return false;
}
} else if (
thisNode.data().cssClass.includes('router-node') &&
destNode.data().cssClass.includes('router-node')
) {
return true;
} else {
return false;
}
}
flood(dataNode: any, previousId: string, port: number, network: NetworkComponent): void {
dataNode = dataNode.remove();
this.portLinkMapping.forEach((linkId, portIn) => {
if (linkId != null && linkId != undefined && linkId != '') {
let edge: GraphEdge = network._graph.$('#' + linkId).data();
let directTargetId = edge.target == this.id ? edge.source : edge.target;
let nextHop = network._graph.$('#' + directTargetId);
let destination: string =
dataNode.data() instanceof Frame
? dataNode.data('layer2header').ipReceiver
: dataNode.data('layer3header').ipReceiver;
if (port == portIn || edge.target == previousId || edge.source == previousId) {
//do not flood the incoming port
} else if (
!this.checkIfSameNetwork(this.portData.get(portIn).get('IPv4').address, destination, network)
) {
//do not flood other network
} else {
let newData =
dataNode.data() instanceof Packet
? Packet.cloneData(dataNode.data())
: Frame.cloneData(dataNode.data());
network.packetSimulator.initThenDirectSend(
network._graph.$('#' + this.id),
nextHop,
newData,
network
);
}
}
});
}
}