mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-10 07:44:50 +01:00
268 lines
9 KiB
JavaScript
268 lines
9 KiB
JavaScript
/*
|
|
* Copyright (c) 2025 Genexis B.V. All rights reserved.
|
|
*
|
|
* This Software and its content are protected by the Dutch Copyright Act
|
|
* ('Auteurswet'). All and any copying and distribution of the software
|
|
* and its content without authorization by Genexis B.V. is
|
|
* prohibited. The prohibition includes every form of reproduction and
|
|
* distribution.
|
|
*
|
|
*/
|
|
|
|
import { getUciByType } from '../uci.js';
|
|
import { getBridgePortType, getTPIDFromDeviceType } from './common.js';
|
|
|
|
// find the port from bridge-vlan; returns [vlanId, isTagged, isPvid] or null
|
|
function findPortFromBridgeVlan(bridgeVlans, portName) {
|
|
if (!bridgeVlans) return null;
|
|
|
|
for (const bridgeVlan of bridgeVlans) {
|
|
const port = bridgeVlan.ports?.find(x => x.split(':')[0] === portName);
|
|
if (port) {
|
|
const flags = port.includes(':') ? port.split(':')[1] : '';
|
|
return [bridgeVlan.vlan, flags.includes('t'), flags.includes('*')];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function isVLANSubInterface(portName, ethPorts) {
|
|
const names = portName.split('.');
|
|
if (names.length > 1) {
|
|
const baseIfname = names.slice(0, -1).join('.');
|
|
if (ethPorts.find(x => x.ifname === baseIfname)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function createProviderBridge(dev, type, ethIndex, bridges, providerBridges, cVLANBridgeIndex) {
|
|
const briIndex = bridges.length + 1;
|
|
|
|
let sVLANBridgeIndex = bridges.findIndex((x) => x['VLAN.']?.[0]?.VLANID === dev.vid && x['Port.']?.[0]?.LowerLayers === `Device.Ethernet.Interface.${ethIndex}`);
|
|
if (sVLANBridgeIndex < 0) {
|
|
// no management port needed for provider bridge
|
|
bridges.push({
|
|
Name: dev.name,
|
|
Alias: `cpe-${dev.name}`,
|
|
Standard: '802.1Q-2011',
|
|
Enable: 1,
|
|
'Port.': [{
|
|
Enable: 1,
|
|
Name: dev.name,
|
|
Alias: `cpe-${dev.name}`,
|
|
TPID: 34984,
|
|
PVID: 1,
|
|
Type: 'ProviderNetworkPort',
|
|
LowerLayers: `Device.Ethernet.Interface.${ethIndex}`,
|
|
}],
|
|
'VLAN.': [{
|
|
Enable: 1,
|
|
VLANID: dev.vid,
|
|
}],
|
|
'VLANPort.': [{
|
|
Enable: 1,
|
|
VLAN: `Device.Bridging.Bridge.${briIndex}.VLAN.1`,
|
|
Port: `Device.Bridging.Bridge.${briIndex}.Port.1`,
|
|
Untagged: 1,
|
|
}],
|
|
_key: dev['.name'],
|
|
});
|
|
sVLANBridgeIndex = bridges.length;
|
|
} else {
|
|
sVLANBridgeIndex = sVLANBridgeIndex + 1;
|
|
const pvBridge = providerBridges.find(x => x.SVLANcomponent === `Device.Bridging.Bridge.${sVLANBridgeIndex}`);
|
|
if (pvBridge) {
|
|
pvBridge.CVLANcomponents = pvBridge.CVLANcomponents + `,Device.Bridging.Bridge.${cVLANBridgeIndex}`;
|
|
return;
|
|
}
|
|
}
|
|
|
|
providerBridges.push({
|
|
Alias: `cpe-${dev.name}`,
|
|
Enable: 1,
|
|
Type: type,
|
|
SVLANcomponent: `Device.Bridging.Bridge.${sVLANBridgeIndex}`,
|
|
CVLANcomponents: `Device.Bridging.Bridge.${cVLANBridgeIndex}`,
|
|
_key: dev['.name'],
|
|
});
|
|
}
|
|
|
|
function addRegularEthernetPort(ethDevice, portIndex, briPorts) {
|
|
const tpid = getTPIDFromDeviceType(ethDevice.type, ethDevice.tpid);
|
|
|
|
briPorts.push({
|
|
Enable: 1,
|
|
Name: ethDevice['ifname'],
|
|
Alias: `cpe-${ethDevice['.name']}`,
|
|
TPID: tpid,
|
|
PVID: 1,
|
|
Type: 'VLANUnawarePort',
|
|
LowerLayers: `Device.Ethernet.Interface.${portIndex + 1}`,
|
|
_key: ethDevice['.name'],
|
|
});
|
|
}
|
|
|
|
|
|
function handleVlanDevice(bridgeIndex, device, ethPorts, devices, bridges, briPorts, briVLAN, briVLANPort, providerBridges) {
|
|
let qinqDev;
|
|
let ethIndex = ethPorts.findIndex(x => device.ifname === x.ifname);
|
|
|
|
if (device.type === '8021ad') {
|
|
if (ethIndex < 0) {
|
|
_log_error('base ethernet device not found', device.ifname);
|
|
return;
|
|
}
|
|
createProviderBridge(device, 'S-VLAN', ethIndex + 1, bridges, providerBridges, bridgeIndex);
|
|
return;
|
|
}
|
|
|
|
if (ethIndex < 0) {
|
|
qinqDev = devices.find(x => x.name === device.ifname);
|
|
if (!qinqDev || !qinqDev.ifname) {
|
|
_log_error('device ifname not found', device.ifname);
|
|
return;
|
|
}
|
|
if (qinqDev.type !== '8021ad' || device.type !== '8021q') {
|
|
_log_error('invalid qinq device type', qinqDev['.name'], device['.name']);
|
|
return;
|
|
}
|
|
device.ifname = qinqDev.ifname;
|
|
ethIndex = ethPorts.findIndex(x => device.ifname === x.ifname);
|
|
if (ethIndex < 0) {
|
|
_log_error('base ethernet device not found', device.ifname);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (device.type !== '8021q') {
|
|
_log_error('unsupported device type', device['.name'], device.type);
|
|
return;
|
|
}
|
|
|
|
let vlanIndex = briVLAN.findIndex(x => Number(x.VLANID) === Number(device.vid));
|
|
if (vlanIndex < 0) {
|
|
briVLAN.push({ Enable: 1, VLANID: Number(device.vid) });
|
|
vlanIndex = briVLAN.length;
|
|
} else {
|
|
vlanIndex += 1;
|
|
}
|
|
|
|
const portType = qinqDev ? 'CustomerEdgePort' : getBridgePortType(device.type);
|
|
const tpid = getTPIDFromDeviceType(device.type, device.tpid);
|
|
|
|
briPorts.push({
|
|
Enable: 1,
|
|
Name: device['ifname'],
|
|
Alias: `cpe-${device['.name']}`,
|
|
TPID: tpid,
|
|
PVID: device.pvid ? Number(device.vid): 1,
|
|
Type: portType,
|
|
LowerLayers: `Device.Ethernet.Interface.${ethIndex + 1}`,
|
|
_key: device['.name'],
|
|
});
|
|
|
|
briVLANPort.push({
|
|
Enable: 1,
|
|
VLAN: `Device.Bridging.Bridge.${bridgeIndex}.VLAN.${vlanIndex}`,
|
|
Port: `Device.Bridging.Bridge.${bridgeIndex}.Port.${briPorts.length}`,
|
|
Untagged: device.untagged ? 1 : 0,
|
|
_key: device['.name'],
|
|
});
|
|
|
|
if (qinqDev && qinqDev.vid) {
|
|
createProviderBridge(qinqDev, 'PE', ethIndex + 1, bridges, providerBridges, bridgeIndex);
|
|
}
|
|
}
|
|
|
|
function importBridge(dev, devices, bridges, bridgeVlans, providerBridges) {
|
|
const briPorts = [];
|
|
const briVLAN = [];
|
|
const briVLANPort = [];
|
|
|
|
// create the management port first
|
|
briPorts.push({
|
|
Alias: `cpe-${dev.name}`,
|
|
Enable: 1,
|
|
Name: dev.name,
|
|
ManagementPort: 1,
|
|
PVID: 1,
|
|
TPID: 37120,
|
|
Type: 'CustomerVLANPort',
|
|
});
|
|
|
|
bridges.push({
|
|
Name: dev.name,
|
|
Alias: `cpe-${dev.name}`,
|
|
Enable: 1,
|
|
'Port.': briPorts,
|
|
'VLAN.': briVLAN,
|
|
'VLANPort.': briVLANPort,
|
|
_key: dev['.name'],
|
|
});
|
|
|
|
const ethPorts = devices.filter(x => x.type === undefined && x.eee !== undefined);
|
|
|
|
for (const portName of (dev.ports || [])) {
|
|
let device;
|
|
const portIndex = ethPorts.findIndex(x => x.ifname === portName);
|
|
const briVLANInfo = findPortFromBridgeVlan(bridgeVlans, portName);
|
|
if (portIndex >= 0 && !briVLANInfo) {
|
|
// Regular ethernet port
|
|
const ethDevice = ethPorts[portIndex];
|
|
addRegularEthernetPort(ethDevice, portIndex, briPorts);
|
|
continue;
|
|
}
|
|
|
|
if (briVLANInfo && portIndex >= 0) {
|
|
// bridge-vlan device
|
|
device = {['.name']: portName, ifname: portName, type: '8021q', name: portName, vid: Number(briVLANInfo[0]), untagged: !briVLANInfo[1], pvid: briVLANInfo[2]};
|
|
} else {
|
|
// vlan device
|
|
device = devices.find(x => x.name === portName);
|
|
if (!device) {
|
|
// check if it is a valid sub-interface
|
|
if (isVLANSubInterface(portName, ethPorts)) {
|
|
const ifname = portName.split('.').slice(0, -1).join('.');
|
|
const vid = portName.split('.').pop();
|
|
device = {['.name']: portName, ifname: ifname, type: '8021q', name: portName, vid: Number(vid)};
|
|
} else {
|
|
_log_error('device not found', portName);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!device.ifname || !device.vid) {
|
|
_log_error('ifname or vid not found', device['.name']);
|
|
return;
|
|
}
|
|
|
|
handleVlanDevice(bridges.length, device, ethPorts, devices, bridges, briPorts, briVLAN, briVLANPort, providerBridges);
|
|
}
|
|
|
|
if (briPorts.length > 1) {
|
|
const indexes = Array.from({ length: briPorts.length - 1 }, (v, i) => i + 2);
|
|
briPorts[0].LowerLayers = indexes.map(i => `Device.Bridging.Bridge.${bridges.length}.Port.${i}`).join(',');
|
|
}
|
|
}
|
|
|
|
export function importDeviceBridgingBridge() {
|
|
const bridges = [];
|
|
const providerBridges = [];
|
|
const devices = getUciByType('network', 'device');
|
|
const bridgeVlans = getUciByType('network', 'bridge-vlan');
|
|
devices?.forEach(dev => {
|
|
if (dev.type === 'bridge') {
|
|
const bridgeVlan = bridgeVlans?.filter(x => x.device === dev.name);
|
|
importBridge(dev, devices, bridges, bridgeVlan, providerBridges);
|
|
}
|
|
});
|
|
|
|
if (providerBridges.length > 0) {
|
|
_dm_update('Device.Bridging.ProviderBridge.', providerBridges);
|
|
}
|
|
|
|
return bridges;
|
|
}
|