/* packet-zep.c * Dissector routines for the ZigBee Encapsulation Protocol * By Owen Kirby * Copyright 2009 Exegin Technologies Limited * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later *------------------------------------------------------------ * * ZEP Packets must be received in the following format: * |UDP Header| ZEP Header |IEEE 802.15.4 Packet| * | 8 bytes | 16/32 bytes | <= 127 bytes | *------------------------------------------------------------ * * ZEP v1 Header will have the following format: * |Preamble|Version|Channel ID|Device ID|CRC/LQI Mode|LQI Val|Reserved|Length| * |2 bytes |1 byte | 1 byte | 2 bytes | 1 byte |1 byte |7 bytes |1 byte| * * ZEP v2 Header will have the following format (if type=1/Data): * |Preamble|Version| Type |Channel ID|Device ID|CRC/LQI Mode|LQI Val|NTP Timestamp|Sequence#|Reserved|Length| * |2 bytes |1 byte |1 byte| 1 byte | 2 bytes | 1 byte |1 byte | 8 bytes | 4 bytes |10 bytes|1 byte| * * ZEP v2 Header will have the following format (if type=2/Ack): * |Preamble|Version| Type |Sequence#| * |2 bytes |1 byte |1 byte| 4 bytes | *------------------------------------------------------------ */ #include "config.h" #include #include #include #include /* Function declarations */ void proto_reg_handoff_zep(void); void proto_register_zep(void); #define ZEP_DEFAULT_PORT 17754 /* ZEP Preamble Code */ #define ZEP_PREAMBLE "EX" /* ZEP Header lengths. */ #define ZEP_V1_HEADER_LEN 16 #define ZEP_V2_HEADER_LEN 32 #define ZEP_V2_ACK_LEN 8 #define ZEP_V2_TYPE_DATA 1 #define ZEP_V2_TYPE_ACK 2 #define ZEP_LENGTH_MASK 0x7F static const range_string type_rvals[] = { {0, 0, "Reserved"}, {ZEP_V2_TYPE_DATA, ZEP_V2_TYPE_DATA, "Data"}, {ZEP_V2_TYPE_ACK, ZEP_V2_TYPE_ACK, "Ack"}, {3, 255, "Reserved" }, {0, 0, NULL} }; static const true_false_string tfs_crc_lqi = { "CRC", "LQI" }; /* Initialize protocol and registered fields. */ static int proto_zep; static int hf_zep_version; static int hf_zep_type; static int hf_zep_channel_id; static int hf_zep_device_id; static int hf_zep_lqi_mode; static int hf_zep_lqi; static int hf_zep_timestamp; static int hf_zep_seqno; static int hf_zep_ieee_length; static int hf_zep_protocol_id; static int hf_zep_reserved_field; /* Initialize protocol subtrees. */ static int ett_zep; /* Dissector handle */ static dissector_handle_t zep_handle; /* Subdissector handles */ static dissector_handle_t ieee802154_handle; static dissector_handle_t ieee802154_cc24xx_handle; /*FUNCTION:------------------------------------------------------ * NAME * dissect_zep * DESCRIPTION * IEEE 802.15.4 packet dissection routine for Wireshark. * PARAMETERS * tvbuff_t *tvb - pointer to buffer containing raw packet. * packet_info *pinfo - pointer to packet information fields * proto_tree *tree - pointer to data tree Wireshark uses to display packet. * RETURNS * void *--------------------------------------------------------------- */ static int dissect_zep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { tvbuff_t *next_tvb; proto_item *proto_root; proto_tree *zep_tree; uint8_t ieee_packet_len; uint8_t zep_header_len; uint8_t version; uint8_t type; uint32_t channel_id, seqno; bool lqi_mode = false; dissector_handle_t next_dissector; if (tvb_reported_length(tvb) < ZEP_V2_ACK_LEN) return 0; /* Determine whether this is a Q51/IEEE 802.15.4 sniffer packet or not */ if(strcmp(tvb_get_string_enc(pinfo->pool, tvb, 0, 2, ENC_ASCII), ZEP_PREAMBLE)){ /* This is not a Q51/ZigBee sniffer packet */ return 0; } /* Extract the protocol version from the ZEP header. */ version = tvb_get_uint8(tvb, 2); if (version == 1) { /* Type indicates a ZEP_v1 packet. */ zep_header_len = ZEP_V1_HEADER_LEN; if (tvb_reported_length(tvb) < ZEP_V1_HEADER_LEN) return 0; type = 0; ieee_packet_len = (tvb_get_uint8(tvb, ZEP_V1_HEADER_LEN - 1) & ZEP_LENGTH_MASK); } else { /* At the time of writing, v2 is the latest version of ZEP, assuming * anything higher than v2 has identical format. */ type = tvb_get_uint8(tvb, 3); if (type == ZEP_V2_TYPE_ACK) { /* ZEP Ack has only the seqno. */ zep_header_len = ZEP_V2_ACK_LEN; ieee_packet_len = 0; } else { /* Although, only type 1 corresponds to data, if another value is present, assume it is dissected the same. */ zep_header_len = ZEP_V2_HEADER_LEN; if (tvb_reported_length(tvb) < ZEP_V2_HEADER_LEN) return 0; ieee_packet_len = (tvb_get_uint8(tvb, ZEP_V2_HEADER_LEN - 1) & ZEP_LENGTH_MASK); } } if(ieee_packet_len < tvb_reported_length(tvb)-zep_header_len){ /* Packet's length is mis-reported, abort dissection */ return 0; } col_set_str(pinfo->cinfo, COL_PROTOCOL, (version==1)?"ZEP":"ZEPv2"); proto_root = proto_tree_add_item(tree, proto_zep, tvb, 0, zep_header_len, ENC_NA); zep_tree = proto_item_add_subtree(proto_root, ett_zep); proto_tree_add_item(zep_tree, hf_zep_protocol_id, tvb, 0, 2, ENC_NA|ENC_ASCII); proto_tree_add_uint(zep_tree, hf_zep_version, tvb, 2, 1, version); switch (version) { case 1: proto_tree_add_item_ret_uint(zep_tree, hf_zep_channel_id, tvb, 3, 1, ENC_NA, &channel_id); col_add_fstr(pinfo->cinfo, COL_INFO, "Encapsulated ZigBee Packet [Channel]=%u [Length]=%u", channel_id, ieee_packet_len); proto_item_append_text(proto_root, ", Channel: %u, Length: %u", channel_id, ieee_packet_len); proto_tree_add_item(zep_tree, hf_zep_device_id, tvb, 4, 2, ENC_BIG_ENDIAN); proto_tree_add_item_ret_boolean(zep_tree, hf_zep_lqi_mode, tvb, 6, 1, ENC_NA, &lqi_mode); if (lqi_mode != 0) { proto_tree_add_item(zep_tree, hf_zep_lqi, tvb, 7, 1, ENC_NA); proto_tree_add_item(zep_tree, hf_zep_reserved_field, tvb, 8, 8, ENC_NA); } else { proto_tree_add_item(zep_tree, hf_zep_reserved_field, tvb, 7, 9, ENC_NA); } proto_tree_add_item(zep_tree, hf_zep_ieee_length, tvb, ZEP_V1_HEADER_LEN - 1, 1, ENC_NA); break; case 2: default: proto_tree_add_uint(zep_tree, hf_zep_type, tvb, 3, 1, type); if (type == ZEP_V2_TYPE_ACK) { proto_tree_add_item_ret_uint(zep_tree, hf_zep_seqno, tvb, 4, 4, ENC_BIG_ENDIAN, &seqno); col_add_fstr(pinfo->cinfo, COL_INFO, "Ack, Sequence Number: %i", seqno); proto_item_append_text(proto_root, ", Ack"); } else { proto_tree_add_item_ret_uint(zep_tree, hf_zep_channel_id, tvb, 4, 1, ENC_NA, &channel_id); col_add_fstr(pinfo->cinfo, COL_INFO, "Encapsulated ZigBee Packet [Channel]=%u [Length]=%u", channel_id, ieee_packet_len); proto_item_append_text(proto_root, ", Channel: %u, Length: %u", channel_id, ieee_packet_len); proto_tree_add_item(zep_tree, hf_zep_device_id, tvb, 5, 2, ENC_BIG_ENDIAN); proto_tree_add_item_ret_boolean(zep_tree, hf_zep_lqi_mode, tvb, 7, 1, ENC_NA, &lqi_mode); if (lqi_mode == 0) { proto_tree_add_item(zep_tree, hf_zep_lqi, tvb, 8, 1, ENC_NA); } proto_tree_add_item(zep_tree, hf_zep_timestamp, tvb, 9, 8, ENC_BIG_ENDIAN|ENC_TIME_NTP); proto_tree_add_item(zep_tree, hf_zep_seqno, tvb, 17, 4, ENC_BIG_ENDIAN); proto_tree_add_item(zep_tree, hf_zep_ieee_length, tvb, ZEP_V2_HEADER_LEN - 1, 1, ENC_NA); } break; } /* Determine which dissector to call next. */ if (lqi_mode) { /* CRC present, use standard IEEE dissector. * XXX - 2-octet or 4-octet CRC? */ next_dissector = ieee802154_handle; } else { /* ChipCon/TI CC24xx-compliant metadata present, CRC absent */ next_dissector = ieee802154_cc24xx_handle; } /* Call the appropriate IEEE 802.15.4 dissector */ if (!((version>=2) && (type==ZEP_V2_TYPE_ACK))) { next_tvb = tvb_new_subset_length(tvb, zep_header_len, ieee_packet_len); if (next_dissector != NULL) { call_dissector(next_dissector, next_tvb, pinfo, tree); } else { /* IEEE 802.15.4 dissectors couldn't be found. */ call_data_dissector(next_tvb, pinfo, tree); } } return tvb_captured_length(tvb); } /* dissect_ieee802_15_4 */ /*FUNCTION:------------------------------------------------------ * NAME * proto_register_zep * DESCRIPTION * IEEE 802.15.4 protocol registration routine. * PARAMETERS * none * RETURNS * void *--------------------------------------------------------------- */ void proto_register_zep(void) { static hf_register_info hf[] = { { &hf_zep_version, { "Protocol Version", "zep.version", FT_UINT8, BASE_DEC, NULL, 0x0, "The version of the sniffer.", HFILL }}, { &hf_zep_type, { "Type", "zep.type", FT_UINT8, BASE_DEC|BASE_RANGE_STRING, RVALS(type_rvals), 0x0, NULL, HFILL }}, { &hf_zep_channel_id, { "Channel ID", "zep.channel_id", FT_UINT8, BASE_DEC, NULL, 0x0, "The logical channel on which this packet was detected.", HFILL }}, { &hf_zep_device_id, { "Device ID", "zep.device_id", FT_UINT16, BASE_DEC, NULL, 0x0, "The ID of the device that detected this packet.", HFILL }}, { &hf_zep_lqi_mode, { "LQI/CRC Mode", "zep.lqi_mode", FT_BOOLEAN, BASE_NONE, TFS(&tfs_crc_lqi), 0x0, "Determines what format the last two bytes of the MAC frame use.", HFILL }}, { &hf_zep_lqi, { "Link Quality Indication", "zep.lqi", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_zep_timestamp, { "Timestamp", "zep.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }}, { &hf_zep_seqno, { "Sequence Number", "zep.seqno", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_zep_ieee_length, { "Length", "zep.length", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, UNS(&units_byte_bytes), ZEP_LENGTH_MASK, "The length (in bytes) of the encapsulated IEEE 802.15.4 MAC frame.", HFILL }}, { &hf_zep_protocol_id, { "Protocol ID String", "zep.protocol_id", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_zep_reserved_field, { "Reserved Fields", "zep.reserved_field", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, }; static int *ett[] = { &ett_zep }; /* Register protocol name and description. */ proto_zep = proto_register_protocol("ZigBee Encapsulation Protocol", "ZEP", "zep"); /* Register header fields and subtrees. */ proto_register_field_array(proto_zep, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* Register dissector with Wireshark. */ zep_handle = register_dissector("zep", dissect_zep, proto_zep); } /* proto_register_zep */ /*FUNCTION:------------------------------------------------------ * NAME * proto_reg_handoff_zep * DESCRIPTION * Registers the zigbee dissector with Wireshark. * Will be called every time 'apply' is pressed in the preferences menu. * PARAMETERS * none * RETURNS * void *--------------------------------------------------------------- */ void proto_reg_handoff_zep(void) { dissector_handle_t h; /* Get dissector handles. */ if ( !(h = find_dissector("wpan")) ) { /* Try use built-in 802.15.4 dissector */ h = find_dissector("ieee802154"); /* otherwise use older 802.15.4 plugin dissector */ } ieee802154_handle = h; if ( !(h = find_dissector("wpan_cc24xx")) ) { /* Try use built-in 802.15.4 (Chipcon) dissector */ h = find_dissector("ieee802154_ccfcs"); /* otherwise use older 802.15.4 (Chipcon) plugin dissector */ } ieee802154_cc24xx_handle = h; dissector_add_uint("udp.port", ZEP_DEFAULT_PORT, zep_handle); } /* proto_reg_handoff_zep */ /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */