class ZigbeeNWK(Packet): name = "Zigbee Network Layer" fields_desc = [ BitField("discover_route", 0, 2), BitField("proto_version", 2, 4), BitEnumField("frametype", 0, 2, {0:'data', 1:'command'}), FlagsField("flags", 0, 8, ['multicast', 'security', 'source_route', 'extended_dst', 'extended_src', 'reserved1', 'reserved2', 'reserved3']), XLEShortField("destination", 0), XLEShortField("source", 0), ByteField("radius", 0), ByteField("seqnum", 1), ConditionalField(ByteField("relay_count", 1), lambda pkt:pkt.flags & 0x04), ConditionalField(ByteField("relay_index", 0), lambda pkt:pkt.flags & 0x04), ConditionalField(FieldListField("relays", [ ], XLEShortField("", 0x0000), count_from = lambda pkt:pkt.relay_count), lambda pkt:pkt.flags & 0x04), #ConditionalField(XLongField("ext_dst", 0), lambda pkt:pkt.flags & 8), ConditionalField(dot15d4AddressField("ext_dst", 0, adjust=lambda pkt,x: 8), lambda pkt:pkt.flags & 8), #ConditionalField(XLongField("ext_src", 0), lambda pkt:pkt.flags & 16), ConditionalField(dot15d4AddressField("ext_src", 0, adjust=lambda pkt,x: 8), lambda pkt:pkt.flags & 16), ] def guess_payload_class(self, payload): if self.flags & 0x02: return ZigbeeSecurityHeader elif self.frametype == 0: return ZigbeeAppDataPayload elif self.frametype == 1: return ZigbeeNWKCommandPayload else: return Packet.guess_payload_class(self, payload)
class ZigbeeAppCommandPayload(Packet): name = "Zigbee Application Layer Command Payload" fields_desc = [ ByteEnumField("cmd_identifier", 1, { 1: "APS_CMD_SKKE_1", 2: "APS_CMD_SKKE_2", 3: "APS_CMD_SKKE_3", 4: "APS_CMD_SKKE_4", 5: "APS_CMD_TRANSPORT_KEY", 6: "APS_CMD_UPDATE_DEVICE", 7: "APS_CMD_REMOVE_DEVICE", 8: "APS_CMD_REQUEST_KEY", 9: "APS_CMD_SWITCH_KEY", 10: "APS_CMD_EA_INIT_CHLNG", 11: "APS_CMD_EA_RSP_CHLNG", 12: "APS_CMD_EA_INIT_MAC_DATA", 13: "APS_CMD_EA_RSP_MAC_DATA", 14: "APS_CMD_TUNNEL" }), ConditionalField(StrFixedLenField("key", None, 16), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField(ByteField("key_seqnum", 0), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField(dot15d4AddressField("dest_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField(dot15d4AddressField("src_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 1), # noqa: E501 ConditionalField(StrField("data", ""), lambda pkt: pkt.cmd_identifier != 1) # noqa: E501 ]
class ZigbeeNWK(Packet): name = "Zigbee Network Layer" fields_desc = [ BitField("discover_route", 0, 2), BitField("proto_version", 2, 4), BitEnumField("frametype", 0, 2, { 0: 'data', 1: 'command', 3: 'Inter-PAN' }), FlagsField("flags", 0, 8, [ 'multicast', 'security', 'source_route', 'extended_dst', 'extended_src', 'reserved1', 'reserved2', 'reserved3' ]), # noqa: E501 XLEShortField("destination", 0), XLEShortField("source", 0), ByteField("radius", 0), ByteField("seqnum", 1), # ConditionalField(XLongField("ext_dst", 0), lambda pkt:pkt.flags & 8), ConditionalField( dot15d4AddressField("ext_dst", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.flags & 8), # noqa: E501 ConditionalField( dot15d4AddressField("ext_src", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.flags & 16), # noqa: E501 ConditionalField(ByteField("relay_count", 1), lambda pkt: pkt.flags & 0x04), # noqa: E501 ConditionalField(ByteField("relay_index", 0), lambda pkt: pkt.flags & 0x04), # noqa: E501 ConditionalField( FieldListField("relays", [], XLEShortField("", 0x0000), count_from=lambda pkt: pkt.relay_count), lambda pkt: pkt.flags & 0x04), # noqa: E501 ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: frametype = ord(_pkt[:1]) & 3 if frametype == 3: return ZigbeeNWKStub return cls def guess_payload_class(self, payload): if self.flags.security: return ZigbeeSecurityHeader elif self.frametype == 0: return ZigbeeAppDataPayload elif self.frametype == 1: return ZigbeeNWKCommandPayload else: return Packet.guess_payload_class(self, payload)
class ZigBeeBeacon(Packet): name = "ZigBee Beacon Payload" fields_desc = [ # Protocol ID (1 octet) ByteField("proto_id", 0), # nwkcProtocolVersion (4 bits) BitField("nwkc_protocol_version", 0, 4), # Stack profile (4 bits) BitField("stack_profile", 0, 4), # End device capacity (1 bit) BitField("end_device_capacity", 0, 1), # Device depth (4 bits) BitField("device_depth", 0, 4), # Router capacity (1 bit) BitField("router_capacity", 0, 1), # Reserved (2 bits) BitField("reserved", 0, 2), # Extended PAN ID (8 octets) dot15d4AddressField("extended_pan_id", 0, adjust=lambda pkt, x: 8), # Tx offset (3 bytes) # In ZigBee 2006 the Tx-Offset is optional, while in the 2007 and later versions, the Tx-Offset is a required value. BitField("tx_offset", 0, 24), # Update ID (1 octet) ByteField("update_id", 0), ]
class ZigbeeSecurityHeader(Packet): name = "Zigbee Security Header" fields_desc = [ # Security control (1 octet) HiddenField(FlagsField("reserved1", 0, 2, [ 'reserved1', 'reserved2' ]), True), BitField("extended_nonce", 1, 1), # set to 1 if the sender address field is present (source) # Key identifier BitEnumField("key_type", 1, 2, { 0:'data_key', 1:'network_key', 2:'key_transport_key', 3:'key_load_key' }), # Security level (3 bits) BitEnumField("nwk_seclevel", 0, 3, { 0:"None", 1:"MIC-32", 2:"MIC-64", 3:"MIC-128", 4:"ENC", 5:"ENC-MIC-32", 6:"ENC-MIC-64", 7:"ENC-MIC-128" }), # Frame counter (4 octets) XLEIntField("fc", 0), # provide frame freshness and prevent duplicate frames # Source address (0/8 octets) ConditionalField(dot15d4AddressField("source", 0, adjust=lambda pkt,x: 8), lambda pkt:pkt.extended_nonce), # Key sequence number (0/1 octet): only present when key identifier is 1 (network key) ConditionalField(ByteField("key_seqnum", 0), lambda pkt:pkt.getfieldval("key_type") == 1), # Payload # the length of the encrypted data is the payload length minus the MIC ZigbeePayloadField("data", "", length_from=lambda pkt, s:len(s)-ZigbeeSecurityHeader.util_mic_len(pkt) ), # Message Integrity Code (0/variable in size), length depends on nwk_seclevel StrLenField("mic", "", length_from=lambda pkt:ZigbeeSecurityHeader.util_mic_len(pkt) ), ] def util_mic_len(pkt): ''' Calculate the length of the attribute value field ''' if ( pkt.nwk_seclevel == 0 ): # no encryption, no mic return 0 elif ( pkt.nwk_seclevel == 1 ): # MIC-32 return 4 elif ( pkt.nwk_seclevel == 2 ): # MIC-64 return 8 elif ( pkt.nwk_seclevel == 3 ): # MIC-128 return 16 elif ( pkt.nwk_seclevel == 4 ): # ENC return 0 elif ( pkt.nwk_seclevel == 5 ): # ENC-MIC-32 return 4 elif ( pkt.nwk_seclevel == 6 ): # ENC-MIC-64 return 8 elif ( pkt.nwk_seclevel == 7 ): # ENC-MIC-128 return 16 else: return 0
class ZigbeeSecurityHeader2(Packet): name = "Zigbee Security Header" fields_desc = [ # Security control (1 octet) FlagsField("reserved1", 0, 2, ['reserved1', 'reserved2']), BitField( "extended_nonce", 1, 1 ), # set to 1 if the sender address field is present (source) # noqa: E501 # Key identifier BitEnumField( "key_type", 1, 2, { 0: 'data_key', 1: 'network_key', 2: 'key_transport_key', 3: 'key_load_key' }), # Security level (3 bits) BitEnumField( "nwk_seclevel", 0, 3, { 0: "None", 1: "MIC-32", 2: "MIC-64", 3: "MIC-128", 4: "ENC", 5: "ENC-MIC-32", 6: "ENC-MIC-64", 7: "ENC-MIC-128" }), # Frame counter (4 octets) XLEIntField( "fc", 0 ), # provide frame freshness and prevent duplicate frames # noqa: E501 # Source address (0/8 octets) ConditionalField( dot15d4AddressField("source", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.extended_nonce), # noqa: E501 # Key sequence number (0/1 octet): only present when key identifier is 1 (network key) # noqa: E501 ConditionalField( ByteField("key_seqnum", 0), lambda pkt: pkt.getfieldval("key_type") == 1), # noqa: E501 # Payload # the length of the encrypted data is the payload length minus the MIC StrField("data", ""), # noqa: E501 # Message Integrity Code (0/variable in size), length depends on nwk_seclevel # noqa: E501 IntField("mic", ""), ] def post_dissect(self, s): # Get the mic dissected correctly mic_length = util_mic_len(self) if mic_length > 0: # Slice "data" into "data + mic" _data, _mic = self.data[:-mic_length], self.data[-mic_length:] self.data, self.mic = _data, _mic return s
class ZigbeeDeviceProfile(Packet): name = "Zigbee Device Profile (ZDP) frame" fields_desc = [ # sequence number (8 bits) ByteField("sequence_number", 0), # Device short address dot15d4AddressField("extended_address", 0, adjust=lambda pkt, x: 8), # flags 1 octet FlagsField("frame_control", 0, 8, [ 'reserved1', 'reserved2', 'reserved3', 'reserved4', 'remove_children', 'rejoin' ]), ] def guess_payload_class(self, payload): return Packet.guess_payload_class(self, payload)
class ZigbeeNWKCommandPayload(Packet): name = "Zigbee Network Layer Command Payload" fields_desc = [ ByteEnumField( "cmd_identifier", 1, { 1: "route request", 2: "route reply", 3: "network status", 4: "leave", 5: "route record", 6: "rejoin request", 7: "rejoin response", 8: "link status", 9: "network report", 10: "network update" # 0x0b - 0xff reserved }), ### Route Request Command ### # Command options (1 octet) ConditionalField(BitField("reserved", 0, 1), lambda pkt: pkt.cmd_identifier == 1), ConditionalField(BitField("multicast", 0, 1), lambda pkt: pkt.cmd_identifier == 1), ConditionalField(BitField("dest_addr_bit", 0, 1), lambda pkt: pkt.cmd_identifier == 1), ConditionalField( BitEnumField( "many_to_one", 0, 2, { 0: "not_m2one", 1: "m2one_support_rrt", 2: "m2one_no_support_rrt", 3: "reserved" }), lambda pkt: pkt.cmd_identifier == 1), ConditionalField(BitField("reserved", 0, 3), lambda pkt: pkt.cmd_identifier == 1), # Route request identifier (1 octet) ConditionalField(ByteField("route_request_identifier", 0), lambda pkt: pkt.cmd_identifier == 1), # Destination address (2 octets) ConditionalField(XLEShortField("destination_address", 0x0000), lambda pkt: pkt.cmd_identifier == 1), # Path cost (1 octet) ConditionalField(ByteField("path_cost", 0), lambda pkt: pkt.cmd_identifier == 1), # Destination IEEE Address (0/8 octets), only present when dest_addr_bit has a value of 1 ConditionalField( dot15d4AddressField("ext_dst", 0, adjust=lambda pkt, x: 8), lambda pkt: (pkt.cmd_identifier == 1 and pkt.dest_addr_bit == 1)), ### Route Reply Command ### # Command options (1 octet) ConditionalField(BitField("reserved", 0, 1), lambda pkt: pkt.cmd_identifier == 2), ConditionalField(BitField("multicast", 0, 1), lambda pkt: pkt.cmd_identifier == 2), ConditionalField(BitField("responder_addr_bit", 0, 1), lambda pkt: pkt.cmd_identifier == 2), ConditionalField(BitField("originator_addr_bit", 0, 1), lambda pkt: pkt.cmd_identifier == 2), ConditionalField(BitField("reserved", 0, 4), lambda pkt: pkt.cmd_identifier == 2), # Route request identifier (1 octet) ConditionalField(ByteField("route_request_identifier", 0), lambda pkt: pkt.cmd_identifier == 2), # Originator address (2 octets) ConditionalField(XLEShortField("originator_address", 0x0000), lambda pkt: pkt.cmd_identifier == 2), # Responder address (2 octets) ConditionalField(XLEShortField("responder_address", 0x0000), lambda pkt: pkt.cmd_identifier == 2), # Path cost (1 octet) ConditionalField(ByteField("path_cost", 0), lambda pkt: pkt.cmd_identifier == 2), # Originator IEEE address (0/8 octets) ConditionalField( dot15d4AddressField("originator_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: (pkt.cmd_identifier == 2 and pkt.originator_addr_bit == 1)), # Responder IEEE address (0/8 octets) ConditionalField( dot15d4AddressField("responder_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: (pkt.cmd_identifier == 2 and pkt.responder_addr_bit == 1)), ### Network Status Command ### # Status code (1 octet) ConditionalField( ByteEnumField( "status_code", 0, { 0x00: "No route available", 0x01: "Tree link failure", 0x02: "Non-tree link failure", 0x03: "Low battery level", 0x04: "No routing capacity", 0x05: "No indirect capacity", 0x06: "Indirect transaction expiry", 0x07: "Target device unavailable", 0x08: "Target address unallocated", 0x09: "Parent link failure", 0x0a: "Validate route", 0x0b: "Source route failure", 0x0c: "Many-to-one route failure", 0x0d: "Address conflict", 0x0e: "Verify addresses", 0x0f: "PAN identifier update", 0x10: "Network address update", 0x11: "Bad frame counter", 0x12: "Bad key sequence number", # 0x13 - 0xff Reserved }), lambda pkt: pkt.cmd_identifier == 3), # Destination address (2 octets) ConditionalField(XLEShortField("destination_address", 0x0000), lambda pkt: pkt.cmd_identifier == 3), ### Leave Command ### # Command options (1 octet) # Bit 7: Remove children ConditionalField(BitField("remove_children", 0, 1), lambda pkt: pkt.cmd_identifier == 4), # Bit 6: Request ConditionalField(BitField("request", 0, 1), lambda pkt: pkt.cmd_identifier == 4), # Bit 5: Rejoin ConditionalField(BitField("rejoin", 0, 1), lambda pkt: pkt.cmd_identifier == 4), # Bit 0 - 4: Reserved ConditionalField(BitField("reserved", 0, 5), lambda pkt: pkt.cmd_identifier == 4), ### Route Record Command ### # Relay count (1 octet) ConditionalField(ByteField("rr_relay_count", 0), lambda pkt: pkt.cmd_identifier == 5), # Relay list (variable in length) ConditionalField( FieldListField("rr_relay_list", [], XLEShortField("", 0x0000), count_from=lambda pkt: pkt.rr_relay_count), lambda pkt: pkt.cmd_identifier == 5), ### Rejoin Request Command ### # Capability Information (1 octet) ConditionalField( BitField("allocate_address", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # Allocate Address ConditionalField( BitField("security_capability", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # Security Capability ConditionalField(BitField( "reserved2", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # bit 5 is reserved ConditionalField(BitField( "reserved1", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # bit 4 is reserved ConditionalField( BitField("receiver_on_when_idle", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # Receiver On When Idle ConditionalField(BitField("power_source", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # Power Source ConditionalField(BitField("device_type", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # Device Type ConditionalField( BitField("alternate_pan_coordinator", 0, 1), lambda pkt: pkt.cmd_identifier == 6), # Alternate PAN Coordinator ### Rejoin Response Command ### # Network address (2 octets) ConditionalField(XLEShortField("network_address", 0xFFFF), lambda pkt: pkt.cmd_identifier == 7), # Rejoin status (1 octet) ConditionalField(ByteField("rejoin_status", 0), lambda pkt: pkt.cmd_identifier == 7), ### Link Status Command ### # Command options (1 octet) ConditionalField(BitField("reserved", 0, 1), lambda pkt: pkt.cmd_identifier == 8), # Reserved ConditionalField(BitField("last_frame", 0, 1), lambda pkt: pkt.cmd_identifier == 8), # Last frame ConditionalField(BitField("first_frame", 0, 1), lambda pkt: pkt.cmd_identifier == 8), # First frame ConditionalField(BitField("entry_count", 0, 5), lambda pkt: pkt.cmd_identifier == 8), # Entry count # Link status list (variable size) ConditionalField( PacketListField("link_status_list", [], LinkStatusEntry, count_from=lambda pkt: pkt.entry_count), lambda pkt: pkt.cmd_identifier == 8), ### Network Report Command ### # Command options (1 octet) ConditionalField( BitEnumField( "report_command_identifier", 0, 3, {0: "PAN identifier conflict"}), # 0x01 - 0x07 Reserved lambda pkt: pkt.cmd_identifier == 9), ConditionalField(BitField("report_information_count", 0, 5), lambda pkt: pkt.cmd_identifier == 9), # EPID: Extended PAN ID (8 octets) ConditionalField( dot15d4AddressField("epid", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 9), # Report information (variable length) # Only present if we have a PAN Identifier Conflict Report ConditionalField( FieldListField( "PAN_ID_conflict_report", [], XLEShortField("", 0x0000), count_from=lambda pkt: pkt.report_information_count), lambda pkt: (pkt.cmd_identifier == 9 and pkt.report_command_identifier == 0)), ### Network Update Command ### # Command options (1 octet) ConditionalField( BitEnumField("update_command_identifier", 0, 3, {0: "PAN Identifier Update"}), # 0x01 - 0x07 Reserved lambda pkt: pkt.cmd_identifier == 10), ConditionalField(BitField("update_information_count", 0, 5), lambda pkt: pkt.cmd_identifier == 10), # EPID: Extended PAN ID (8 octets) ConditionalField( dot15d4AddressField("epid", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 10), # Update Id (1 octet) ConditionalField(ByteField("update_id", 0), lambda pkt: pkt.cmd_identifier == 10), # Update Information (Variable) # Only present if we have a PAN Identifier Update # New PAN ID (2 octets) ConditionalField( XLEShortField("new_PAN_ID", 0x0000), lambda pkt: (pkt.cmd_identifier == 10 and pkt.update_command_identifier == 0)), #ZigbeePayloadField("data", "", length_from=lambda pkt, s:len(s)), ]
class ZigbeeAppCommandPayload(Packet): name = "Zigbee Application Layer Command Payload" fields_desc = [ ByteEnumField( "cmd_identifier", 1, { 1: "APS_CMD_SKKE_1", 2: "APS_CMD_SKKE_2", 3: "APS_CMD_SKKE_3", 4: "APS_CMD_SKKE_4", 5: "APS_CMD_TRANSPORT_KEY", 6: "APS_CMD_UPDATE_DEVICE", 7: "APS_CMD_REMOVE_DEVICE", 8: "APS_CMD_REQUEST_KEY", 9: "APS_CMD_SWITCH_KEY", # TODO: implement 10 to 14 10: "APS_CMD_EA_INIT_CHLNG", 11: "APS_CMD_EA_RSP_CHLNG", 12: "APS_CMD_EA_INIT_MAC_DATA", 13: "APS_CMD_EA_RSP_MAC_DATA", 14: "APS_CMD_TUNNEL" }), # SKKE Commands ConditionalField( dot15d4AddressField("initiator", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier in [1, 2, 3, 4]), ConditionalField( dot15d4AddressField("responder", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier in [1, 2, 3, 4]), ConditionalField(StrFixedLenField("data", 0, length=16), lambda pkt: pkt.cmd_identifier in [1, 2, 3, 4]), # Transport-key Command ConditionalField(ByteEnumField("key_type", 0, _TransportKeyKeyTypes), lambda pkt: pkt.cmd_identifier == 5), ConditionalField(StrFixedLenField("key", None, 16), lambda pkt: pkt.cmd_identifier == 5), ConditionalField( ByteField("key_seqnum", 0), lambda pkt: (pkt.cmd_identifier == 5 and pkt.key_type in [0x01, 0x05])), ConditionalField( dot15d4AddressField("dest_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 5), ConditionalField( dot15d4AddressField("src_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 5), # Update-Device Command ConditionalField( dot15d4AddressField("address", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 6), ConditionalField(XLEShortField("short_address", 0), lambda pkt: pkt.cmd_identifier == 6), ConditionalField(ByteField("status", 0), lambda pkt: pkt.cmd_identifier == 6), # Remove-Device Command ConditionalField( dot15d4AddressField("address", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier == 7), # Request-Key Command ConditionalField(ByteEnumField("key_type", 0, _TransportKeyKeyTypes), lambda pkt: pkt.cmd_identifier == 8), ConditionalField(StrFixedLenField("key", None, 16), lambda pkt: pkt.cmd_identifier == 8), # Switch-Key Command ConditionalField(StrFixedLenField("seqnum", None, 8), lambda pkt: pkt.cmd_identifier == 9), # Un-implemented: 10-14 (+?) ConditionalField( StrField("data", ""), lambda pkt: (pkt.cmd_identifier < 0 or pkt.cmd_identifier > 9)) ]
class ZigbeeAppCommandPayload(Packet): name = "Zigbee Application Layer Command Payload" fields_desc = [ ByteEnumField("cmd_identifier", 1, { 1: "APS_CMD_SKKE_1", 2: "APS_CMD_SKKE_2", 3: "APS_CMD_SKKE_3", 4: "APS_CMD_SKKE_4", 5: "APS_CMD_TRANSPORT_KEY", 6: "APS_CMD_UPDATE_DEVICE", 7: "APS_CMD_REMOVE_DEVICE", 8: "APS_CMD_REQUEST_KEY", 9: "APS_CMD_SWITCH_KEY", # TODO: implement 10 to 13 10: "APS_CMD_EA_INIT_CHLNG", 11: "APS_CMD_EA_RSP_CHLNG", 12: "APS_CMD_EA_INIT_MAC_DATA", 13: "APS_CMD_EA_RSP_MAC_DATA", 14: "APS_CMD_TUNNEL", 15: "APS_CMD_VERIFY_KEY", 16: "APS_CMD_CONFIRM_KEY" }), # SKKE Commands ConditionalField(dot15d4AddressField("initiator", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier in [1, 2, 3, 4]), ConditionalField(dot15d4AddressField("responder", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier in [1, 2, 3, 4]), ConditionalField(StrFixedLenField("data", 0, length=16), lambda pkt: pkt.cmd_identifier in [1, 2, 3, 4]), # Confirm-key command ConditionalField( ByteEnumField("status", 0, _ApsStatusValues), lambda pkt: pkt.cmd_identifier == 16), # Common fields ConditionalField( ByteEnumField("key_type", 0, _TransportKeyKeyTypes), lambda pkt: pkt.cmd_identifier in [5, 8, 15, 16]), ConditionalField(dot15d4AddressField("address", 0, adjust=lambda pkt, x: 8), lambda pkt: pkt.cmd_identifier in [6, 7, 15, 16]), # Transport-key Command ConditionalField( StrFixedLenField("key", None, 16), lambda pkt: pkt.cmd_identifier == 5), ConditionalField( ByteField("key_seqnum", 0), lambda pkt: (pkt.cmd_identifier == 5 and pkt.key_type in [0x01, 0x05])), ConditionalField( dot15d4AddressField("dest_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: ((pkt.cmd_identifier == 5 and pkt.key_type not in [0x02, 0x03]) or pkt.cmd_identifier == 14)), ConditionalField( dot15d4AddressField("src_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: (pkt.cmd_identifier == 5 and pkt.key_type not in [0x02, 0x03])), ConditionalField( dot15d4AddressField("partner_addr", 0, adjust=lambda pkt, x: 8), lambda pkt: ((pkt.cmd_identifier == 5 and pkt.key_type in [0x02, 0x03]) or (pkt.cmd_identifier == 8 and pkt.key_type == 0x02))), ConditionalField( ByteField("initiator_flag", 0), lambda pkt: (pkt.cmd_identifier == 5 and pkt.key_type in [0x02, 0x03])), # Update-Device Command ConditionalField(XLEShortField("short_address", 0), lambda pkt: pkt.cmd_identifier == 6), ConditionalField(ByteField("update_status", 0), lambda pkt: pkt.cmd_identifier == 6), # Switch-Key Command ConditionalField(StrFixedLenField("seqnum", None, 8), lambda pkt: pkt.cmd_identifier == 9), # Un-implemented: 10-13 (+?) ConditionalField(StrField("unimplemented", ""), lambda pkt: (pkt.cmd_identifier >= 10 and pkt.cmd_identifier <= 13)), # Tunnel Command ConditionalField( FlagsField("frame_control", 2, 4, [ "ack_format", "security", "ack_req", "extended_hdr" ]), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( BitEnumField("delivery_mode", 0, 2, { 0: "unicast", 1: "indirect", 2: "broadcast", 3: "group_addressing" }), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( BitEnumField("aps_frametype", 1, 2, { 0: "data", 1: "command", 2: "ack" }), lambda pkt: pkt.cmd_identifier == 14), ConditionalField( ByteField("counter", 0), lambda pkt: pkt.cmd_identifier == 14), # Verify-Key Command ConditionalField( StrFixedLenField("key_hash", None, 16), lambda pkt: pkt.cmd_identifier == 15), ] def guess_payload_class(self, payload): if self.cmd_identifier == 14: # Tunneled APS Auxiliary Header return ZigbeeSecurityHeader else: return Packet.guess_payload_class(self, payload)