zigbee-on-host
Version:
Zigbee stack designed to run on a host and communicate with a radio co-processor (RCP)
125 lines • 8.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.processFrame = processFrame;
const logger_js_1 = require("../utils/logger.js");
const mac_js_1 = require("../zigbee/mac.js");
const zigbee_aps_js_1 = require("../zigbee/zigbee-aps.js");
const zigbee_nwk_js_1 = require("../zigbee/zigbee-nwk.js");
const zigbee_nwkgp_js_1 = require("../zigbee/zigbee-nwkgp.js");
const NS = "frame-handler";
/**
* 05-3474-23 (Zigbee PRO) multi-layer processing pipeline
*
* SPEC COMPLIANCE NOTES:
* - ✅ Decodes MAC CMD/DATA frames and dispatches according to IEEE 802.15.4 frame type
* - ✅ Validates PAN ID and destination addressing before NWK processing
* - ✅ Routes Green Power (GP) frames per Zigbee GP spec (14-0563-19) when protocol version indicates GP
* - ✅ Applies duplicate checks via respective handlers (MAC/NWK/APS/GP)
* - ⚠️ INTERPAN frame type not supported (throws) - optional for coordinator
* - ⚠️ Beacon/Other MAC frame types ignored (logged at debug level)
* DEVICE SCOPE: Centralized trust center
*/
/* @__INLINE__ */
async function processFrame(payload, context, macHandler, nwkHandler, nwkGPHandler, apsHandler, rssi = context.rssiMin) {
const [macFCF, macFCFOutOffset] = (0, mac_js_1.decodeMACFrameControl)(payload, 0);
// TODO: process BEACON for PAN ID conflict detection?
if (macFCF.frameType !== 3 /* MACFrameType.CMD */ && macFCF.frameType !== 1 /* MACFrameType.DATA */) {
logger_js_1.logger.debug(() => `<-~- MAC Ignoring frame with type not CMD/DATA (${macFCF.frameType})`, NS);
return;
}
const [macHeader, macHOutOffset] = (0, mac_js_1.decodeMACHeader)(payload, macFCFOutOffset, macFCF);
const macPayload = (0, mac_js_1.decodeMACPayload)(payload, macHOutOffset, macFCF, macHeader);
if (macFCF.frameType === 3 /* MACFrameType.CMD */) {
await macHandler.processCommand(macPayload, macHeader);
// done
return;
}
if (macHeader.destinationPANId !== 65535 /* ZigbeeMACConsts.BCAST_PAN */ && macHeader.destinationPANId !== context.netParams.panId) {
logger_js_1.logger.debug(() => `<-~- MAC Ignoring frame with mismatching PAN Id ${macHeader.destinationPANId}`, NS);
return;
}
if (macFCF.destAddrMode === 2 /* MACFrameAddressMode.SHORT */ &&
macHeader.destination16 !== 65535 /* ZigbeeMACConsts.BCAST_ADDR */ &&
macHeader.destination16 !== 0 /* ZigbeeConsts.COORDINATOR_ADDRESS */) {
logger_js_1.logger.debug(() => `<-~- MAC Ignoring frame intended for device ${macHeader.destination16}`, NS);
return;
}
if (macPayload.byteLength > 0) {
const protocolVersion = (macPayload.readUInt8(0) & 60 /* ZigbeeNWKConsts.FCF_VERSION */) >> 2;
if (protocolVersion === 3 /* ZigbeeNWKConsts.VERSION_GREEN_POWER */) {
if ((macFCF.destAddrMode === 2 /* MACFrameAddressMode.SHORT */ && macHeader.destination16 === 65535 /* ZigbeeMACConsts.BCAST_ADDR */) ||
macFCF.destAddrMode === 3 /* MACFrameAddressMode.EXT */) {
const [nwkGPFCF, nwkGPFCFOutOffset] = (0, zigbee_nwkgp_js_1.decodeZigbeeNWKGPFrameControl)(macPayload, 0);
const [nwkGPHeader, nwkGPHOutOffset] = (0, zigbee_nwkgp_js_1.decodeZigbeeNWKGPHeader)(macPayload, nwkGPFCFOutOffset, nwkGPFCF);
if (nwkGPHeader.frameControl.frameType !== 0 /* ZigbeeNWKGPFrameType.DATA */ &&
nwkGPHeader.frameControl.frameType !== 1 /* ZigbeeNWKGPFrameType.MAINTENANCE */) {
logger_js_1.logger.debug(() => `<-~- NWKGP Ignoring frame with type ${nwkGPHeader.frameControl.frameType}`, NS);
return;
}
// Delegate GP duplicate check to NWK GP handler
if (nwkGPHeader.frameControl.frameType !== 1 /* ZigbeeNWKGPFrameType.MAINTENANCE */ &&
nwkGPHandler.isDuplicateFrame(macHeader, nwkGPHeader)) {
logger_js_1.logger.debug(() => `<-~- NWKGP Ignoring duplicate frame macSeqNum=${macHeader.sequenceNumber} nwkGPFC=${nwkGPHeader.securityFrameCounter}`, NS);
return;
}
const nwkGPPayload = (0, zigbee_nwkgp_js_1.decodeZigbeeNWKGPPayload)(macPayload, nwkGPHOutOffset, context.netParams.networkKey, macHeader.source64, nwkGPFCF, nwkGPHeader);
// Delegate GP frame processing to NWK GP handler
nwkGPHandler.processFrame(nwkGPPayload, macHeader, nwkGPHeader, context.computeLQA(rssi));
}
else {
logger_js_1.logger.debug(() => `<-x- NWKGP Invalid frame addressing ${macFCF.destAddrMode} (${macHeader.destination16})`, NS);
return;
}
}
else {
const [nwkFCF, nwkFCFOutOffset] = (0, zigbee_nwk_js_1.decodeZigbeeNWKFrameControl)(macPayload, 0);
const [nwkHeader, nwkHOutOffset] = (0, zigbee_nwk_js_1.decodeZigbeeNWKHeader)(macPayload, nwkFCFOutOffset, nwkFCF);
if (macHeader.destination16 !== undefined &&
macHeader.destination16 >= 65528 /* ZigbeeConsts.BCAST_MIN */ &&
nwkHeader.source16 === 0 /* ZigbeeConsts.COORDINATOR_ADDRESS */) {
logger_js_1.logger.debug(() => "<-~- NWK Ignoring frame from coordinator (broadcast loopback)", NS);
return;
}
const resolvedSource64 = nwkHeader.source64 ?? (nwkHeader.source16 !== undefined ? context.address16ToAddress64.get(nwkHeader.source16) : undefined);
const sourceLQA = context.computeDeviceLQA(nwkHeader.source16, nwkHeader.source64, rssi);
const nwkPayload = (0, zigbee_nwk_js_1.decodeZigbeeNWKPayload)(macPayload, nwkHOutOffset, undefined, // use pre-hashed this.context.netParams.networkKey,
resolvedSource64, nwkFCF, nwkHeader);
if (nwkFCF.security && nwkHeader.securityHeader) {
const accepted = context.updateIncomingNWKFrameCounter(nwkHeader.securityHeader.source64, nwkHeader.securityHeader.frameCounter);
if (!accepted) {
logger_js_1.logger.warning(() => `<-x- NWK Rejecting replay frame src16=${nwkHeader.source16}:${nwkHeader.securityHeader.source64} counter=${nwkHeader.securityHeader?.frameCounter}`, NS);
return;
}
}
if (nwkFCF.frameType === 0 /* ZigbeeNWKFrameType.DATA */) {
const [apsFCF, apsFCFOutOffset] = (0, zigbee_aps_js_1.decodeZigbeeAPSFrameControl)(nwkPayload, 0);
const [apsHeader, apsHOutOffset] = (0, zigbee_aps_js_1.decodeZigbeeAPSHeader)(nwkPayload, apsFCFOutOffset, apsFCF);
if (nwkHeader.source16 === undefined && nwkHeader.source64 === undefined) {
logger_js_1.logger.debug(() => `<-~- APS Ignoring frame with no sender info nwkSeqNum=${nwkHeader.seqNum}`, NS);
return;
}
if (apsHeader.frameControl.ackRequest && nwkHeader.source16 !== 0 /* ZigbeeConsts.COORDINATOR_ADDRESS */) {
await apsHandler.sendACK(macHeader, nwkHeader, apsHeader);
}
// Delegate APS duplicate check to APS handler
if (apsHeader.frameControl.frameType !== 2 /* ZigbeeAPSFrameType.ACK */ && apsHandler.isDuplicateFrame(nwkHeader, apsHeader)) {
logger_js_1.logger.debug(() => `<=~= APS Ignoring duplicate frame nwkSeqNum=${nwkHeader.seqNum} counter=${apsHeader.counter}`, NS);
return;
}
const apsPayload = (0, zigbee_aps_js_1.decodeZigbeeAPSPayload)(nwkPayload, apsHOutOffset, undefined, // use pre-hashed this.context.netParams.tcKey,
/* nwkHeader.frameControl.extendedSource ? nwkHeader.source64 : this.context.address16ToAddress64.get(nwkHeader.source16!) */
nwkHeader.source64 ?? context.address16ToAddress64.get(nwkHeader.source16), apsFCF, apsHeader);
// Delegate APS frame processing to APS handler
await apsHandler.processFrame(apsPayload, macHeader, nwkHeader, apsHeader, sourceLQA);
}
else if (nwkFCF.frameType === 1 /* ZigbeeNWKFrameType.CMD */) {
// Delegate NWK command processing to NWK handler
await nwkHandler.processCommand(nwkPayload, macHeader, nwkHeader);
}
else if (nwkFCF.frameType === 3 /* ZigbeeNWKFrameType.INTERPAN */) {
throw new Error("INTERPAN not supported");
}
}
}
}
//# sourceMappingURL=frame.js.map