/* * 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 { getUciOption, getUciByType, setUci, addUci, delUci } from '../uci.js'; import { getParamValue, replaceArrayElement, isTrue } from '../utils.js'; import * as dm from './dm_consts.js'; import { getBridgeDeviceType, getTPIDFromDeviceType } from './common.js'; function findVLANPort(vlanPort, VLANs, Ports) { const [, vlanIndices] = _dm_node(vlanPort.VLAN); const vlanIdx = vlanIndices[vlanIndices.length - 1]; const vlan = VLANs?.find(x => x['.index'] === vlanIdx); if (!vlan) { _log_error(`vlan not found for vlanPort: ${vlanPort.VLAN}`); return; } const [, portIndices] = _dm_node(vlanPort.Port); const portIdx = portIndices[portIndices.length - 1]; const port = Ports?.find(x => x['.index'] === portIdx); if (!port) { _log_error(`port not found for vlanPort: ${vlanPort.Port}`); return; } return [vlan, port]; } function createVLANDevice(devName, ifname, VLAN, Port) { const ingress_qos_mapping = Port.PriorityRegeneration !== '0,1,2,3,4,5,6,7' ? Port.PriorityRegeneration.split(',').map((p, i) => `${i}:${p}`) : ''; const egress_qos_mapping = Port.X_IOPSYS_EU_EgressPriorityRegeneration !== '' ? Port.X_IOPSYS_EU_EgressPriorityRegeneration.split(',').map((p, i) => `${i}:${p}`) : ''; const uciConfigs = { ifname: ifname, vid: VLAN.VLANID, name: ifname + '.' + VLAN.VLANID, type: getBridgeDeviceType(Port.Type), tpid: getTPIDFromDeviceType(Port.Type, Port.TPID), ingress_qos_mapping, egress_qos_mapping, }; addUci('network', 'device', devName, uciConfigs); } function applyBridge(bri, Ports, VLANs, VLANPorts) { deinitDeviceBridgingBridge(bri._key, false); const ports = []; for (const vlan of VLANs || []) { vlan.ports = []; vlan.hasUntagged = false; } for (const vlanPort of VLANPorts || []) { if (!vlanPort.Enable || !vlanPort.Port || !vlanPort.VLAN) { continue; } const [vlan, port] = findVLANPort(vlanPort, VLANs, Ports); if (!vlan || !port || port.LowerLayers === '' || !port.Enable || !vlan.Enable || vlan.VLANID <= 0) { continue; } port.used = true; if (port.Type === 'ProviderNetworkPort') { continue; } const devName = `br_${bri['.index']}_dev_${vlanPort['.index']}`; if (!port.LowerLayers.startsWith('Device.Ethernet.Interface')) { _log_error(`applyBridge, LowerLayers not found for port: ${port.LowerLayers}`); continue; } const ifname = _dm_linker_value(port.LowerLayers); if (!ifname) { _log_error(`applyBridge, ifname not found for port: ${port.LowerLayers}`); continue; } if (vlanPort.Untagged) { ports.push(ifname); vlan.hasUntagged = true; vlan.ports.push(ifname + ':u' + (vlanPort.PVID === vlan.VLANID ? '*' : '')); // vlan.ports.push(ifname + ':u*'); } else { createVLANDevice(devName, ifname, vlan, port); const vlanDevName = ifname + '.' + vlan.VLANID; ports.push(vlanDevName); vlan.ports.push(vlanDevName + ':u*'); } } for (const port of Ports || []) { if (port.used || isTrue(port.ManagementPort)) { continue; } if (port.LowerLayers.startsWith('Device.Ethernet.Interface')) { const ifname = _dm_linker_value(port.LowerLayers); if (!ifname) { _log_error(`applyBridge, ifname not found for port: ${port.LowerLayers}`); continue; } ports.push(ifname); } } if (ports.length > 0) { setUci('network', bri._key, { ports: ports }); } // create the bridge-vlan for the untagged port for (const vlan of VLANs || []) { if (vlan.hasUntagged) { addUci('network', 'bridge-vlan', `br_${bri['.index']}_bv_${vlan['.index']}`, { device: bri.Name, vlan: vlan.VLANID, ports: vlan.ports, }); } } applyProviderBridges(); } function applyPEBridges(ifname, vlanID, portLowerLayers, cvlanBridgePath) {const vlanPorts = _dm_get(cvlanBridgePath + '.VLANPort.'); for (const vlanPort of vlanPorts || []) { if (!vlanPort.Enable || !vlanPort.Port || !vlanPort.VLAN) { continue; } const portVals = _dm_get(vlanPort.Port); if (portVals?.LowerLayers !== portLowerLayers || portVals?.Type !== 'CustomerEdgePort') { _log_error(`applyPEBridges, portVals not found for vlanPort: ${vlanPort.Port}`); continue; } const vlan = _dm_get(vlanPort.VLAN); if (!vlan || vlan.VLANID <= 0 || !vlan.Enable) { _log_error(`applyPEBridges, vlan not found for vlanPort: ${vlanPort.VLAN}`); continue; } const devName = ifname + '.' + vlan.VLANID; const briName = getParamValue(cvlanBridgePath, '_key'); if (!briName) { _log_error(`applyPEBridges, briName not found for cvlanBridgePath: ${cvlanBridgePath}`); continue; } const ports = getUciOption('network', briName, 'ports') || []; const devs = getUciByType('network', 'device', { match: { name: devName } }); if (devs.length === 0) { _log_error(`applyPEBridges, device not found for devName: ${devName}`); continue; } const newName = `${ifname}.${vlanID}.${vlan.VLANID}`; setUci('network', devs[0]['.name'], {ifname: `${ifname}.${vlanID}`, name: newName}); replaceArrayElement(ports, devName, newName); setUci('network', briName, { ports: ports }); } } function applyProviderBridge(pbridgeIndex, type, svlanBridgePath, cvlanBridgePaths) { const vlanPorts = _dm_get(svlanBridgePath + '.VLANPort.'); const briName = getParamValue(svlanBridgePath, '_key'); if (briName) { delUci('network', briName); } for (const vlanPort of vlanPorts || []) { if (!vlanPort.Enable || !vlanPort.Port || !vlanPort.VLAN) { continue; } const portVals = _dm_get(vlanPort.Port); if (!portVals) { _log_error(`applyProviderBridge, portVals not found for vlanPort: ${vlanPort.Port}`); continue; } if (portVals.Type !== 'ProviderNetworkPort') { _log_error(`applyProviderBridge, portVals.Type is not ProviderNetworkPort for vlanPort: ${vlanPort.Port}`); continue; } const ifname = _dm_linker_value(portVals.LowerLayers); if (!ifname) { _log_error(`applyProviderBridge, ifname not found for port: ${portVals.LowerLayers}`); continue; } const vlan = _dm_get(vlanPort.VLAN); if (!vlan || !vlan.Enable || vlan.VLANID <= 0) { _log_error(`applyProviderBridge, vlan invalid for vlanPort: ${vlanPort.VLAN}`); continue; } const devName = `pb_${pbridgeIndex}_dev_${vlanPort['.index']}`; createVLANDevice(devName, ifname, vlan, portVals); cvlanBridgePaths.split(',').forEach(cvlanBridgePath => { if (type === 'S-VLAN') { const briName = getParamValue(cvlanBridgePath, '_key'); if (briName) { let ports = getUciOption('network', briName, 'ports') || []; ports = ports.filter(x => !x.startsWith(ifname)); ports.push(ifname + '.' + vlan.VLANID); setUci('network', briName, { ports: ports }); } else { _log_error(`applyProviderBridge, briName not found for cvlanBridgePath: ${cvlanBridgePath}`); } } else { applyPEBridges(ifname, vlan.VLANID, portVals.LowerLayers, cvlanBridgePath); } }); } } function applyProviderBridges() { const pbridges = _dm_get(dm.DM_DEVICE_BRIDGING_PROVIDERBRIDGE); for (const pbridge of pbridges || []) { if (!pbridge.Enable || !pbridge.SVLANcomponent || !pbridge.CVLANcomponents || !pbridge.Type) { continue; } applyProviderBridge(pbridge['.index'], pbridge.Type, pbridge.SVLANcomponent, pbridge.CVLANcomponents); } } function applyAllBridges() { const bridges = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE); for (const bri of bridges || []) { applyBridge(bri, bri.Port, bri.VLAN, bri.VLANPort); } } export function applyDeviceBridgingProviderBridge() { applyAllBridges(); } function isProviderBridge(ports) { return ports.some(port => port.Type === 'ProviderNetworkPort' || port.Type === 'CustomerEdgePort'); } export function applyDeviceBridgingBridgePort(ports, bri) { const vlans = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLAN, bri['.index']); const vlanPorts = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLANPORT, bri['.index']); if (isProviderBridge(ports)) { applyAllBridges(); return; } applyBridge(bri, ports, vlans, vlanPorts); } export function applyDeviceBridgingBridgeVLAN(vlans, bri) { const ports = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_PORT, bri['.index']); if (isProviderBridge(ports)) { applyAllBridges(); return; } const vlanPorts = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLANPORT, bri['.index']); applyBridge(bri, ports, vlans, vlanPorts); } export function applyDeviceBridgingBridgeVLANPort(vlanPorts, bri) { const ports = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_PORT, bri['.index']); if (isProviderBridge(ports)) { applyAllBridges(); return; } const vlans = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLAN, bri['.index']); applyBridge(bri, ports, vlans, vlanPorts); } export function initDeviceBridgingBridge(bri) { setUci('network', bri._key, { type: 'bridge', name: bri.Name, enabled: '0', }); // create empty interface for the bridge addUci('network', 'interface', `itf_${bri._key}`, { device: bri.Name, bridge_empty: '1', }); } export const filterDeviceBridgingBridge = uci => uci.type === 'bridge'; function delVLANDevice(devName, devices, ethPorts) { if (ethPorts.find(x => x.name === devName)) { return; } const dev = devices.find(x => x.name === devName); if (!dev) { return; } // delete possible vlan stack device delVLANDevice(dev.ifname, devices, ethPorts); delUci('network', dev['.name']); } export function deinitDeviceBridgingBridge(uci, removeInterface = true) { const ports = getUciOption('network', uci, 'ports'); const devices = getUciByType('network', 'device'); const ethPorts = devices.filter(x => x.type === undefined && x.eee !== undefined); ports?.forEach(port => { delVLANDevice(port, devices, ethPorts); }); const name = getUciOption('network', uci, 'name'); // delete related bridge-vlan devices (query first to avoid noisy "section missing" logs) const bridgeVlans = getUciByType('network', 'bridge-vlan', { match: { device: name } }) || []; bridgeVlans.forEach(vlan => delUci('network', vlan['.name'])); if (removeInterface) { // delete the empty interface created for this bridge (query first) const interfaces = getUciByType('network', 'interface', { match: { device: name, bridge_empty: '1' } }) || []; interfaces.forEach(intf => delUci('network', intf['.name'])); } }