dm-framework

This commit is contained in:
Xiaofeng Meng 2025-12-03 09:57:18 +01:00
parent c0a1139764
commit 03d520b65f
13 changed files with 1521 additions and 8 deletions

View file

@ -11,7 +11,7 @@ USE_LOCAL:=0
ifneq ($(USE_LOCAL),1)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://dev.iopsys.eu/bbf/bbfdm.git
PKG_SOURCE_VERSION:=8f72146f0f42b76b43ef545136548dd2ca1b0388
PKG_SOURCE_VERSION:=2d885215c3c7d22f71aaf51fe78469b606fa9142
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_MIRROR_HASH:=skip
endif

View file

@ -5,6 +5,7 @@ STOP=05
USE_PROCD=1
PROG=/usr/sbin/dm-service
DM_AGENT_PROG=/usr/sbin/dm-agent
BBFDM_MICROSERVICE_DIR="/etc/bbfdm/services"
@ -24,7 +25,8 @@ validate_bbfdm_micro_service_section()
_add_microservice()
{
local name path loglevel
local enable enable_core unified_daemon
local enable enable_core unified_daemon dm_framework
local daemon_prog
# Check enable from micro-service
path="${1}"
@ -47,14 +49,25 @@ _add_microservice()
return 0
fi
json_get_var dm_framework dm-framework 0
if [ "${dm_framework}" -eq "1" ] || [ "${dm_framework}" = "true" ]; then
daemon_prog="${DM_AGENT_PROG}"
else
daemon_prog="${PROG}"
fi
json_select config
json_get_var loglevel loglevel 4
procd_open_instance "${name}"
procd_set_param command ${PROG}
procd_set_param command ${daemon_prog}
# Only add parameters for dm-service, not for dm-agent
if [ "${daemon_prog}" = "${PROG}" ]; then
procd_append_param command -m "${name}"
procd_append_param command -l "${loglevel}"
fi
if [ "${enable_core}" -eq "1" ]; then
procd_set_param limits core="unlimited"

View file

@ -20,4 +20,9 @@ config BRIDGEMNGR_BRIDGE_VENDOR_EXT
config BRIDGEMNGR_BRIDGE_VENDOR_PREFIX
string "Package specific datamodel Vendor Prefix for TR181 extensions"
default ""
config BRIDGEMNGR_NEW_DM_SUPPORT
bool "Use new DM framework support"
default y
endif

View file

@ -20,13 +20,23 @@ PKG_LICENSE:=GPL-2.0-only
PKG_LICENSE_FILES:=LICENSE
include $(INCLUDE_DIR)/package.mk
ifneq ($(CONFIG_BRIDGEMNGR_NEW_DM_SUPPORT),y)
include ../bbfdm/bbfdm.mk
else
include ../dm-framework/dm-framework.mk
endif
define Package/bridgemngr
CATEGORY:=Utilities
TITLE:=Bridge Manager
DEPENDS:=+libuci +libubox +libubus +libblobmsg-json
ifeq ($(CONFIG_BRIDGEMNGR_NEW_DM_SUPPORT),y)
DEPENDS+= +dm-framework
PKG_BUILD_DEPENDS:=dm-framework
else
DEPENDS+= +libbbfdm-api +libbbfdm-ubus +dm-service
endif
endef
define Package/bridgemngr/description
@ -37,7 +47,9 @@ define Package/$(PKG_NAME)/config
source "$(SOURCE)/Config.in"
endef
ifneq ($(CONFIG_BRIDGEMNGR_NEW_DM_SUPPORT),y)
MAKE_PATH:=src
endif
ifeq ($(CONFIG_BRIDGEMNGR_BRIDGE_VENDOR_PREFIX),"")
VENDOR_PREFIX = $(CONFIG_BBF_VENDOR_PREFIX)
@ -55,15 +67,25 @@ ifeq ($(CONFIG_BRIDGEMNGR_COPY_PBITS),y)
TARGET_CFLAGS+=-DBRIDGEMNGR_COPY_PBITS
endif
ifeq ($(CONFIG_BRIDGEMNGR_NEW_DM_SUPPORT),y)
define Build/Compile
$(call Build/Compile/DM,./dm,$(PKG_BUILD_DIR)/dm_build,$(VENDOR_PREFIX))
endef
endif
define Package/bridgemngr/install
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/config
ifeq ($(CONFIG_BRIDGEMNGR_NEW_DM_SUPPORT),y)
$(call Build/Install/DM,./dm,$(PKG_BUILD_DIR)/dm_build,$(1),bridgemngr)
else
$(BBFDM_REGISTER_SERVICES) ./bbfdm_service.json $(1) $(PKG_NAME)
$(BBFDM_INSTALL_MS_DM) $(PKG_BUILD_DIR)/src/libbridgemngr.so $(1) $(PKG_NAME)
ifeq ($(CONFIG_BRIDGEMNGR_BRIDGE_VENDOR_EXT), y)
$(BBFDM_INSTALL_MS_PLUGIN) $(PKG_BUILD_DIR)/src/libbridgeext.so $(1) $(PKG_NAME) 10
$(BBFDM_INSTALL_MS_PLUGIN) -v ${VENDOR_PREFIX} ./files/VLAN_Filtering_Extension.json $(1) $(PKG_NAME) 11
endif
endif
$(INSTALL_BIN) ./files/etc/init.d/bridging $(1)/etc/init.d/

View file

@ -0,0 +1,449 @@
[
{
"object": "Device.Bridging.",
"access": "readOnly",
"parameters": [
{
"name": "MaxBridgeEntries",
"access": "readOnly",
"dataType": "unsignedInt",
"const" : "4094"
},
{
"name": "MaxDBridgeEntries",
"access": "readOnly",
"dataType": "unsignedInt",
"const" : "4094"
},
{
"name": "MaxQBridgeEntries",
"access": "readOnly",
"dataType": "unsignedInt",
"const" : "4094"
},
{
"name": "MaxVLANEntries",
"access": "readOnly",
"dataType": "unsignedInt",
"const" : "4094"
},
{
"name": "BridgeNumberOfEntries",
"access": "readOnly",
"dataType": "unsignedInt"
}
]
},
{
"object": "Device.Bridging.Bridge.{i}.",
"uniqueKeys": "Name,Alias",
"access": "readWrite",
"uci": "network.device",
"flags": [
"dmmap"
],
"parameters": [
{
"name": "Enable",
"access": "readWrite",
"dataType": "boolean",
"uci": "enabled",
"uci-default": "true"
},
{
"name": "Name",
"access": "readOnly",
"dataType": "string(:64)",
"set_on_create": "bridge_",
"db": true
},
{
"name": "Alias",
"access": "readWrite",
"dataType": "string(:64)"
},
{
"name": "Status",
"access": "readOnly",
"dataType": "enum",
"enum": [
"Disabled",
"Enabled",
"Error"
],
"default": "Disabled"
},
{
"name": "Standard",
"access": "readWrite",
"dataType": "enum",
"enum": [
"802.1D-2004",
"802.1Q-2005",
"802.1Q-2011"
],
"default": "802.1Q-2011"
},
{
"name": "PortNumberOfEntries",
"access": "readOnly",
"dataType": "unsignedInt"
},
{
"name": "VLANNumberOfEntries",
"access": "readOnly",
"dataType": "unsignedInt"
},
{
"name": "VLANPortNumberOfEntries",
"access": "readOnly",
"dataType": "unsignedInt"
}
]
},
{
"object": "Device.Bridging.Bridge.{i}.STP.",
"access": "readOnly",
"parameters": [
{
"name": "Enable",
"access": "readWrite",
"dataType": "boolean",
"uci": "stp"
},
{
"name": "Status",
"access": "readOnly",
"dataType": "enum",
"enum": [
"Disabled",
"Enabled",
"Error_Misconfigured",
"Error"
],
"default": "Disabled"
},
{
"name": "Protocol",
"access": "readWrite",
"dataType": "enum",
"enum": [
"STP",
"RSTP"
]
},
{
"name": "BridgePriority",
"access": "readWrite",
"dataType": "unsignedInt(0:61440)",
"default": "32768"
},
{
"name": "HelloTime",
"access": "readWrite",
"dataType": "unsignedInt(100:1000)",
"default": "200"
},
{
"name": "MaxAge",
"access": "readWrite",
"dataType": "unsignedInt(600:4000)",
"default": "2000"
},
{
"name": "ForwardingDelay",
"access": "readWrite",
"dataType": "unsignedInt(4:30)",
"default": "15"
}
]
},
{
"object": "Device.Bridging.Bridge.{i}.Port.{i}.",
"uniqueKeys": "Alias,Name",
"access": "readWrite",
"flags": [
"dmmap"
],
"parameters": [
{
"name": "Enable",
"access": "readWrite",
"dataType": "boolean"
},
{
"name": "Status",
"access": "readOnly",
"dataType": "enum",
"enum": [
"Up",
"Down",
"Unknown",
"Dormant",
"NotPresent",
"LowerLayerDown",
"Error"
],
"default": "Down"
},
{
"name": "Alias",
"access": "readWrite",
"dataType": "string(:64)"
},
{
"name": "Name",
"access": "readOnly",
"dataType": "string(:64)",
"set_on_create": "port_",
"db": "true",
"flags": [
"linker"
],
"js-value": "ifname"
},
{
"name": "LastChange",
"access": "readOnly",
"dataType": "unsignedInt",
"const": "0"
},
{
"name": "LowerLayers",
"access": "readWrite",
"dataType": "pathRef[]",
"pathRef": [
"Device.Bridging.Bridge.{i}.Port."
],
"js-value": "ssidPath"
},
{
"name": "ManagementPort",
"access": "readWrite",
"dataType": "boolean"
},
{
"name": "PriorityRegeneration",
"access": "readWrite",
"dataType": "unsignedInt(0:7)[]",
"default": "0,1,2,3,4,5,6,7"
},
{
"name": "{BBF_VENDOR_PREFIX}EgressPriorityRegeneration",
"access": "readWrite",
"dataType": "unsignedInt(0:7)[]"
},
{
"name": "Type",
"access": "readWrite",
"dataType": "enum",
"enum": [
"ProviderNetworkPort",
"CustomerNetworkPort",
"CustomerEdgePort",
"CustomerVLANPort",
"VLANUnawarePort"
],
"default": "CustomerVLANPort"
},
{
"name": "PVID",
"access": "readWrite",
"dataType": "int(1:4094)",
"default": "1"
},
{
"name": "TPID",
"access": "readWrite",
"dataType": "unsignedInt",
"default": "33024"
}
]
},
{
"object": "Device.Bridging.Bridge.{i}.Port.{i}.Stats.",
"access": "readOnly",
"parameters": [
{
"name": "BytesSent",
"dataType": "unsignedLong"
},
{
"name": "BytesReceived",
"dataType": "unsignedLong"
},
{
"name": "PacketsSent",
"dataType": "unsignedLong"
},
{
"name": "PacketsReceived",
"dataType": "unsignedLong"
},
{
"name": "ErrorsSent",
"dataType": "StatsCounter32"
},
{
"name": "ErrorsReceived",
"dataType": "StatsCounter32"
},
{
"name": "UnicastPacketsSent",
"dataType": "unsignedLong"
},
{
"name": "DiscardPacketsSent",
"dataType": "StatsCounter32"
},
{
"name": "DiscardPacketsReceived",
"dataType": "StatsCounter32"
},
{
"name": "MulticastPacketsSent",
"dataType": "unsignedLong"
},
{
"name": "UnicastPacketsReceived",
"dataType": "unsignedLong"
},
{
"name": "MulticastPacketsReceived",
"dataType": "unsignedLong"
},
{
"name": "BroadcastPacketsSent",
"dataType": "unsignedLong"
},
{
"name": "BroadcastPacketsReceived",
"dataType": "unsignedLong"
},
{
"name": "UnknownProtoPacketsReceived",
"dataType": "StatsCounter32"
}
]
},
{
"object": "Device.Bridging.Bridge.{i}.VLAN.{i}.",
"uniqueKeys": "Alias,VLANID",
"access": "readWrite",
"parameters": [
{
"name": "Enable",
"access": "readWrite",
"dataType": "boolean"
},
{
"name": "Name",
"access": "readWrite",
"dataType": "string(:64)",
"set_on_create": "vlan_"
},
{
"name": "Alias",
"access": "readWrite",
"dataType": "string(:64)"
},
{
"name": "VLANID",
"access": "readWrite",
"dataType": "int(0:4094)"
}
]
},
{
"object": "Device.Bridging.Bridge.{i}.VLANPort.{i}.",
"uniqueKeys": "Alias,VLAN",
"access": "readWrite",
"parameters": [
{
"name": "Enable",
"access": "readWrite",
"dataType": "boolean"
},
{
"name": "Alias",
"access": "readWrite",
"dataType": "string(:64)"
},
{
"name": "VLAN",
"access": "readWrite",
"dataType": "pathRef",
"pathRef": [
"Device.Bridging.Bridge.{i}.VLAN."
]
},
{
"name": "Port",
"access": "readWrite",
"dataType": "pathRef",
"pathRef": [
"Device.Bridging.Bridge.{i}.Port."
]
},
{
"name": "Untagged",
"access": "readWrite",
"dataType": "boolean"
}
]
},
{
"object": "Device.Bridging.ProviderBridge.{i}.",
"uniqueKeys": "Alias",
"access": "readWrite",
"parameters": [
{
"name": "Enable",
"access": "readWrite",
"dataType": "boolean"
},
{
"name": "Status",
"access": "readOnly",
"dataType": "enum",
"enum": [
"Disabled",
"Enabled",
"Error_Misconfigured",
"Error"
],
"default": "Disabled"
},
{
"name": "Alias",
"access": "readWrite",
"dataType": "string(:64)"
},
{
"name": "Type",
"access": "readWrite",
"dataType": "enum",
"enum": [
"S-VLAN",
"PE"
]
},
{
"name": "SVLANcomponent",
"access": "readWrite",
"dataType": "pathRef",
"pathRef": [
"Device.Bridging.Bridge."
]
},
{
"name": "CVLANcomponents",
"access": "readWrite",
"dataType": "pathRef[]",
"pathRef": [
"Device.Bridging.Bridge."
]
}
]
}
]

View file

@ -0,0 +1,343 @@
/*
* 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']));
}
}

View file

@ -0,0 +1,268 @@
/*
* 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;
}

133
bridgemngr/dm/bridge.js Normal file
View file

@ -0,0 +1,133 @@
/*
* 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 * as std from 'std';
import { isTrue } from '../utils.js';
import { getUciByType } from '../uci.js';
function setMgmtPortLowerLayers(bri) {
if (!bri) return 0;
const portPath = `Device.Bridging.Bridge.${bri['.index']}.Port.`;
const mgmtPort = _dm_instances(portPath, '(ManagementPort="true" OR ManagementPort=1)');
if (mgmtPort.length !== 1) return 0;
const nonMgmtPort = _dm_instances(portPath, '(ManagementPort="false" OR ManagementPort=0)');
_dm_update(`${mgmtPort[0]}.LowerLayers`, nonMgmtPort.join(','));
return 0;
}
export function changedDeviceBridgingBridgePort(bri) {
return setMgmtPortLowerLayers(bri);
}
export function changedDeviceBridgingBridgePortManagementPort(bri) {
return setMgmtPortLowerLayers(bri);
}
export function getDeviceBridgingBridgeStatus(bri) {
const enable = _dm_get(`Device.Bridging.Bridge.${bri['.index']}.Enable`);
return enable ? 'Enabled' : 'Disabled';
}
export function getDeviceBridgingBridgeSTPStatus(bri) {
const stpState = std.loadFile(`/sys/class/net/${bri.Name}/bridge/stp_state`)?.trim();
return stpState === '1' ? 'Enabled' : 'Disabled';
}
export function getDeviceBridgingBridgePortStatus(bri, port) {
if (!port['.db']) return 'Up';
const enable = _dm_get(`Device.Bridging.Bridge.${bri['.index']}.Port.${port['.index']}.Enable`);
return enable ? 'Up' : 'Down';
}
export function infoDeviceBridgingBridgePort(path, port) {
const mgmtPort = _dm_get(`${path}.ManagementPort`);
if (typeof mgmtPort === 'undefined' || mgmtPort) return;
const lower = _dm_get(`${path}.LowerLayers`);
if (lower) {
port.ifname = _dm_linker_value(lower);
}
}
// Helper function to read network statistics
function getNetworkStat(port, statName) {
return std.loadFile(`/sys/class/net/${port.ifname}/statistics/${statName}`)?.trim();
}
export const getDeviceBridgingBridgePortStatsBytesSent = (bri, port) =>
getNetworkStat(port, 'tx_bytes');
export const getDeviceBridgingBridgePortStatsBytesReceived = (bri, port) =>
getNetworkStat(port, 'rx_bytes');
export const getDeviceBridgingBridgePortStatsPacketsSent = (bri, port) =>
getNetworkStat(port, 'tx_packets');
export const getDeviceBridgingBridgePortStatsPacketsReceived = (bri, port) =>
getNetworkStat(port, 'rx_packets');
export const getDeviceBridgingBridgePortStatsErrorsSent = (bri, port) =>
getNetworkStat(port, 'tx_errors');
export const getDeviceBridgingBridgePortStatsErrorsReceived = (bri, port) =>
getNetworkStat(port, 'rx_errors');
export const getDeviceBridgingBridgePortStatsDiscardPacketsSent = (bri, port) =>
getNetworkStat(port, 'tx_dropped');
export const getDeviceBridgingBridgePortStatsDiscardPacketsReceived = (bri, port) =>
getNetworkStat(port, 'rx_dropped');
export const getDeviceBridgingBridgePortStatsMulticastPacketsReceived = (bri, port) =>
getNetworkStat(port, 'multicast');
export const getDeviceBridgingBridgePortStatsUnicastPacketsSent = (bri, port) =>
getNetworkStat(port, 'tx_unicast_packets');
export const getDeviceBridgingBridgePortStatsUnicastPacketsReceived = (bri, port) =>
getNetworkStat(port, 'rx_unicast_packets');
export const getDeviceBridgingBridgePortStatsMulticastPacketsSent = (bri, port) =>
getNetworkStat(port, 'tx_multicast_packets');
export const getDeviceBridgingBridgePortStatsBroadcastPacketsSent = (bri, port) =>
getNetworkStat(port, 'tx_broadcast_packets');
export const getDeviceBridgingBridgePortStatsBroadcastPacketsReceived = (bri, port) =>
getNetworkStat(port, 'rx_broadcast_packets');
export const getDeviceBridgingBridgePortStatsUnknownProtoPacketsReceived = (bri, port) =>
getNetworkStat(port, 'rx_unknown_packets');
export function getDeviceBridgingBridgePort(bri) {
const networkName = bri.Name.startsWith('br-') ? bri.Name.slice(3) : bri.Name;
const wifiIfaces = getUciByType('wireless', 'wifi-iface', { match: { multi_ap: '2', network: networkName } });
wifiIfaces?.forEach(x => {
const ssid = getUciByType('WiFi', 'SSID',
{ match: { device: x.device, ssid: x.ssid}, confdir: '/etc/bbfdm/dmmap'});
if (Array.isArray(ssid) && ssid[0].__instance__) {
x.ssidPath = `Device.WiFi.SSID.${ssid[0].__instance__}`;
}
});
return wifiIfaces;
}
export function setDeviceBridgingBridgePortManagementPort(val, bri, port) {
if (isTrue(val)) {
_db_set(`Device.Bridging.Bridge.${bri['.index']}.Port.${port['.index']}.Name`, bri.Name);
}
return 1;
}

56
bridgemngr/dm/common.js Executable file
View file

@ -0,0 +1,56 @@
/*
* 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.
*
*/
export const bridgePortTypeMap = [
{ portType: 'ProviderNetworkPort', devType: '8021ad' },
{ portType: 'CustomerVLANPort', devType: '8021q' },
{ portType: 'CustomerEdgePort', devType: '8021q' },
{ portType: 'CustomerNetworkPort', devType: '8021q' },
{ portType: 'VLANUnawarePort', devType: '' }
];
export function getBridgePortType(devType) {
const mapping = bridgePortTypeMap.find(map => map.devType === devType);
return mapping ? mapping.portType : null;
}
export function getBridgeDeviceType(portType) {
const mapping = bridgePortTypeMap.find(map => map.portType === portType);
return mapping ? mapping.devType : '';
}
export function getDefaultTPID(deviceType) {
switch (deviceType) {
case '8021q':
return '33024';
case '8021ad':
return '34984';
default:
return '37120';
}
}
export function getTPIDFromDeviceType(deviceType, explicitTPID) {
// If explicit TPID is set, use it
if (explicitTPID && explicitTPID !== '') {
return parseInt(explicitTPID, 10);
}
// Default TPID based on device type
switch (deviceType) {
case '8021q':
return 33024;
case '8021ad':
return 34984;
default:
return 37120;
}
}

170
dm-framework/Makefile Executable file
View file

@ -0,0 +1,170 @@
#
# Copyright (c) 2023 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.
#
#
include $(TOPDIR)/rules.mk
PKG_NAME:=dm-framework
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://dev.iopsys.eu/lcm/dm-framework.git
PKG_SOURCE_VERSION:=ba2ec403f08cc0d5401a1610517f17490c94a978
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_MIRROR_HASH:=skip
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
# Build directories for each component
DMAPI_BUILD_DIR:=$(PKG_BUILD_DIR)/dm-api-build
DMAGENT_BUILD_DIR:=$(PKG_BUILD_DIR)/dm-agent-build
include $(INCLUDE_DIR)/package.mk
include ../bbfdm/bbfdm.mk
#
# DM-API Package Definition
#
define Package/dm-api
CATEGORY:=Genexis
TITLE:=dm-api
DEPENDS:=+libsqlite3 \
+libjson-c +libstdcpp +quickjs \
+libubus +libubox +libuci
URL:=http://www.genexis.eu
PKG_LICENSE:=GENEXIS
PKG_LICENSE_URL:=
endef
define Package/dm-api/description
This package contains api for the dm-framework
endef
#
# DM-Agent Package Definition
#
define Package/dm-agent
DEPENDS:=+dm-api +libubox +libubus +ubus
CATEGORY:=Genexis
TITLE:=dm-framework agent
URL:=http://www.genexis.eu
PKG_LICENSE:=GENEXIS
PKG_LICENSE_URL:=
endef
define Package/dm-agent/description
This package contains dm-framework agent.
endef
#
# Build Preparation
#
define Build/Prepare
$(call Build/Prepare/Default)
# Prepare dm-api
mkdir -p $(DMAPI_BUILD_DIR)
$(CP) -rf $(PKG_BUILD_DIR)/dm-api/* $(DMAPI_BUILD_DIR)/
# Prepare dm-agent
mkdir -p $(DMAGENT_BUILD_DIR)
$(CP) -rf $(PKG_BUILD_DIR)/dm-agent/* $(DMAGENT_BUILD_DIR)/
endef
TARGET_CFLAGS += $(FPIC)
#
# Build Compilation
#
define Build/Compile
# Build dm-api
$(MAKE) -C $(DMAPI_BUILD_DIR)\
PROJECT_ROOT="$(DMAPI_BUILD_DIR)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
ARCH="$(LINUX_KARCH)" \
EXTRA_CFLAGS="$(TARGET_CFLAGS) -I$(DMAPI_BUILD_DIR)" \
all
# Build dm-agent (depends on dm-api)
$(MAKE) -C $(DMAGENT_BUILD_DIR)\
PROJECT_ROOT="$(DMAGENT_BUILD_DIR)" \
CROSS_COMPILE="$(TARGET_CROSS)" \
ARCH="$(LINUX_KARCH)" \
EXTRA_CFLAGS="$(TARGET_CFLAGS) -I$(DMAGENT_BUILD_DIR)" \
all
endef
#
# Development Installation (headers and libraries)
#
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include
$(INSTALL_DIR) $(1)/usr/lib
# DM-API development files - headers are now in dm-api/include/
$(CP) $(DMAPI_BUILD_DIR)/include/dm_types.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/include/dm_node.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/core/dm_api.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/core/dm_linker.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/core/dbmgr.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/include/dm_log.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/utils/dm_list.h $(1)/usr/include/
$(CP) $(DMAPI_BUILD_DIR)/libdmapi.so $(1)/usr/lib/
# Install json2code.js script and package.json to staging for other packages to use
$(INSTALL_DIR) $(1)/usr/lib/dm-framework/scripts
$(CP) $(PKG_BUILD_DIR)/scripts/json2code.js $(1)/usr/lib/dm-framework/scripts/
$(CP) $(PKG_BUILD_DIR)/scripts/package.json $(1)/usr/lib/dm-framework/scripts/
endef
#
# Package Installation - DM-API
#
define Package/dm-api/install
$(INSTALL_DIR) $(1)/usr/lib
$(INSTALL_DIR) $(1)/sbin/
$(INSTALL_DIR) $(1)/etc/bbfdm/dmf
$(INSTALL_BIN) $(DMAPI_BUILD_DIR)/libdmapi.so $(1)/usr/lib/
$(CP) $(DMAPI_BUILD_DIR)/quickjs/uci.js $(1)/etc/bbfdm/dmf/
$(CP) $(DMAPI_BUILD_DIR)/quickjs/utils.js $(1)/etc/bbfdm/dmf/
endef
#
# Package Installation - DM-Agent
#
define Package/dm-agent/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/config
$(BBFDM_REGISTER_SERVICES) ./dmf_service.json $(1) $(PKG_NAME)
$(INSTALL_BIN) $(DMAGENT_BUILD_DIR)/dm-agent $(1)/usr/sbin
endef
define Package/dm-framework
CATEGORY:=Genexis
TITLE:=DM Framework Meta Package
DEPENDS:=+dm-api +dm-agent
URL:=http://www.genexis.eu
PKG_LICENSE:=GENEXIS
endef
define Package/dm-framework/description
This is a meta package that pulls in dm-api and dm-agent.
endef
define Package/dm-framework/install
true
endef
# Register all three packages
$(eval $(call BuildPackage,dm-api))
$(eval $(call BuildPackage,dm-agent))
$(eval $(call BuildPackage,dm-framework))

View file

@ -0,0 +1,33 @@
# dm-framework.mk - Common rules for DM Framework
DM_SCRIPT_DIR ?= $(STAGING_DIR)/usr/lib/dm-framework/scripts
JSON2CODE = $(DM_SCRIPT_DIR)/json2code.js
# Macro to generate code
# $(1): Input directory (datamodels)
# $(2): Output directory (where generated files go)
# $(3): Vendor Prefix (optional)
define Build/Compile/DM
$(INSTALL_DIR) $(2)
@# Install npm dependencies if not already installed
@if [ ! -d "$(DM_SCRIPT_DIR)/node_modules" ]; then \
cd $(DM_SCRIPT_DIR) && npm install --production; \
fi
node $(JSON2CODE) -i $(1) -o $(2) $(if $(3),--vendor-prefix $(3))
$(TARGET_CC) $(TARGET_CFLAGS) -I$(2) -I$(STAGING_DIR)/usr/include/ -fPIC -c $(2)/dm.c -o $(2)/dm.o
$(TARGET_CC) $(TARGET_LDFLAGS) -shared -o $(2)/lib$(PKG_NAME).so $(2)/dm.o
endef
# Macro to install DM
# $(1): Input directory (datamodels)
# $(2): Output directory (build dir)
# $(3): Destination directory (rootfs)
# $(4): Package Name (subdir in /etc/bbfdm/dmf)
define Build/Install/DM
$(INSTALL_DIR) $(3)/etc/bbfdm/dmf/$(4)
$(CP) $(2)/lib$(PKG_NAME).so $(3)/etc/bbfdm/dmf/$(4)/
$(CP) $(1)/*.js $(3)/etc/bbfdm/dmf/$(4)/
$(CP) $(2)/default.db $(3)/etc/bbfdm/dmf/default_dm.db
$(CP) $(2)/exports.js $(3)/etc/bbfdm/dmf/$(4)/exports.js
$(CP) $(2)/dm_consts.js $(3)/etc/bbfdm/dmf/$(4)/dm_consts.js
endef

View file

@ -0,0 +1,17 @@
{
"daemon": {
"enable": "1",
"service_name": "dmf",
"dm-framework": true,
"unified_daemon": false,
"services": [
{
"parent_dm": "Device.",
"object": "Bridging"
}
],
"config": {
"loglevel": "3"
}
}
}

View file

@ -35,6 +35,10 @@ MAKE_FLAGS = \
EXTRA_LIBS="-latomic" \
CROSS_PREFIX="$(TARGET_CROSS)"
# Ensure the static library is built with position independent code so it can
# be linked into shared objects.
TARGET_CFLAGS += -fPIC
define Build/Compile
# The upstream Makefile uses the same CFLAGS for host and target builds,
# which breaks cross-compilation. We work around this by first building