mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-10 07:44:50 +01:00
Introduce dm-framework.mk providing reusable build macros for packages that integrate with the DM Framework: - Build/Compile/DM: Generates C code and shared libraries from JSON data models using json2code.js, compiles them, and creates package-specific .so files - Build/Install/DM: Installs generated libraries, JS handlers, and data model files to the dm-framework directory(dmf) Also adds bridgemngr as the first package utilizing these helpers to implement bridge data model with dm-framework, which can be enabled through flag CONFIG_BRIDGEMNGR_USE_DM_FRAMEWORK. The commit also include changes in bbfdm dm-framework adaption.
343 lines
12 KiB
JavaScript
343 lines
12 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 {
|
|
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']));
|
|
}
|
|
}
|