UNPKG

@webwriter/network

Version:

Visualization of network topologies. Can represent different kinds of networks.

515 lines (457 loc) 20.2 kB
import { SlInput, SlButton } from '@shoelace-style/shoelace'; import { NetworkComponent } from '../..'; import { Ipv4Address } from '../adressing/Ipv4Address'; import { Ipv6Address } from '../adressing/Ipv6Address'; import { MacAddress } from '../adressing/MacAddress'; import { Net } from '../components/logicalNodes/Net'; import { Repeater, Hub, Switch, Bridge, AccessPoint, Router } from '../components/physicalNodes/Connector'; import { Host } from '../components/physicalNodes/Host'; import { ConnectionType, PhysicalNode } from '../components/physicalNodes/PhysicalNode'; import { initNetwork } from '../network-config'; import { AddressingHelper } from '../utils/AdressingHelper'; import { biPcDisplayHorizontal, biPhone, iconToDataURI } from '../styles/icons'; interface ComponentData { componentType: string; color?: string; } interface PhysicalNodeData extends ComponentData { interfaces: Array<InterfaceData>; } interface NetNodeData extends ComponentData { net: NetData; } interface InterfaceData { name?: string; connectionType?: ConnectionType; mac?: string; ipv4?: string; ipv6?: string; } interface NetData { netid: string; netmask: string; bitmask: number; } export class GraphNodeFactory { static addNode(network: NetworkComponent, data: PhysicalNodeData | NetNodeData): void { if (data.componentType == '' || data.componentType == null) { return; } if (!network.networkAvailable) { initNetwork(network); } switch (data.componentType) { case 'edge': //edge is not added with the "plus-button" break; case 'net': const netData = data as NetNodeData; this.addNetNodeData(network, netData); // this.addNetNode(network); break; default: //default is adding physical node const nodeData = data as PhysicalNodeData; if (!nodeData.interfaces || nodeData.interfaces.length == 0) { nodeData.interfaces = []; } this.addPhysicalNodeData(network, nodeData); break; } network._graph.center(); } private static addNetNode(network: NetworkComponent): void { let netId: string = (network.renderRoot.querySelector('#net-num') as SlInput).value.trim(); let netmask: string = (network.renderRoot.querySelector('#net-mask') as SlInput).value.trim(); let bitmask: number = (network.renderRoot.querySelector('#net-bitmask') as SlInput).valueAsNumber; let newNet = Net.createNet(network.currentColor, netId, netmask, bitmask, network.ipv4Database, network); if (newNet != null) { network._graph.add({ group: 'nodes', data: newNet, classes: newNet.cssClass, }); } } private static addNetNodeData(network: NetworkComponent, data: NetNodeData): void { let netid: string = data.net?.netid || ''; let netmask: string = data.net?.netmask || ''; let bitmask: number = data.net?.bitmask || 0; let color: string = data.color || '#70e6af'; let newNet = Net.createNet(color, netid, netmask, bitmask, network.ipv4Database, network); if (newNet != null) { network._graph.add({ group: 'nodes', data: newNet, classes: newNet.cssClass, }); } } private static addPhysicalNodeData(network: NetworkComponent, data: PhysicalNodeData): void { let numberOfPorts: number = data.interfaces.length; let color: string = data.color || '#70e6af'; let names: Map<number, string> = new Map(); let portConnectionTypes: Map<number, ConnectionType> = new Map(); let portMacs: Map<number, MacAddress> = new Map(); let portIpv4s: Map<number, Ipv4Address> = new Map(); let portIpv6s: Map<number, Ipv6Address> = new Map(); for (let index = 1; index <= numberOfPorts; index++) { let name: string = data.interfaces[index - 1].name || ''; if (name != '') names.set(index, name); let connectionType: ConnectionType = data.interfaces[index - 1].connectionType || 'ethernet'; portConnectionTypes.set(index, connectionType); let macAddress: MacAddress = MacAddress.validateAddress(data.interfaces[index - 1].mac, network.macDatabase) || MacAddress.generateRandomAddress(network.macDatabase); portMacs.set(index, macAddress); switch (data.componentType) { case 'router': case 'computer': case 'mobile': let ipv4: Ipv4Address = Ipv4Address.validateAddress(data.interfaces[index - 1].ipv4, network.ipv4Database) || Ipv4Address.getLoopBackAddress(); let ipv6: Ipv6Address = Ipv6Address.validateAddress(data.interfaces[index - 1].ipv6, network.ipv6Database) || Ipv6Address.getLoopBackAddress(); portIpv4s.set(index, ipv4); portIpv6s.set(index, ipv6); break; } } let component: PhysicalNode; switch (data.componentType) { //connectors //layer 1 case 'repeater': component = new Repeater(color, portConnectionTypes, name); break; case 'hub': component = new Hub(color, numberOfPorts, name); break; //layer 2 case 'switch': component = new Switch(color, numberOfPorts, portMacs, name); break; case 'bridge': component = new Bridge(color, portConnectionTypes, portMacs, name); break; case 'access-point': component = new AccessPoint(color, numberOfPorts, portMacs, name); break; //layer 3 case 'router': component = new Router( color, numberOfPorts, names, portConnectionTypes, portMacs, portIpv4s, portIpv6s, name ); break; //host case 'computer': component = new Host( color, iconToDataURI(biPcDisplayHorizontal), numberOfPorts, names, portConnectionTypes, portMacs, portIpv4s, portIpv6s, name ); break; case 'mobile': component = new Host( color, iconToDataURI(biPhone), numberOfPorts, names, portConnectionTypes, portMacs, portIpv4s, portIpv6s, name ); break; default: return; } if (component.layer >= 2) { portMacs.forEach((mac) => { MacAddress.addAddressToDatabase(mac, network.macDatabase, component.id); }); } if (component.layer >= 3) { portIpv4s.forEach((ip4) => { Ipv4Address.addAddressToDatabase(ip4, network.ipv4Database, component.id); }); portIpv6s.forEach((ip6) => { Ipv6Address.addAddressToDatabase(ip6, network.ipv6Database, component.id); }); } network._graph.add({ group: 'nodes', data: component, position: { x: 10, y: 10 }, classes: component.cssClass, }); } private static addPhysicalNode(network: NetworkComponent): void { let name: string = (network.renderRoot.querySelector('#inputName') as SlInput).value.trim(); let inputNumOfPorts: SlInput = network.renderRoot.querySelector('#ports') as SlInput; let numberOfPorts: number = inputNumOfPorts.value != '' ? inputNumOfPorts.valueAsNumber : network.currentComponentToAdd == 'computer' || network.currentComponentToAdd == 'mobile' ? 1 : 2; let names: Map<number, string> = new Map(); let portConnectionTypes: Map<number, ConnectionType> = new Map(); let portMacs: Map<number, MacAddress> = new Map(); let portIpv4s: Map<number, Ipv4Address> = new Map(); let portIpv6s: Map<number, Ipv6Address> = new Map(); for (let index = 1; index <= numberOfPorts; index++) { let inputInterfaceName: SlInput = network.renderRoot.querySelector('#interface-name-' + index) as SlInput; let name: string = inputInterfaceName != null && inputInterfaceName.value ? inputInterfaceName.value : ''; let inputConnection: SlInput = network.renderRoot.querySelector('#connection-type-' + index) as SlInput; let inputMac: SlInput = network.renderRoot.querySelector('#mac-' + index) as SlInput; let inputIpv4: SlInput = network.renderRoot.querySelector('#ip4-' + index) as SlInput; let inputIpv6: SlInput = network.renderRoot.querySelector('#ip6-' + index) as SlInput; let connectionType: ConnectionType = inputConnection != null ? (inputConnection.value == 'wireless' ? 'wireless' : 'ethernet') : 'ethernet'; switch (network.currentComponentToAdd) { case 'switch': case 'bridge': case 'access-point': let mac: MacAddress = inputMac == null ? MacAddress.generateRandomAddress(network.macDatabase) : MacAddress.validateAddress(inputMac.value, network.macDatabase); mac = mac != null ? mac : MacAddress.generateRandomAddress(network.macDatabase); portMacs.set(index, mac); break; case 'router': case 'computer': case 'mobile': let macAddress: MacAddress = inputMac == null ? MacAddress.generateRandomAddress(network.macDatabase) : MacAddress.validateAddress(inputMac.value, network.macDatabase); macAddress = macAddress != null ? macAddress : MacAddress.generateRandomAddress(network.macDatabase); let ipv4: Ipv4Address = inputIpv4 == null ? Ipv4Address.getLoopBackAddress() : Ipv4Address.validateAddress(inputIpv4.value, network.ipv4Database); ipv4 = ipv4 != null ? ipv4 : Ipv4Address.getLoopBackAddress(); let ipv6: Ipv6Address = inputIpv6 == null ? Ipv6Address.getLoopBackAddress() : Ipv6Address.validateAddress(inputIpv6.value, network.ipv6Database); ipv6 = ipv6 != null ? ipv6 : Ipv6Address.getLoopBackAddress(); portMacs.set(index, macAddress); portIpv4s.set(index, ipv4); portIpv6s.set(index, ipv6); break; } if (name != '') names.set(index, name); portConnectionTypes.set(index, connectionType); } let component: PhysicalNode; switch (network.currentComponentToAdd) { //connectors //layer 1 case 'repeater': component = new Repeater(network.currentColor, portConnectionTypes, name); break; case 'hub': component = new Hub(network.currentColor, numberOfPorts, name); break; //layer 2 case 'switch': component = new Switch(network.currentColor, numberOfPorts, portMacs, name); break; case 'bridge': component = new Bridge(network.currentColor, portConnectionTypes, portMacs, name); break; case 'access-point': component = new AccessPoint(network.currentColor, numberOfPorts, portMacs, name); break; //layer 3 case 'router': component = new Router( network.currentColor, numberOfPorts, names, portConnectionTypes, portMacs, portIpv4s, portIpv6s, name ); break; //host case 'computer': component = new Host( network.currentColor, 'pc-display-horizontal', numberOfPorts, names, portConnectionTypes, portMacs, portIpv4s, portIpv6s, name ); break; case 'mobile': component = new Host( network.currentColor, 'phone', numberOfPorts, names, portConnectionTypes, portMacs, portIpv4s, portIpv6s, name ); break; default: break; } if (component.layer >= 2) { portMacs.forEach((mac) => { MacAddress.addAddressToDatabase(mac, network.macDatabase, component.id); }); } if (component.layer >= 3) { portIpv4s.forEach((ip4) => { Ipv4Address.addAddressToDatabase(ip4, network.ipv4Database, component.id); }); portIpv6s.forEach((ip6) => { Ipv6Address.addAddressToDatabase(ip6, network.ipv6Database, component.id); }); } network._graph.add({ group: 'nodes', data: component, position: { x: 10, y: 10 }, classes: component.cssClass, }); } static removeNode(node: any, network: NetworkComponent): void { if (node.hasClass('net-node')) { this.removeNet(node, network); } else if (node.hasClass('gateway-node')) { this.removeGateway(node, network); } else { let physicalNode: PhysicalNode = node.data(); if (physicalNode.layer > 2) { physicalNode.portData.forEach((data) => { network.ipv4Database.delete(data.get('IPv4').address); }); } } } static removeGateway(node: any, network: NetworkComponent): void { let gateway: Router = node.data(); gateway.portNetMapping.forEach((net, port) => { net.gateways.delete(gateway.id); if ( net.currentDefaultGateway != undefined && net.currentDefaultGateway != null && net.currentDefaultGateway[0] == gateway.id && net.currentDefaultGateway[1] == port ) { if (net.gateways.size > 0) { let iter = net.gateways.entries(); let temp = iter.next(); while (temp.value != undefined && temp.value[1] == null) { temp = iter.next(); } if (temp.value != undefined && temp.value[1] != null) { net.currentDefaultGateway = temp.value; } else { net.currentDefaultGateway = null; } } else { net.currentDefaultGateway = null; } } network._graph .$('#' + net.id) .children() .forEach((child) => { let oldGateway = child.data('defaultGateway'); if ( oldGateway != null && oldGateway != undefined && oldGateway[0] == node.id() && oldGateway[1] == port ) { if (net.currentDefaultGateway != null && net.currentDefaultGateway != undefined) { child.data('defaultGateway', net.currentDefaultGateway); } else { child.data('defaultGateway', null); child.toggleClass('default-gateway-not-found', true); if (!child.data('cssClass').includes('default-gateway-not-found')) child.data('cssClass').push('default-gateway-not-found'); } } }); }); gateway.portData.forEach((data) => { network.ipv4Database.delete(data.get('IPv4').address); }); } static removeNet(node: any, network: NetworkComponent): void { //free addresses of all children node.children().forEach((child) => { this.removeNode(child, network); }); let net: Net = node.data() as Net; if (net.networkAddress != null && net.networkAddress != undefined) { network.ipv4Database.delete(AddressingHelper.getBroadcastAddress(net.networkAddress.address, net.bitmask)); //remove the broadcast address network.ipv4Database.delete(net.networkAddress.address); //free ID of network } //free the port of the gateways net.gateways.forEach((port, gatewayId) => { let gateway: Router = network._graph.$('#' + gatewayId).data(); if (port != null && port != undefined && !Number.isNaN(port)) { gateway.portNetMapping.set(port, null); gateway.portLinkMapping.set(port, null); } gateway.nets.forEach((nw, index, array) => { if (nw.id == net.id) { array.splice(index, 1); } }); if (gateway.nets.length == 0) network._graph.$('#' + gatewayId).toggleClass('gateway-node', false); //if the gateway has no networks --> back to router-node }); } static toggleResetColor(network: NetworkComponent): void { let changeColorHandler = function (event: any) { let node = event.target; node.data('color', network.currentColor); }; if (!network.resetColorModeOn) { network._graph.on('tap', changeColorHandler); (network.renderRoot.querySelector('#resetColorBtn') as SlButton).name = 'pause'; (network.renderRoot.querySelector('#resetColorBtn') as HTMLElement).style.backgroundColor = '#0291DB'; (network.renderRoot.querySelector('#drawBtn') as HTMLButtonElement).disabled = true; } else { // just remove handler network._graph.removeListener('tap'); (network.renderRoot.querySelector('#resetColorBtn') as SlButton).name = 'eyedropper'; (network.renderRoot.querySelector('#resetColorBtn') as HTMLElement).style.backgroundColor = '#8BA8CC'; (network.renderRoot.querySelector('#drawBtn') as HTMLButtonElement).disabled = false; } network.resetColorModeOn = !network.resetColorModeOn; } }