UNPKG

zigbee-on-host

Version:

Zigbee stack designed to run on a host and communicate with a radio co-processor (RCP)

258 lines 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ZigbeeNWKStatus = void 0; exports.decodeZigbeeNWKFrameControl = decodeZigbeeNWKFrameControl; exports.decodeZigbeeNWKHeader = decodeZigbeeNWKHeader; exports.decodeZigbeeNWKPayload = decodeZigbeeNWKPayload; exports.encodeZigbeeNWKFrame = encodeZigbeeNWKFrame; const zigbee_js_1 = require("./zigbee.js"); /** Network Status Code Definitions. */ var ZigbeeNWKStatus; (function (ZigbeeNWKStatus) { /** @deprecated in R23, should no longer be sent, but still processed (same as @see LINK_FAILURE ) */ ZigbeeNWKStatus[ZigbeeNWKStatus["LEGACY_NO_ROUTE_AVAILABLE"] = 0] = "LEGACY_NO_ROUTE_AVAILABLE"; /** @deprecated in R23, should no longer be sent, but still processed (same as @see LINK_FAILURE ) */ ZigbeeNWKStatus[ZigbeeNWKStatus["LEGACY_LINK_FAILURE"] = 1] = "LEGACY_LINK_FAILURE"; /** This link code indicates a failure to route across a link. */ ZigbeeNWKStatus[ZigbeeNWKStatus["LINK_FAILURE"] = 2] = "LINK_FAILURE"; // LOW_BATTERY = 0x03, // deprecated // NO_ROUTING = 0x04, // deprecated // NO_INDIRECT = 0x05, // deprecated // INDIRECT_EXPIRE = 0x06, // deprecated // DEVICE_UNAVAIL = 0x07, // deprecated // ADDR_UNAVAIL = 0x08, // deprecated /** * The failure occurred as a result of a failure in the RF link to the device’s parent. * This status is only used locally on a device to indicate loss of communication with the parent. */ ZigbeeNWKStatus[ZigbeeNWKStatus["PARENT_LINK_FAILURE"] = 9] = "PARENT_LINK_FAILURE"; // VALIDATE_ROUTE = 0x0a, // deprecated /** Source routing has failed, probably indicating a link failure in one of the source route’s links. */ ZigbeeNWKStatus[ZigbeeNWKStatus["SOURCE_ROUTE_FAILURE"] = 11] = "SOURCE_ROUTE_FAILURE"; /** A route established as a result of a many-to-one route request has failed. */ ZigbeeNWKStatus[ZigbeeNWKStatus["MANY_TO_ONE_ROUTE_FAILURE"] = 12] = "MANY_TO_ONE_ROUTE_FAILURE"; /** The address in the destination address field has been determined to be in use by two or more devices. */ ZigbeeNWKStatus[ZigbeeNWKStatus["ADDRESS_CONFLICT"] = 13] = "ADDRESS_CONFLICT"; // VERIFY_ADDRESS = 0x0e, // deprecated /** The operational network PAN identifier of the device has been updated. */ ZigbeeNWKStatus[ZigbeeNWKStatus["PANID_UPDATE"] = 15] = "PANID_UPDATE"; /** The network address of the local device has been updated. */ ZigbeeNWKStatus[ZigbeeNWKStatus["NETWORK_ADDRESS_UPDATE"] = 16] = "NETWORK_ADDRESS_UPDATE"; // BAD_FRAME_COUNTER = 0x11, // XXX: not in spec // BAD_KEY_SEQNO = 0x12, // XXX: not in spec /** The NWK command ID is not known to the device. */ ZigbeeNWKStatus[ZigbeeNWKStatus["UNKNOWN_COMMAND"] = 19] = "UNKNOWN_COMMAND"; /** Notification to the local application that a PAN ID Conflict Report has been received by the local Network Manager. */ ZigbeeNWKStatus[ZigbeeNWKStatus["PANID_CONFLICT_REPORT"] = 20] = "PANID_CONFLICT_REPORT"; // RESERVED = 0x15-0xff, })(ZigbeeNWKStatus || (exports.ZigbeeNWKStatus = ZigbeeNWKStatus = {})); /** * Decode Zigbee NWK frame control field. * HOT PATH: Called for every incoming Zigbee NWK frame. * 05-3474-23 R23.1, Table 3-19 (NWK frame control field) * * SPEC COMPLIANCE NOTES: * - ✅ Extracts protocol version, discover route, source route, and security bits per Zigbee PRO * - ✅ Surfaces end-device initiator bit introduced in r21 for parent-loss detection * - ⚠️ Leaves multicast control parsing to later stages since Zigbee seldom uses legacy flag * DEVICE SCOPE: All logical devices */ /* @__INLINE__ */ function decodeZigbeeNWKFrameControl(data, offset) { // HOT PATH: Extract NWK FCF fields with bitwise operations const fcf = data.readUInt16LE(offset); offset += 2; return [ { frameType: fcf & 3 /* ZigbeeNWKConsts.FCF_FRAME_TYPE */, protocolVersion: (fcf & 60 /* ZigbeeNWKConsts.FCF_VERSION */) >> 2, discoverRoute: (fcf & 192 /* ZigbeeNWKConsts.FCF_DISCOVER_ROUTE */) >> 6, multicast: Boolean((fcf & 256 /* ZigbeeNWKConsts.FCF_MULTICAST */) >> 8), security: Boolean((fcf & 512 /* ZigbeeNWKConsts.FCF_SECURITY */) >> 9), sourceRoute: Boolean((fcf & 1024 /* ZigbeeNWKConsts.FCF_SOURCE_ROUTE */) >> 10), extendedDestination: Boolean((fcf & 2048 /* ZigbeeNWKConsts.FCF_EXT_DEST */) >> 11), extendedSource: Boolean((fcf & 4096 /* ZigbeeNWKConsts.FCF_EXT_SOURCE */) >> 12), endDeviceInitiator: Boolean((fcf & 8192 /* ZigbeeNWKConsts.FCF_END_DEVICE_INITIATOR */) >> 13), }, offset, ]; } /** * 05-3474-23 R23.1, Table 3-19 (NWK frame control field) * * SPEC COMPLIANCE NOTES: * - ✅ Encodes NWK FCF bits honouring Zigbee PRO versioning and source route flags * - ✅ Keeps multicast flag optional but off by default to match Zigbee routing profile * - ⚠️ Requires caller to pre-validate discover route bits against frame type * DEVICE SCOPE: All logical devices */ function encodeZigbeeNWKFrameControl(view, offset, fcf) { offset = view.writeUInt16LE((fcf.frameType & 3 /* ZigbeeNWKConsts.FCF_FRAME_TYPE */) | ((fcf.protocolVersion << 2) & 60 /* ZigbeeNWKConsts.FCF_VERSION */) | ((fcf.discoverRoute << 6) & 192 /* ZigbeeNWKConsts.FCF_DISCOVER_ROUTE */) | (((fcf.multicast ? 1 : 0) << 8) & 256 /* ZigbeeNWKConsts.FCF_MULTICAST */) | (((fcf.security ? 1 : 0) << 9) & 512 /* ZigbeeNWKConsts.FCF_SECURITY */) | (((fcf.sourceRoute ? 1 : 0) << 10) & 1024 /* ZigbeeNWKConsts.FCF_SOURCE_ROUTE */) | (((fcf.extendedDestination ? 1 : 0) << 11) & 2048 /* ZigbeeNWKConsts.FCF_EXT_DEST */) | (((fcf.extendedSource ? 1 : 0) << 12) & 4096 /* ZigbeeNWKConsts.FCF_EXT_SOURCE */) | (((fcf.endDeviceInitiator ? 1 : 0) << 13) & 8192 /* ZigbeeNWKConsts.FCF_END_DEVICE_INITIATOR */), offset); return offset; } /** * 05-3474-23 R23.1, Tables 3-20/3-21 (NWK frame formats) * * SPEC COMPLIANCE NOTES: * - ✅ Applies frame-type specific field presence rules (extended addressing, source routes) * - ✅ Tracks radius/sequence to support routing loops and replay protection per spec * - ⚠️ Skips multicast control field body since Zigbee host does not emit legacy multicast frames * DEVICE SCOPE: All logical devices */ function decodeZigbeeNWKHeader(data, offset, frameControl) { let destination16; let source16; let radius; let seqNum; let destination64; let source64; let relayIndex; let relayAddresses; if (frameControl.frameType !== 3 /* ZigbeeNWKFrameType.INTERPAN */) { destination16 = data.readUInt16LE(offset); offset += 2; source16 = data.readUInt16LE(offset); offset += 2; radius = data.readUInt8(offset); offset += 1; seqNum = data.readUInt8(offset); offset += 1; if (frameControl.extendedDestination) { destination64 = data.readBigUInt64LE(offset); offset += 8; } if (frameControl.extendedSource) { source64 = data.readBigUInt64LE(offset); offset += 8; } if (frameControl.multicast) { offset += 1; } if (frameControl.sourceRoute) { const relayCount = data.readUInt8(offset); offset += 1; relayIndex = data.readUInt8(offset); offset += 1; relayAddresses = []; for (let i = 0; i < relayCount; i++) { relayAddresses.push(data.readUInt16LE(offset)); offset += 2; } } } if (offset >= data.byteLength) { throw new Error("Invalid NWK frame: no payload"); } return [ { frameControl, destination16, source16, radius, seqNum, destination64, source64, relayIndex, relayAddresses, securityHeader: undefined, // set later, or not }, offset, ]; } /** * 05-3474-23 R23.1, Tables 3-20/3-21 (NWK frame formats) * * SPEC COMPLIANCE NOTES: * - ✅ Serialises mandatory radius, sequence, and addressing fields for NWK data/command frames * - ✅ Encodes source routing list per Zigbee PRO requirements when enabled * - ⚠️ Expects caller to populate relay list indices already bounds-checked * DEVICE SCOPE: All logical devices */ function encodeZigbeeNWKHeader(data, offset, header) { offset = encodeZigbeeNWKFrameControl(data, offset, header.frameControl); if (header.frameControl.frameType !== 3 /* ZigbeeNWKFrameType.INTERPAN */) { offset = data.writeUInt16LE(header.destination16, offset); offset = data.writeUInt16LE(header.source16, offset); offset = data.writeUInt8(header.radius, offset); offset = data.writeUInt8(header.seqNum, offset); if (header.frameControl.extendedDestination) { offset = data.writeBigUInt64LE(header.destination64, offset); } if (header.frameControl.extendedSource) { offset = data.writeBigUInt64LE(header.source64, offset); } if (header.frameControl.sourceRoute) { offset = data.writeUInt8(header.relayAddresses.length, offset); offset = data.writeUInt8(header.relayIndex, offset); for (const relayAddress of header.relayAddresses) { offset = data.writeUInt16LE(relayAddress, offset); } } } return offset; } /** * * @param data * @param offset * @param decryptKey If undefined, use default pre-hashed * @param macSource64 * @param frameControl * @param header */ /** * 05-3474-23 R23.1, Annex A (NWK security) * * SPEC COMPLIANCE NOTES: * - ✅ Invokes CCM* decrypt when security bit set, storing auxiliary header for Trust Center use * - ✅ Supports fallback to default hashed NWK keys when dedicated key not supplied * - ⚠️ Defers key selection policy (global vs. unique) to higher stack layers * DEVICE SCOPE: All logical devices */ function decodeZigbeeNWKPayload(data, offset, decryptKey, macSource64, frameControl, header) { if (frameControl.security) { const [payload, securityHeader, dOutOffset] = (0, zigbee_js_1.decryptZigbeePayload)(data, offset, decryptKey, macSource64); offset = dOutOffset; header.securityHeader = securityHeader; return payload; } return data.subarray(offset); } /** * @param header * @param payload * @param securityHeader * @param encryptKey If undefined, and security=true, use default pre-hashed */ /** * 05-3474-23 R23.1, Table 3-20 (NWK data frame format) & Annex A (NWK security) * * SPEC COMPLIANCE NOTES: * - ✅ Constructs NWK frame then encrypts/authenticates payload per Zigbee security flag * - ✅ Copies authentication tag trailing bytes as mandated by CCM* * - ⚠️ Caller must ensure payload length respects NWK/APS combined MTU when security enabled * DEVICE SCOPE: All logical devices */ function encodeZigbeeNWKFrame(header, payload, securityHeader, encryptKey) { let offset = 0; const data = Buffer.alloc(116 /* ZigbeeNWKConsts.FRAME_MAX_SIZE */); offset = encodeZigbeeNWKHeader(data, offset, header); if (header.frameControl.security) { const [cryptedPayload, authTag, eOutOffset] = (0, zigbee_js_1.encryptZigbeePayload)(data, offset, payload, securityHeader, encryptKey); offset = eOutOffset; offset += cryptedPayload.copy(data, offset); offset += authTag.copy(data, offset); return data.subarray(0, offset); } offset += payload.copy(data, offset); return data.subarray(0, offset); } //# sourceMappingURL=zigbee-nwk.js.map