This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
op25-legacy/wireshark/plugins/p25/packet-p25cai.c

1936 lines
62 KiB
C
Raw Normal View History

/* packet-p25cai.c
* Routines for APCO Project 25 Common Air Interface dissection
* Copyright 2008, Michael Ossmann <mike@ossmann.com>
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <epan/packet.h>
#include <epan/prefs.h>
#define FRAME_SYNC_MAGIC 0x5575F5FF77FF
/* function prototypes */
void proto_reg_handoff_p25cai(void);
void dissect_voice(tvbuff_t *tvb, proto_tree *tree, int offset);
void dissect_lc(tvbuff_t *tvb, proto_tree *tree);
void dissect_es(tvbuff_t *tvb, proto_tree *tree);
void dissect_pdu(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree);
void dissect_pdu_response(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks);
void dissect_pdu_unconfirmed(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks);
void dissect_pdu_confirmed(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks);
void dissect_pdu_ambt(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks);
tvbuff_t* extract_status_symbols(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *outbound);
tvbuff_t* build_hdu_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset);
tvbuff_t* build_tsdu_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset);
tvbuff_t* build_term_lc_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset);
tvbuff_t* build_ldu_lsd_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset);
tvbuff_t* build_ldu_lc_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset);
tvbuff_t* build_ldu_es_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset);
void data_deinterleave(tvbuff_t *tvb, guint8 *deinterleaved, int bit_offset);
void trellis_1_2_decode(guint8 *encoded, guint8 *decoded, int offset);
void trellis_3_4_decode(guint8 *encoded, guint8 *decoded, int offset);
guint8 golay_18_6_8_decode(guint32 codeword);
guint16 golay_24_12_8_decode(guint32 codeword);
guint8 hamming_10_6_3_decode(guint32 codeword);
void cyclic_16_8_5_decode(guint32 codeword, guint8 *decoded);
void rs_24_12_13_decode(guint8 *codeword, guint8 *decoded);
void rs_24_16_9_decode(guint8 *codeword, guint8 *decoded);
void rs_36_20_17_decode(guint8 *codeword, guint8 *decoded);
int find_min(guint8 list[], int len);
int count_bits(unsigned int n);
/* Initialize the protocol and registered fields */
static int proto_p25cai = -1;
static int hf_p25cai_fs = -1;
static int hf_p25cai_nid = -1;
static int hf_p25cai_nac = -1;
static int hf_p25cai_duid = -1;
static int hf_p25cai_hdu = -1;
static int hf_p25cai_tsbk = -1;
static int hf_p25cai_pdu = -1;
static int hf_p25cai_ldu1 = -1;
static int hf_p25cai_ldu2 = -1;
static int hf_p25cai_mi = -1;
static int hf_p25cai_mfid = -1;
static int hf_p25cai_algid = -1;
static int hf_p25cai_kid = -1;
static int hf_p25cai_tgid = -1;
static int hf_p25cai_ss_parent = -1;
static int hf_p25cai_ss = -1;
static int hf_p25cai_lc = -1;
static int hf_p25cai_lcf = -1;
static int hf_p25cai_lbf = -1;
static int hf_p25cai_ptbf = -1;
static int hf_p25cai_isp_opcode = -1;
static int hf_p25cai_osp_opcode = -1;
static int hf_p25cai_unknown_opcode = -1;
static int hf_p25cai_args = -1;
static int hf_p25cai_crc = -1;
static int hf_p25cai_imbe = -1;
static int hf_p25cai_lsd = -1;
static int hf_p25cai_es = -1;
static int hf_p25cai_an = -1;
static int hf_p25cai_io = -1;
static int hf_p25cai_pdu_format = -1;
static int hf_p25cai_sapid = -1;
static int hf_p25cai_llid = -1;
static int hf_p25cai_fmf = -1;
static int hf_p25cai_btf = -1;
static int hf_p25cai_poc = -1;
static int hf_p25cai_syn = -1;
static int hf_p25cai_ns = -1;
static int hf_p25cai_fsnf = -1;
static int hf_p25cai_dho = -1;
static int hf_p25cai_db = -1;
static int hf_p25cai_dbsn = -1;
static int hf_p25cai_crc9 = -1;
static int hf_p25cai_ud = -1;
static int hf_p25cai_packet_crc = -1;
static int hf_p25cai_class = -1;
static int hf_p25cai_type = -1;
static int hf_p25cai_status = -1;
static int hf_p25cai_x = -1;
static int hf_p25cai_sllid = -1;
/* Field values */
static const value_string data_unit_ids[] = {
{ 0x0, "Header Data Unit" },
{ 0x3, "Terminator without Link Control" },
{ 0x5, "Logical Link Data Unit 1" },
{ 0x7, "Trunking Signaling Data Unit" },
{ 0xA, "Logical Link Data Unit 2" },
{ 0xC, "Packet Data Unit" },
{ 0xF, "Terminator with Link Control" },
{ 0, NULL }
};
static const value_string network_access_codes[] = {
{ 0x293, "Default NAC" },
{ 0xF7E, "Receiver to open on any NAC" },
{ 0xF7F, "Repeater to receive and retransmit any NAC" },
{ 0, NULL }
};
static const value_string manufacturer_ids[] = {
/* from http://ftp.tiaonline.org/TR-8/TR815/Public/MFID_Assignments_080723.doc */
{ 0x00, "Standard MFID (pre-2001)" },
{ 0x01, "Standard MFID (post-2001)" },
{ 0x09, "Aselsan Inc." },
{ 0x10, "Relm / BK Radio" },
{ 0x18, "EADS Public Safety Inc." },
{ 0x20, "Cycomm" },
{ 0x28, "Efratom Time and Frequency Products, Inc" },
{ 0x30, "Com-Net Ericsson" },
{ 0x34, "Etherstack" },
{ 0x38, "Datron" },
{ 0x40, "Icom" },
{ 0x48, "Garmin" },
{ 0x50, "GTE" },
{ 0x55, "IFR Systems" },
{ 0x5A, "INIT Innovations in Transportation, Inc" },
{ 0x60, "GEC-Marconi" },
{ 0x64, "Harris Corp." },
{ 0x68, "Kenwood Communications" },
{ 0x70, "Glenayre Electronics" },
{ 0x74, "Japan Radio Co." },
{ 0x78, "Kokusai" },
{ 0x7C, "Maxon" },
{ 0x80, "Midland" },
{ 0x86, "Daniels Electronics Ltd." },
{ 0x90, "Motorola" },
{ 0xA0, "Thales" },
{ 0xA4, "M/A-COM" },
{ 0xB0, "Raytheon" },
{ 0xC0, "SEA" },
{ 0xC8, "Securicor" },
{ 0xD0, "ADI" },
{ 0xD8, "Tait Electronics" },
{ 0xE0, "Teletec" },
{ 0xF0, "Transcrypt International" },
{ 0xF8, "Vertex Standard" },
{ 0xFC, "Zetron, Inc" },
{ 0, NULL }
};
/* TODO: use link_control_opcodes instead */
static const value_string link_control_formats[] = {
{ 0x00, "Group Call Format" },
{ 0x03, "Individual Call Format" },
{ 0x80, "Encrypted Group Call Format" },
{ 0x83, "Encrypted Individual Call Format" },
{ 0, NULL }
};
static const value_string link_control_opcodes[] = {
/* from AABF */
{ 0x00, "Group Voice Channel User (LCGVR)" }, /* LC_GRP_V_CH_USR */
{ 0x01, "Reserved" },
{ 0x02, "Group Voice Channel Update (LCGVU)" }, /* LC_GRP_V_CH_UPDT */
{ 0x03, "Unit to Unit Voice Channel User (LCUVR)" }, /* LC_UU_V_CH_USR */
{ 0x04, "Group Voice Channel Update - Explicit (LCGVUX)" }, /* LC_GRP_CH_UPDT_EXP */
{ 0x05, "Unit to Unit Answer Request (LCUAQ)" }, /* LC_UU_ANS_REQ */
{ 0x06, "Telephone Interconnect Voice Channel User (LCTVR)" }, /* LC_TELE_INT_V_CH_USR */
{ 0x07, "Telephone Interconnect Answer Request (LCTAQ)" }, /* LC_TELE_INT_ANS_REQ */
{ 0x08, "Reserved" },
{ 0x09, "Reserved" },
{ 0x0A, "Reserved" },
{ 0x0B, "Reserved" },
{ 0x0C, "Reserved" },
{ 0x0D, "Reserved" },
{ 0x0E, "Reserved" },
{ 0x0F, "Call Termination/Cancellation (LCCT)" }, /* LC_CALL_TRM_CAN */
{ 0x10, "Group Affiliation Query (LCGAQ)" }, /* LC_GRP_AFF_Q */
{ 0x11, "Unit Registration Command (LCRC)" }, /* LC_U_REG_CMD */
{ 0x12, "Unit Authentication Command (LCAC)" }, /* LC_AUTH_CMD */
{ 0x13, "Status Query (LCSQ)" }, /* LC_STS_Q */
{ 0x14, "Status Update (LCSU)" }, /* LC_STA_UPDT */
{ 0x15, "Message Update (LCMU)" }, /* LC_MSG_UPDT */
{ 0x16, "Call Alert (LCCA)" }, /* LC_CALL_ALRT */
{ 0x17, "Extended Function Command (LCEFC)" }, /* LC_EXT_FNCT_CMD */
{ 0x18, "Channel Identifier Update (LCCIU)" }, /* LC_CH_ID_UPDT */
{ 0x19, "Channel Identifier Update - Explicit (LCCIUX)" }, /* LC_CH_ID_UPDT_EXP */
{ 0x1A, "Reserved" },
{ 0x1B, "Reserved" },
{ 0x1C, "Reserved" },
{ 0x1D, "Reserved" },
{ 0x1E, "Reserved" },
{ 0x1F, "Reserved" },
{ 0x20, "System Service Broadcast (LCSSB)" }, /* LC_SYS_SRV_BCST */
{ 0x21, "Secondary Control Channel Broadcast (LCSCB)" }, /* LC_SCCB */
{ 0x22, "Adjacent Site Status Broadcast (LCASB)" }, /* LC_ADJ_STS_BCST */
{ 0x23, "RFSS Status Broadcast (LCRSB)" }, /* LC_RFSS_STS_BCST */
{ 0x24, "Network Status Broadcast (LCNSB)" }, /* LC_NET_STS_BCST */
{ 0x25, "Protection Parameter Broadcast (LCPPB)" }, /* LC_P_PARM_BCST */
{ 0x26, "Secondary Control Channel Broadcast - Explicit (LCSCBX)" }, /* LC_SCCB_EXP */
{ 0x27, "Adjacent Site Status Broadcast - Explicit (LCASBX)" }, /* LC_ADJ_STS_BCST_EXP */
{ 0x28, "RFSS Status Broadcast - Explicit (LCRSBX)" }, /* LC_RFSS_STS_BCST_EXP */
{ 0x29, "Network Status Broadcast - Explicit (LCNSBX)" }, /* LC_NET_STS_BCST_EXP */
{ 0x2A, "Reserved" },
{ 0x2B, "Reserved" },
{ 0x2C, "Reserved" },
{ 0x2D, "Reserved" },
{ 0x2E, "Reserved" },
{ 0x2F, "Reserved" },
{ 0x30, "Reserved" },
{ 0x31, "Reserved" },
{ 0x32, "Reserved" },
{ 0x33, "Reserved" },
{ 0x34, "Reserved" },
{ 0x35, "Reserved" },
{ 0x36, "Reserved" },
{ 0x37, "Reserved" },
{ 0x38, "Reserved" },
{ 0x39, "Reserved" },
{ 0x3A, "Reserved" },
{ 0x3B, "Reserved" },
{ 0x3C, "Reserved" },
{ 0x3D, "Reserved" },
{ 0x3E, "Reserved" },
{ 0x3F, "Reserved" },
{ 0, NULL }
};
static const range_string logical_link_ids[] = {
{ 0x000000, 0x000000, "No One" },
{ 0x000001, 0x98967F, "General Use" },
{ 0x989680, 0xFFFFFE, "Talk Group or Special Purpose Use" },
{ 0xFFFFFF, 0xFFFFFF, "Everyone" },
{ 0, 0, NULL }
};
static const value_string talk_group_ids[] = {
{ 0x0000, "No One" },
{ 0x0001, "Default Talk Group" },
{ 0xFFFF, "Everyone" },
{ 0, NULL }
};
static const value_string key_ids[] = {
{ 0x0000, "Default Key ID" },
{ 0, NULL }
};
static const value_string algorithm_ids[] = {
/* mostly from http://ftp.tiaonline.org/TR-8/TR815/Public/ALGID_Guide_040528.doc */
/* Type I */
{ 0x00, "ACCORDION 1.3" },
{ 0x01, "BATON (Auto Even)" },
{ 0x02, "FIREFLY Type 1" },
{ 0x03, "MAYFLY Type 1" },
{ 0x04, "SAVILLE" },
{ 0x41, "BATON (Auto Odd)" },
/* Type III */
{ 0x80, "Unencrypted message" },
{ 0x81, "DES-OFB" },
{ 0x82, "2 key Triple DES" },
{ 0x83, "3 key Triple DES" },
{ 0x84, "AES-256" },
/* Motorola proprietary */
/* FIXME maybe these should only be interpreted this way if mfid == 0x90 */
{ 0x9F, "DES-XL" },
{ 0xA0, "DVI-XL" },
{ 0xA1, "DVP-XL" },
{ 0, NULL }
};
static const value_string service_access_points[] = {
{ 0x00, "Unencrypted User Data" },
{ 0x01, "Encrypted User Data" },
{ 0x02, "Circuit Data" },
{ 0x03, "Circuit Data Control" },
{ 0x04, "Packet Data" },
{ 0x05, "Address Resolution Protocol" },
{ 0x06, "SNDCP Packet Data Control" },
{ 0x1F, "Extended Address" },
{ 0x20, "Registration and Authorization" },
{ 0x21, "Channel Reassignment" },
{ 0x22, "System Configuration" },
{ 0x23, "MR Loop-Back" },
{ 0x24, "MR Statistics" },
{ 0x25, "MR Out-of-Service" },
{ 0x26, "MR Paging" },
{ 0x27, "MR Configuration" },
{ 0x28, "Unencrypted Key Management Message" },
{ 0x29, "Encrypted Key Management Message" },
{ 0x3D, "Trunking Control" },
{ 0x3F, "Protected Trunking Control" },
{ 0, NULL }
};
static const value_string emergency_indicators[] = {
{ 0x0, "Routine, non-emergency condition" },
{ 0x1, "Emergency condition" },
{ 0, NULL }
};
static const value_string isp_opcodes[] = {
/* voice service isp */
/* TODO: dissect AABC page 43 */
{ 0x00, "Group Voice Service Request" }, /* GRP_V_REQ */
{ 0x04, "Unit To Unit Voice Service Request" }, /* UU_V_REQ */
{ 0x05, "Unit To Unit Answer Response" }, /* UU_ANS_RSP */
{ 0x08, "Telephone Interconnect Request - Explicit Dialing" }, /* TELE_INT_DIAL_REQ */
{ 0x09, "Telephone Interconnect Request - Implicit Dialing" }, /* TELE_INT_PSTN_REQ */
{ 0x0A, "Telephone Interconnect Answer Response" }, /* TELE_INT_ANS_RSP */
/* data service isp */
/* TODO: dissect AABC page 69 */
{ 0x10, "Individual Data Service Request (obsolete)" }, /* IND_DATA_REQ */
{ 0x11, "Group Data Service Request (obsolete)" }, /* GRP_DATA_REQ */
{ 0x12, "SNDCP Data Channel Request" }, /* SN-DAT_CHN_REQ */
{ 0x13, "SNDCP Data Page Response" }, /* SN-DAT_PAGE_RES */
{ 0x14, "SNDCP Reconnect Request" }, /* SN-REC_REQ */
/* control and status isp */
/* TODO: dissect AABC page 86 */
{ 0x20, "Acknowledge Response - Unit" }, /* ACK_RSP_U */
{ 0x2E, "Authentication Query" }, /* AUTH_Q */
{ 0x2F, "Authentication Response" }, /* AUTH_RSP */
{ 0x1F, "Call Alert Request" }, /* CALL_ALRT_REQ */
{ 0x23, "Cancel Service Request" }, /* CAN_SRV_REQ */
{ 0x27, "Emergency Alarm Request" }, /* EMRG_ALRM_REQ */
{ 0x24, "Extended Function Response" }, /* EXT_FNCT_RSP */
{ 0x29, "Group Affiliation Query Response" }, /* GRP_AFF_Q_RSP */
{ 0x28, "Group Affiliation Request" }, /* GRP_AFF_REQ */
{ 0x32, "Identifier Update Request" }, /* IDEN_UP_REQ */
{ 0x1C, "Message Update Request" }, /* MSG_UPDT_REQ */
{ 0x30, "Protection Parameter Request" }, /* P_PARM_REQ */
{ 0x1A, "Status Query Request" }, /* STS_Q_REQ */
{ 0x19, "Status Query Response" }, /* STS_Q_RSP */
{ 0x18, "Status Update Request" }, /* STS_UPDT_REQ */
{ 0x2C, "Unit Registration Request" }, /* U_REG_REQ */
{ 0x2B, "De-Registration Request" }, /* U_DE_REG_REQ */
{ 0x2D, "Location Registration Request" }, /* LOC_REG_REQ */
{ 0x1D, "Radio Unit Monitor Request" }, /* RAD_MON_REQ */
{ 0x36, "Roaming Address Request" }, /* ROAM_ADDR_REQ */
{ 0x37, "Roaming Address Response" }, /* ROAM_ADDR_RSP */
{ 0, NULL }
};
static const value_string osp_opcodes[] = {
/* voice service osp */
/* TODO: dissect AABC page 53 */
{ 0x00, "Group Voice Channel Grant" }, /* GRP_V_CH_GRANT */
{ 0x02, "Group Voice Channel Grant Update" }, /* GRP_V_CH_GRANT_UPDT */
{ 0x03, "Group Voice Channel Grant Update - Explicit" }, /* GRP_V_CH_GRANT_UPDT_EXP */
{ 0x04, "Unit To Unit Voice Channel Grant" }, /* UU_V_CH_GRANT */
{ 0x05, "Unit To Unit Answer Request" }, /* UU_ANS_REQ */
{ 0x06, "Unit To Unit Voice Channel Grant Update" }, /* UU_V_CH_GRANT_UPDT */
{ 0x08, "Telephone Interconnect Voice Channel Grant" }, /* TELE_INT_CH_GRANT */
{ 0x09, "Telephone Interconnect Voice Channel Grant Update" }, /* TELE_INT_CH_GRANT_UPDT */
{ 0x0A, "Telephone Interconnect Answer Request" }, /* TELE_INT_ANS_REQ */
/* data service osp */
/* TODO: dissect AABC page 76 */
{ 0x10, "Individual Data Channel Grant (obsolete)" }, /* IND_DATA_CH_GRANT */
{ 0x11, "Group Data Channel Grant (obsolete)" }, /* GRP_DATA_CH_GRANT */
{ 0x12, "Group Data Channel Announcement (obsolete)" }, /* GRP_DATA_CH_ANN */
{ 0x13, "Group Data Channel Announcement Explicit (obsolete)" }, /* GRP_DATA_CH_ANN_EXP */
{ 0x14, "SNDCP Data Channel Grant" }, /* SN-DATA_CHN_GNT */
{ 0x15, "SNDCP Data Page Request" }, /* SN-DATA_PAGE_REQ */
{ 0x16, "SNDCP Data Channel Announcement - Explicit" }, /* SN-DAT_CHN_ANN_EXP */
/* control and status osp */
/* TODO: dissect AABC page 121 */
{ 0x20, "Acknowledge Response - FNE" }, /* ACK_RSP_FNE */
{ 0x3C, "Adjacent Status Broadcast" }, /* ADJ_STS_BCST */
{ 0x2E, "Authentication Command" }, /* AUTH_CMD */
{ 0x1F, "Call Alert" }, /* CALL_ALRT */
{ 0x27, "Deny Response" }, /* DENY_RSP */
{ 0x24, "Extended Function Command" }, /* EXT_FNCT_CMD */
{ 0x2A, "Group Affiliation Query" }, /* GRP_AFF_Q */
{ 0x28, "Group Affiliation Response" }, /* GRP_AFF_RSP */
{ 0x3D, "Identifier Update" }, /* IDEN_UP */
{ 0x1C, "Message Update" }, /* MSG_UPDT */
{ 0x3B, "Network Status Broadcast" }, /* NET_STS_BCST */
{ 0x3E, "Protection Parameter Broadcast" }, /* P_PARM_BCST */
{ 0x3F, "Protection Parameter Update" }, /* P_PARM_UPDT */
{ 0x21, "Queued Response" }, /* QUE_RSP */
{ 0x3A, "RFSS Status Broadcast" }, /* RFSS_STS_BCST */
{ 0x39, "Secondary Control Channel Broadcast" }, /* SCCB */
{ 0x1A, "Status Query" }, /* STS_Q */
{ 0x18, "Status Update" }, /* STS_UPDT */
{ 0x38, "System Service Broadcast" }, /* SYS_SRV_BCST */
{ 0x2D, "Unit Registration Command" }, /* U_REG_CMD */
{ 0x2C, "Unit Registration Response" }, /* U_REG_RSP */
{ 0x2F, "De-Registration Acknowledge" }, /* U_DE_REG_ACK */
{ 0x2B, "Location Registration Response" }, /* LOC_REG_RSP */
{ 0x1D, "Radio Unit Monitor Command" }, /* RAD_MON_CMD */
{ 0x36, "Roaming Address Command" }, /* ROAM_ADDR_CMD */
{ 0x37, "Roaming Address Update" }, /* ROAM_ADDR_UPDT */
{ 0x35, "Time and Date Announcement" }, /* TIME_DATE_ANN */
{ 0x34, "Identifier Update for VHF/UHF Bands" }, /* IDEN_UP_VU */
{ 0x29, "Secondary Control Channel Broadcast - Explicit" }, /* SCCB_EXP */
{ 0, NULL }
};
static const range_string cancel_reason_codes[] = {
{ 0x00, 0x00, "No Reason Code" },
{ 0x01, 0x0F, "Reserved" },
{ 0x10, 0x10, "Terminate Queued Condition" },
{ 0x11, 0x1F, "Reserved" },
{ 0x20, 0x20, "Terminate Resource Assignment" },
{ 0x21, 0x7F, "Reserved" },
{ 0x80, 0xFF, "User or System Definable" },
{ 0, 0, NULL }
};
static const range_string deny_response_reason_codes[] = {
{ 0x00, 0x0F, "Reserved" },
{ 0x10, 0x10, "The requestion unit is not valid" },
{ 0x11, 0x11, "The requestion unit is not authorized for this service" },
{ 0x12, 0x1F, "Reserved" },
{ 0x20, 0x20, "The target unit is not valid" },
{ 0x21, 0x21, "The target unit is not authorized for this service" },
{ 0x22, 0x2E, "Reserved" },
{ 0x2F, 0x2F, "Target unit has refused this call" },
{ 0x30, 0x30, "The target group is not valid" },
{ 0x31, 0x31, "The target group is not authorized for this service" },
{ 0x32, 0x3F, "Reserved" },
{ 0x40, 0x40, "Invalid dialing" },
{ 0x41, 0x41, "Telephone number is not authorized" },
{ 0x42, 0x42, "PSTN address is not valid" },
{ 0x43, 0x4F, "Reserved" },
{ 0x50, 0x50, "Call time-out has occurred" },
{ 0x51, 0x51, "Landline has terminated this call" },
{ 0x52, 0x52, "Subscriber unit has terminated this call" },
{ 0x53, 0x5E, "Reserved" },
{ 0x5F, 0x5F, "Call has been pre-empted" },
{ 0x60, 0x60, "Site access denial" },
{ 0x61, 0xEF, "User or system definable" },
{ 0xF0, 0xF0, "The call options are not valid for thie service" },
{ 0xF1, 0xF1, "Protection service option is not valid" },
{ 0xF2, 0xF2, "Duplex service option is not valid" },
{ 0xF3, 0xF3, "Circuit or packet mode service option is not valid" },
{ 0xF4, 0xFE, "User or system definable" },
{ 0xFF, 0xFF, "The system does not support this service" },
{ 0, 0, NULL }
};
static const range_string queued_response_reason_codes[] = {
{ 0x00, 0x0F, "Reserved" },
{ 0x10, 0x10, "The requesting unit is active in another service" },
{ 0x11, 0x1F, "Reserved" },
{ 0x10, 0x10, "The target unit is active in another service" },
{ 0x21, 0x2E, "Reserved" },
{ 0x2F, 0x2F, "The target unit has queued the call" },
{ 0x30, 0x30, "The target group is currently active" },
{ 0x31, 0x3F, "Reserved" },
{ 0x40, 0x40, "Channel resources are not currently active" },
{ 0x41, 0x41, "Telephone resources are not currently active" },
{ 0x42, 0x42, "Data resources are not currently active" },
{ 0x43, 0x4F, "Reserved" },
{ 0x50, 0x50, "Superseding service currently active" },
{ 0x51, 0x7F, "Reserved" },
{ 0x80, 0xFF, "User or System Definable" },
{ 0, 0, NULL }
};
static const value_string pdu_formats[] = {
{ 0x03, "Response Packet" },
{ 0x15, "Unconfirmed Data Packet" },
{ 0x16, "Confirmed Data Packet" },
{ 0x17, "Alternate Multiple Block Trunking Control Packet" },
{ 0, NULL }
};
static const true_false_string inbound_outbound = {
"Outbound",
"Inbound"
};
static const value_string status_symbols[] = {
{ 0x0, "Unknown" },
{ 0x1, "Busy" },
{ 0x2, "Unknown" },
{ 0x3, "Idle (start of inbound slot)" },
{ 0, NULL }
};
/* Initialize the subtree pointers */
static gint ett_p25cai = -1;
static gint ett_ss = -1;
static gint ett_nid = -1;
static gint ett_du = -1;
static gint ett_lc = -1;
static gint ett_es = -1;
static gint ett_db = -1;
/* Code to actually dissect the packets */
static int
dissect_p25cai(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
/* Set up structures needed to add the protocol subtree and manage it */
proto_item *ti, *nid_item, *du_item;
proto_tree *p25cai_tree, *nid_tree, *du_tree;
int offset, outbound;
tvbuff_t *extracted_tvb, *du_tvb;
guint8 duid, last_block, mfid;
/* If this doesn't look like a P25 CAI frame, give up and return 0 so that
* perhaps another dissector can take over.
*/
/* Check that there's enough data */
if (tvb_length(tvb) < 14) /* P25 CAI smallest packet size */
return 0;
/* Check for correct Frame Sync value
*
* This is commented out for now so that we can deal with symbol errors.
* We could use it if we have error correction working before frames get
* passed to wireshark. Perhaps a more generally useful alternative would
* look for a minimum number of matching bits rather than requiring a 100%
* match.
*
* guint64 fs;
* fs = tvb_get_ntoh64(tvb, 0) >> 16;
* if (fs != FRAME_SYNC_MAGIC)
* return 0;
*/
/* Make entries in Protocol column and Info column on summary display */
if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "P25 CAI");
/* Clear the Info column first just in case of duid fetching failure. */
if (check_col(pinfo->cinfo, COL_INFO))
col_clear(pinfo->cinfo, COL_INFO);
duid = tvb_get_guint8(tvb, 7) & 0xF;
if (check_col(pinfo->cinfo, COL_INFO))
col_set_str(pinfo->cinfo, COL_INFO, val_to_str(duid, data_unit_ids, "Unknown Data Unit (0x%02x)"));
/* see if we are being asked for details */
if (tree) {
/* create display subtree for the protocol */
ti = proto_tree_add_item(tree, proto_p25cai, tvb, 0, -1, FALSE);
p25cai_tree = proto_item_add_subtree(ti, ett_p25cai);
offset = 0;
/* top level P25 CAI tree */
proto_tree_add_item(p25cai_tree, hf_p25cai_fs, tvb, offset, 6, FALSE);
offset += 6;
/* Extract Status Symbols */
extracted_tvb = extract_status_symbols(tvb, pinfo, p25cai_tree, &outbound);
/* process extracted_tvb from here on */
nid_item = proto_tree_add_item(p25cai_tree, hf_p25cai_nid, extracted_tvb, offset, 8, FALSE);
/* NID subtree */
nid_tree = proto_item_add_subtree(nid_item, ett_nid);
proto_tree_add_item(nid_tree, hf_p25cai_nac, extracted_tvb, offset, 2, FALSE);
proto_tree_add_item(nid_tree, hf_p25cai_duid, extracted_tvb, offset, 2, FALSE);
offset += 8;
switch (duid) {
/* Header Data Unit */
case 0x0:
du_tvb = build_hdu_tvb(extracted_tvb, pinfo, offset);
offset = 0;
du_item = proto_tree_add_item(p25cai_tree, hf_p25cai_hdu, du_tvb, offset, -1, FALSE);
du_tree = proto_item_add_subtree(du_item, ett_du);
proto_tree_add_item(du_tree, hf_p25cai_mi, du_tvb, offset, 9, FALSE);
offset += 9;
proto_tree_add_item(du_tree, hf_p25cai_mfid, du_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(du_tree, hf_p25cai_algid, du_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(du_tree, hf_p25cai_kid, du_tvb, offset, 2, FALSE);
offset += 2;
proto_tree_add_item(du_tree, hf_p25cai_tgid, du_tvb, offset, 2, FALSE);
offset += 2;
break;
/* Terminator Data Unit without Link Control */
case 0x3:
/* nothing left to decode */
break;
/* Logical Link Data Unit 1 */
case 0x5:
du_item = proto_tree_add_item(p25cai_tree, hf_p25cai_ldu1, extracted_tvb, offset, -1, FALSE);
du_tree = proto_item_add_subtree(du_item, ett_du);
dissect_voice(extracted_tvb, du_tree, offset);
dissect_lc(build_ldu_lc_tvb(extracted_tvb, pinfo, offset), du_tree);
proto_tree_add_item(du_tree, hf_p25cai_lsd, build_ldu_lsd_tvb(extracted_tvb, pinfo, offset), 0, 2, FALSE);
break;
/* Trunking Signaling Data Unit */
case 0x7:
du_tvb = build_tsdu_tvb(extracted_tvb, pinfo, offset);
offset = 0;
last_block = 0;
while (last_block == 0) {
last_block = tvb_get_guint8(du_tvb, offset) >> 7;
mfid = tvb_get_guint8(du_tvb, offset + 1);
du_item = proto_tree_add_item(p25cai_tree, hf_p25cai_tsbk, du_tvb, offset, 12, FALSE);
du_tree = proto_item_add_subtree(du_item, ett_du);
proto_tree_add_item(du_tree, hf_p25cai_lbf, du_tvb, offset, 1, FALSE);
proto_tree_add_item(du_tree, hf_p25cai_ptbf, du_tvb, offset, 1, FALSE);
if (mfid > 1) {
proto_tree_add_item(du_tree, hf_p25cai_unknown_opcode, du_tvb, offset, 1, FALSE);
} else if (outbound) {
proto_tree_add_item(du_tree, hf_p25cai_osp_opcode, du_tvb, offset, 1, FALSE);
} else {
proto_tree_add_item(du_tree, hf_p25cai_isp_opcode, du_tvb, offset, 1, FALSE);
}
offset += 1;
proto_tree_add_item(du_tree, hf_p25cai_mfid, du_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(du_tree, hf_p25cai_args, du_tvb, offset, 8, FALSE);
offset += 8;
/* TODO: dissect args subtree */
proto_tree_add_item(du_tree, hf_p25cai_crc, du_tvb, offset, 2, FALSE);
/* TODO: verify CRC */
offset += 2;
}
break;
/* Logical Link Data Unit 2 */
case 0xA:
du_item = proto_tree_add_item(p25cai_tree, hf_p25cai_ldu2, extracted_tvb, offset, -1, FALSE);
du_tree = proto_item_add_subtree(du_item, ett_du);
dissect_voice(extracted_tvb, du_tree, offset);
dissect_es(build_ldu_es_tvb(extracted_tvb, pinfo, offset), du_tree);
proto_tree_add_item(du_tree, hf_p25cai_lsd, build_ldu_lsd_tvb(extracted_tvb, pinfo, offset), 0, 2, FALSE);
break;
/* Packet Data Unit */
/* TODO: This case is hasn't been tested. */
case 0xC:
dissect_pdu(extracted_tvb, pinfo, offset, p25cai_tree);
break;
/* Terminator Data Unit with Link Control */
case 0xF:
dissect_lc(build_term_lc_tvb(extracted_tvb, pinfo, offset), p25cai_tree);
break;
/* Unknown Data Unit */
default:
/* don't know how to decode any more */
break;
}
}
/* Return the amount of data this dissector was able to dissect */
return tvb_length(tvb);
}
/* Dissect voice frames */
void
dissect_voice(tvbuff_t *tvb, proto_tree *tree, int offset)
{
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 196);
/* TODO: make these less raw */
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 18;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 23;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 23;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 23;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 23;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 23;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 23;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 22;
proto_tree_add_item(tree, hf_p25cai_imbe, tvb, offset, 18, FALSE);
offset += 18;
}
/* Dissect Link Control */
void
dissect_lc(tvbuff_t *tvb, proto_tree *tree)
{
proto_tree *lc_tree;
proto_item *lc_item;
int offset = 0;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 9);
lc_item = proto_tree_add_item(tree, hf_p25cai_lc, tvb, offset, 9, FALSE);
lc_tree = proto_item_add_subtree(lc_item, ett_lc);
proto_tree_add_item(lc_tree, hf_p25cai_lcf, tvb, offset, 1, FALSE);
offset += 1;
/* TODO: Decode Link Control according to Link Control Format. */
}
/* Dissect Encryption Sync from an LDU2 */
void
dissect_es(tvbuff_t *tvb, proto_tree *tree)
{
proto_tree *es_tree;
proto_item *es_item;
int offset = 0;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 12);
es_item = proto_tree_add_item(tree, hf_p25cai_es, tvb, offset, 12, FALSE);
es_tree = proto_item_add_subtree(es_item, ett_es);
proto_tree_add_item(es_tree, hf_p25cai_mi, tvb, offset, 9, FALSE);
offset += 9;
proto_tree_add_item(es_tree, hf_p25cai_algid, tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(es_tree, hf_p25cai_kid, tvb, offset, 2, FALSE);
}
/* Dissect Packet Data Unit */
void
dissect_pdu(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree)
{
proto_tree *pdu_tree;
proto_item *pdu_item;
guint8 *header_buffer, *trellis_buffer, pdu_format;
int blocks_to_follow;
pdu_item = proto_tree_add_item(tree, hf_p25cai_pdu, tvb, offset, -1, FALSE);
pdu_tree = proto_item_add_subtree(pdu_item, ett_du);
/* Prepare the header portion of the tvb. */
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 25);
header_buffer = (guint8*)ep_alloc0(12);
trellis_buffer = (guint8*)ep_alloc0(25);
data_deinterleave(tvb, trellis_buffer, offset * 8);
trellis_1_2_decode(trellis_buffer, header_buffer, 0);
/* Find out how many data blocks we are supposed to have. */
blocks_to_follow = header_buffer[6] & 0x7F;
pdu_format = header_buffer[0] & 0x1F;
switch (pdu_format) {
/* Response Packet */
case 0x03:
dissect_pdu_response(tvb, pinfo, 0, pdu_tree, header_buffer, blocks_to_follow);
break;
/* Unconfirmed Data Packet */
case 0x15:
dissect_pdu_unconfirmed(tvb, pinfo, 0, pdu_tree, header_buffer, blocks_to_follow);
break;
/* Confirmed Data Packet */
case 0x16:
dissect_pdu_confirmed(tvb, pinfo, 0, pdu_tree, header_buffer, blocks_to_follow);
break;
/* Alternate Multiple Block Trunking (MBT) Control Packet */
case 0x17:
dissect_pdu_ambt(tvb, pinfo, 0, pdu_tree, header_buffer, blocks_to_follow);
break;
/* Unknown PDU format */
default:
break;
}
}
/* Dissect Response Packet */
void
dissect_pdu_response(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks)
{
tvbuff_t *pdu_tvb;
proto_tree *db_tree;
proto_item *db_item;
guint8 *pdu_buffer, *trellis_buffer;
int i, tvb_bit_offset, pdu_offset, pdu_buffer_length;
pdu_buffer_length = 12 + num_blocks * 12;
pdu_buffer = (guint8*)ep_alloc0(pdu_buffer_length);
/* Our tvb offset doesn't fall on bit boundaries, so we track it by bits
* instead of bytes.
*/
tvb_bit_offset = 308;
pdu_offset = 12;
/* Copy the temporary header onto the pdu_buffer. */
for (i = 0; i < 12; i++)
pdu_buffer[i] = header[i];
/* Add the data blocks to the pdu_buffer. */
for (i = 0; i < num_blocks; i++) {
DISSECTOR_ASSERT(tvb_length_remaining(tvb, tvb_bit_offset / 8) >= 25);
trellis_buffer = (guint8*)ep_alloc0(25);
data_deinterleave(tvb, trellis_buffer, tvb_bit_offset);
trellis_1_2_decode(trellis_buffer, pdu_buffer, pdu_offset);
tvb_bit_offset += 196;
pdu_offset += 12;
}
/* Setup a new tvb buffer with the decoded data. */
pdu_tvb = tvb_new_real_data(pdu_buffer, pdu_buffer_length, pdu_buffer_length);
tvb_set_child_real_data_tvbuff(tvb, pdu_tvb);
add_new_data_source(pinfo, pdu_tvb, "Packet Data Unit");
/* Dissect the PDU header. */
proto_tree_add_item(tree, hf_p25cai_io, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_pdu_format, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_class, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_type, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_status, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_mfid, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_llid, pdu_tvb, offset, 3, FALSE);
offset += 3;
proto_tree_add_item(tree, hf_p25cai_x, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_btf, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_sllid, pdu_tvb, offset, 3, FALSE);
offset += 3;
proto_tree_add_item(tree, hf_p25cai_crc, pdu_tvb, offset, 2, FALSE);
offset += 2;
/* Dissect the data blocks. */
for (i = 0; i < num_blocks; i++) {
db_item = proto_tree_add_item(tree, hf_p25cai_db, pdu_tvb, offset, 12, FALSE);
db_tree = proto_item_add_subtree(db_item, ett_db);
if (i == num_blocks - 1) {
/* The last data block includes the packet CRC */
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 8, FALSE);
offset += 8;
proto_tree_add_item(db_tree, hf_p25cai_packet_crc, pdu_tvb, offset, 4, FALSE);
} else {
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 12, FALSE);
}
}
}
/* Dissect Unonfirmed Data Packet */
void
dissect_pdu_unconfirmed(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks)
{
tvbuff_t *pdu_tvb;
proto_tree *db_tree;
proto_item *db_item;
guint8 *pdu_buffer, *trellis_buffer;
int i, tvb_bit_offset, pdu_offset, pdu_buffer_length;
pdu_buffer_length = 12 + num_blocks * 12;
pdu_buffer = (guint8*)ep_alloc0(pdu_buffer_length);
/* Our tvb offset doesn't fall on bit boundaries, so we track it by bits
* instead of bytes.
*/
tvb_bit_offset = 308;
pdu_offset = 12;
/* Copy the temporary header onto the pdu_buffer. */
for (i = 0; i < 12; i++)
pdu_buffer[i] = header[i];
/* Add the data blocks to the pdu_buffer. */
for (i = 0; i < num_blocks; i++) {
DISSECTOR_ASSERT(tvb_length_remaining(tvb, tvb_bit_offset / 8) >= 25);
trellis_buffer = (guint8*)ep_alloc0(25);
data_deinterleave(tvb, trellis_buffer, tvb_bit_offset);
trellis_1_2_decode(trellis_buffer, pdu_buffer, pdu_offset);
tvb_bit_offset += 196;
pdu_offset += 12;
}
/* Setup a new tvb buffer with the decoded data. */
pdu_tvb = tvb_new_real_data(pdu_buffer, pdu_buffer_length, pdu_buffer_length);
tvb_set_child_real_data_tvbuff(tvb, pdu_tvb);
add_new_data_source(pinfo, pdu_tvb, "Packet Data Unit");
/* Dissect the PDU header. */
proto_tree_add_item(tree, hf_p25cai_an, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_io, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_pdu_format, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_sapid, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_mfid, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_llid, pdu_tvb, offset, 3, FALSE);
offset += 3;
proto_tree_add_item(tree, hf_p25cai_btf, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_poc, pdu_tvb, offset, 1, FALSE);
offset += 1;
/* reserved octet here */
offset += 1;
proto_tree_add_item(tree, hf_p25cai_dho, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_crc, pdu_tvb, offset, 2, FALSE);
offset += 2;
/* Dissect the data blocks. */
for (i = 0; i < num_blocks; i++) {
db_item = proto_tree_add_item(tree, hf_p25cai_db, pdu_tvb, offset, 12, FALSE);
db_tree = proto_item_add_subtree(db_item, ett_db);
if (i == num_blocks - 1) {
/* The last data block includes the packet CRC */
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 8, FALSE);
offset += 8;
proto_tree_add_item(db_tree, hf_p25cai_packet_crc, pdu_tvb, offset, 4, FALSE);
} else {
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 12, FALSE);
}
}
}
/* Dissect Confirmed Data Packet */
void
dissect_pdu_confirmed(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks)
{
tvbuff_t *pdu_tvb;
proto_tree *db_tree;
proto_item *db_item;
guint8 *pdu_buffer, *trellis_buffer;
int i, tvb_bit_offset, pdu_offset, pdu_buffer_length;
pdu_buffer_length = 12 + num_blocks * 18;
pdu_buffer = (guint8*)ep_alloc0(pdu_buffer_length);
/* Our tvb offset doesn't fall on bit boundaries, so we track it by bits
* instead of bytes.
*/
tvb_bit_offset = 308;
pdu_offset = 12;
/* Copy the temporary header onto the pdu_buffer. */
for (i = 0; i < 12; i++)
pdu_buffer[i] = header[i];
/* Add the data blocks to the pdu_buffer. */
for (i = 0; i < num_blocks; i++) {
DISSECTOR_ASSERT(tvb_length_remaining(tvb, tvb_bit_offset / 8) >= 25);
trellis_buffer = (guint8*)ep_alloc0(25);
data_deinterleave(tvb, trellis_buffer, tvb_bit_offset);
trellis_3_4_decode(trellis_buffer, pdu_buffer, pdu_offset);
tvb_bit_offset += 196;
pdu_offset += 18;
}
/* Setup a new tvb buffer with the decoded data. */
pdu_tvb = tvb_new_real_data(pdu_buffer, pdu_buffer_length, pdu_buffer_length);
tvb_set_child_real_data_tvbuff(tvb, pdu_tvb);
add_new_data_source(pinfo, pdu_tvb, "Packet Data Unit");
/* Dissect the PDU header. */
proto_tree_add_item(tree, hf_p25cai_an, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_io, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_pdu_format, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_sapid, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_mfid, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_llid, pdu_tvb, offset, 3, FALSE);
offset += 3;
proto_tree_add_item(tree, hf_p25cai_fmf, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_btf, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_poc, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_syn, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_ns, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_fsnf, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_dho, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_crc, pdu_tvb, offset, 2, FALSE);
offset += 2;
/* Dissect the data blocks. */
for (i = 0; i < num_blocks; i++) {
db_item = proto_tree_add_item(tree, hf_p25cai_db, pdu_tvb, offset, 18, FALSE);
db_tree = proto_item_add_subtree(db_item, ett_db);
proto_tree_add_item(db_tree, hf_p25cai_dbsn, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(db_tree, hf_p25cai_crc9, pdu_tvb, offset, 2, FALSE);
offset += 2;
if (i == num_blocks - 1) {
/* The last data block includes the packet CRC */
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 12, FALSE);
offset += 12;
proto_tree_add_item(db_tree, hf_p25cai_packet_crc, pdu_tvb, offset, 4, FALSE);
} else {
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 16, FALSE);
}
}
}
/* Dissect Alternate Multiple Block Trunking (MBT) Control Packet */
void
dissect_pdu_ambt(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, guint8 *header, int num_blocks)
{
tvbuff_t *pdu_tvb;
proto_tree *db_tree;
proto_item *db_item;
guint8 *pdu_buffer, *trellis_buffer, outbound, mfid;
int i, tvb_bit_offset, pdu_offset, pdu_buffer_length;
pdu_buffer_length = 12 + num_blocks * 12;
pdu_buffer = (guint8*)ep_alloc0(pdu_buffer_length);
/* Our tvb offset doesn't fall on bit boundaries, so we track it by bits
* instead of bytes.
*/
tvb_bit_offset = 308;
pdu_offset = 12;
/* Copy the temporary header onto the pdu_buffer. */
for (i = 0; i < 12; i++)
pdu_buffer[i] = header[i];
/* Add the data blocks to the pdu_buffer. */
for (i = 0; i < num_blocks; i++) {
DISSECTOR_ASSERT(tvb_length_remaining(tvb, tvb_bit_offset / 8) >= 25);
trellis_buffer = (guint8*)ep_alloc0(25);
data_deinterleave(tvb, trellis_buffer, tvb_bit_offset);
trellis_1_2_decode(trellis_buffer, pdu_buffer, pdu_offset);
tvb_bit_offset += 196;
pdu_offset += 12;
}
/* Setup a new tvb buffer with the decoded data. */
pdu_tvb = tvb_new_real_data(pdu_buffer, pdu_buffer_length, pdu_buffer_length);
tvb_set_child_real_data_tvbuff(tvb, pdu_tvb);
add_new_data_source(pinfo, pdu_tvb, "Packet Data Unit");
/* Dissect the PDU header. */
proto_tree_add_item(tree, hf_p25cai_an, pdu_tvb, offset, 1, FALSE);
outbound = (tvb_get_guint8(pdu_tvb, offset) >> 5) & 0x01;
proto_tree_add_item(tree, hf_p25cai_io, pdu_tvb, offset, 1, FALSE);
proto_tree_add_item(tree, hf_p25cai_pdu_format, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_sapid, pdu_tvb, offset, 1, FALSE);
offset += 1;
mfid = tvb_get_guint8(pdu_tvb, offset);
proto_tree_add_item(tree, hf_p25cai_mfid, pdu_tvb, offset, 1, FALSE);
offset += 1;
proto_tree_add_item(tree, hf_p25cai_llid, pdu_tvb, offset, 3, FALSE);
offset += 3;
proto_tree_add_item(tree, hf_p25cai_btf, pdu_tvb, offset, 1, FALSE);
offset += 1;
if (mfid > 1) {
proto_tree_add_item(tree, hf_p25cai_unknown_opcode, pdu_tvb, offset, 1, FALSE);
} else if (outbound) {
proto_tree_add_item(tree, hf_p25cai_osp_opcode, pdu_tvb, offset, 1, FALSE);
} else {
proto_tree_add_item(tree, hf_p25cai_isp_opcode, pdu_tvb, offset, 1, FALSE);
}
offset += 1;
/* TODO: 2 octets defined by trunking messages */
offset += 2;
proto_tree_add_item(tree, hf_p25cai_crc, pdu_tvb, offset, 2, FALSE);
offset += 2;
/* Dissect the data blocks. */
for (i = 0; i < num_blocks; i++) {
db_item = proto_tree_add_item(tree, hf_p25cai_db, pdu_tvb, offset, 12, FALSE);
db_tree = proto_item_add_subtree(db_item, ett_db);
if (i == num_blocks - 1) {
/* The last data block includes the packet CRC */
/* really "MBT data" not "user data" */
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 8, FALSE);
offset += 8;
proto_tree_add_item(db_tree, hf_p25cai_packet_crc, pdu_tvb, offset, 4, FALSE);
} else {
proto_tree_add_item(db_tree, hf_p25cai_ud, pdu_tvb, offset, 12, FALSE);
}
}
}
/* Extract status symbols, display them, and return frame without status symbols */
tvbuff_t*
extract_status_symbols(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *outbound)
{
proto_item *ss_parent_item;
proto_tree *ss_tree;
int i, j, raw_length, extracted_length;
guint8 *extracted_buffer;
tvbuff_t *extracted_tvb;
raw_length = tvb_length(tvb);
extracted_length = raw_length - (raw_length / 36);
*outbound = 0;
/* Create buffer to become new tvb. */
extracted_buffer = (guint8*)ep_alloc0(extracted_length);
/* Create status symbol subtree. */
ss_parent_item = proto_tree_add_item(tree, hf_p25cai_ss_parent, tvb, 0, -1, FALSE);
ss_tree = proto_item_add_subtree(ss_parent_item, ett_ss);
/* Go through frame one dibit at a time. */
for (i = 0, j = 0; i < raw_length * 4; i++) {
if (i % 36 == 35) {
/* After every 35 dibits is a status symbol. */
proto_tree_add_item(ss_tree, hf_p25cai_ss, tvb, i/4, 1, FALSE);
/* Check to see if the status symbol is odd. */
if (tvb_get_guint8(tvb, i/4) & 0x1) {
/* Flag as outbound (only outbound frames should have odd status symbols).
* This may not be a very reliable means of determining the direction, but
* I haven't found anything better.
*/
*outbound |= 1;
}
} else {
/* Extract frame bits from between status symbols. */
/* I'm sure there is a more efficient way to do this. */
extracted_buffer[j/4] |= tvb_get_bits8(tvb, i * 2, 2) << (6 - (j % 4) * 2);
j++;
}
}
/* Setup a new tvb buffer with the extracted data. */
extracted_tvb = tvb_new_real_data(extracted_buffer, extracted_length, extracted_length);
tvb_set_child_real_data_tvbuff(tvb, extracted_tvb);
add_new_data_source(pinfo, extracted_tvb, "P25 CAI");
return extracted_tvb;
}
/* Build Header Data Unit tvb. */
tvbuff_t*
build_hdu_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset)
{
guint8 *hdu_buffer, *rs_codeword;
guint8 rs_code_byte, high_byte, low_byte;
guint32 golay_codeword;
int i, j;
tvbuff_t *hdu_tvb;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 81);
rs_codeword = (guint8*)ep_alloc0(27);
hdu_buffer = (guint8*)ep_alloc0(15);
/* Each 18 bits is a Golay codeword. */
for (i = offset * 8, j = 0; j < 216; i += 18, j += 6) {
/* Take 18 bits from the tvb, adjusting for byte boundaries. */
golay_codeword = (tvb_get_ntohl(tvb, i / 8) >> (14 - i % 8)) & 0x3FFFF;
rs_code_byte = golay_18_6_8_decode(golay_codeword) << 2;
/* Stuff high bits into one byte of the new buffer. */
high_byte = rs_code_byte >> (j % 8);
rs_codeword[j / 8] |= high_byte;
/* Stuff low bits into the next unless beyond end of buffer. */
if (j < 210) {
low_byte = rs_code_byte << (8 - j % 8);
rs_codeword[j / 8 + 1] |= low_byte;
}
}
rs_36_20_17_decode(rs_codeword, hdu_buffer);
/* Setup a new tvb buffer with the decoded data. */
hdu_tvb = tvb_new_real_data(hdu_buffer, 15, 15);
tvb_set_child_real_data_tvbuff(tvb, hdu_tvb);
add_new_data_source(pinfo, hdu_tvb, "data units");
return hdu_tvb;
}
/* Build Low Speed Data tvb from LDU */
tvbuff_t*
build_ldu_lsd_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset)
{
guint8 *lsd_buffer;
tvbuff_t *lsd_tvb;
/* we were passed the offset to the beginning of the LDU */
offset += 174;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 4);
lsd_buffer = (guint8*)ep_alloc0(2);
cyclic_16_8_5_decode(tvb_get_ntohl(tvb, offset), lsd_buffer);
/* Setup a new tvb buffer with the decoded data. */
lsd_tvb = tvb_new_real_data(lsd_buffer, 2, 2);
tvb_set_child_real_data_tvbuff(tvb, lsd_tvb);
add_new_data_source(pinfo, lsd_tvb, "Low Speed Data");
return lsd_tvb;
}
/* Build Link Control tvb from LDU1 */
tvbuff_t*
build_ldu_lc_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset)
{
guint8 *lc_buffer, *rs_codeword;
guint8 rs_code_byte, high_byte, low_byte;
guint32 hamming_codeword;
int i, j, r, t;
tvbuff_t *lc_tvb;
/* we were passed the offset to the beginning of the LDU */
offset += 36;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 147);
rs_codeword = (guint8*)ep_alloc0(18);
lc_buffer = (guint8*)ep_alloc0(9);
/* step through tvb bits to find 10 bit hamming codewords */
for (i = offset * 8, r = 0; r < 144; i += 184) {
for (j = 0; j < 40; j += 10, r += 6) {
/* t = tvb bit index
* r = reed-solomon codeword bit index
*/
t = i + j;
hamming_codeword = (tvb_get_ntohl(tvb, t / 8) >> (22 - t % 8)) & 0x3FF;
rs_code_byte = hamming_10_6_3_decode(hamming_codeword) << 2;
/* Stuff high bits into one byte of the new buffer. */
high_byte = rs_code_byte >> (r % 8);
rs_codeword[r / 8] |= high_byte;
/* Stuff low bits into the next unless beyond end of buffer. */
if (r < 144) {
low_byte = rs_code_byte << (8 - r % 8);
rs_codeword[r / 8 + 1] |= low_byte;
}
}
}
rs_24_12_13_decode(rs_codeword, lc_buffer);
/* Setup a new tvb buffer with the decoded data. */
lc_tvb = tvb_new_real_data(lc_buffer, 9, 9);
tvb_set_child_real_data_tvbuff(tvb, lc_tvb);
add_new_data_source(pinfo, lc_tvb, "Link Control");
return lc_tvb;
}
/* Build Encryption Sync tvb LDU2 */
tvbuff_t*
build_ldu_es_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset)
{
guint8 *es_buffer, *rs_codeword;
guint8 rs_code_byte, high_byte, low_byte;
guint32 hamming_codeword;
int i, j, r, t;
tvbuff_t *es_tvb;
/* we were passed the offset to the beginning of the LDU */
offset += 36;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 147);
rs_codeword = (guint8*)ep_alloc0(18);
es_buffer = (guint8*)ep_alloc0(12);
/* step through tvb bits to find 10 bit hamming codewords */
for (i = offset * 8, r = 0; r < 144; i += 184) {
for (j = 0; j < 40; j += 10, r += 6) {
/* t = tvb bit index
* r = reed-solomon codeword bit index
*/
t = i + j;
hamming_codeword = (tvb_get_ntohl(tvb, t / 8) >> (22 - t % 8)) & 0x3FF;
rs_code_byte = hamming_10_6_3_decode(hamming_codeword) << 2;
/* Stuff high bits into one byte of the new buffer. */
high_byte = rs_code_byte >> (r % 8);
rs_codeword[r / 8] |= high_byte;
/* Stuff low bits into the next unless beyond end of buffer. */
if (r < 144) {
low_byte = rs_code_byte << (8 - r % 8);
rs_codeword[r / 8 + 1] |= low_byte;
}
}
}
rs_24_16_9_decode(rs_codeword, es_buffer);
/* Setup a new tvb buffer with the decoded data. */
es_tvb = tvb_new_real_data(es_buffer, 12, 12);
tvb_set_child_real_data_tvbuff(tvb, es_tvb);
add_new_data_source(pinfo, es_tvb, "Encryption Sync");
return es_tvb;
}
/* Build Trunking Signaling Block tvb. */
tvbuff_t*
build_tsdu_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset)
{
guint8 *tsdu_buffer, *trellis_buffer;
guint8 last_block;
int tsdu_offset, tvb_bit_offset;
tvbuff_t *tsdu_tvb;
/* From here on, our tvb offset may not fall on bit boundaries, so we track
* it by bits instead of bytes.
*/
tvb_bit_offset = offset * 8;
tsdu_offset = 0;
last_block = 0;
tsdu_buffer = (guint8*)ep_alloc0(36); /* 12 times maximum number of TSBKs (3) */
while (last_block == 0) {
DISSECTOR_ASSERT(tvb_length_remaining(tvb, tvb_bit_offset / 8) >= 25);
trellis_buffer = (guint8*)ep_alloc0(25);
data_deinterleave(tvb, trellis_buffer, tvb_bit_offset);
trellis_1_2_decode(trellis_buffer, tsdu_buffer, tsdu_offset);
tvb_bit_offset += 196;
last_block = tsdu_buffer[tsdu_offset] >> 7;
tsdu_offset += 12;
}
/* Setup a new tvb buffer with the decoded data. */
tsdu_tvb = tvb_new_real_data(tsdu_buffer, tsdu_offset, tsdu_offset);
tvb_set_child_real_data_tvbuff(tvb, tsdu_tvb);
add_new_data_source(pinfo, tsdu_tvb, "data units");
return tsdu_tvb;
}
/* Build Link Control tvb from Terminator */
tvbuff_t*
build_term_lc_tvb(tvbuff_t *tvb, packet_info *pinfo, int offset)
{
guint8 *lc_buffer, *rs_codeword;
guint16 rs_code_chunk;
guint8 high_byte, low_byte;
guint32 golay_codeword;
int i, j;
tvbuff_t *lc_tvb;
DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= 36);
rs_codeword = (guint8*)ep_alloc0(18);
lc_buffer = (guint8*)ep_alloc0(9);
/* Each 24 bits is a Golay codeword. */
for (i = offset * 8, j = 0; j < 144; i += 24, j += 12) {
/* Take 24 bits from the tvb, adjusting for byte boundaries. */
golay_codeword = (tvb_get_ntohl(tvb, i / 8) >> (8 - i % 8)) & 0xFFFFFF;
rs_code_chunk = golay_24_12_8_decode(golay_codeword) << 4;
/* Stuff high bits into one byte of the new buffer. */
high_byte = rs_code_chunk >> (j % 8);
rs_codeword[j / 8] |= high_byte;
/* Stuff low bits into the next. */
low_byte = rs_code_chunk << (8 - j % 8);
rs_codeword[j / 8 + 1] |= low_byte;
}
rs_24_12_13_decode(rs_codeword, lc_buffer);
/* Setup a new tvb buffer with the decoded data. */
lc_tvb = tvb_new_real_data(lc_buffer, 9, 9);
tvb_set_child_real_data_tvbuff(tvb, lc_tvb);
add_new_data_source(pinfo, lc_tvb, "data units");
return lc_tvb;
}
/* Deinterleave data block. Assumes output buffer is already zeroed. */
void
data_deinterleave(tvbuff_t *tvb, guint8 *deinterleaved, int bit_offset)
{
int d, i, j, t;
int steps[] = {0, 52, 100, 148};
/* step through input nibbles to copy to output */
for (i = bit_offset, d = 0; i < 45 + bit_offset; i += 4) {
for (j = 0; j < 4; j++, d += 4) {
/* t = tvb bit index
* d = deinterleaved bit index
*/
t = i + steps[j];
deinterleaved[d / 8] |= ((tvb_get_guint8(tvb, t / 8) << (t % 8)) & 0xF0) >> (d % 8);
}
}
t = bit_offset + 48;
deinterleaved[24] |= ((tvb_get_guint8(tvb, t / 8) << (t % 8)) & 0xF0);
}
/* 1/2 rate trellis decoder. Assumes output buffer is already zeroed. */
void
trellis_1_2_decode(guint8 *encoded, guint8 *decoded, int offset)
{
int i, j;
int state = 0;
guint8 codeword;
/* Hamming distance */
guint8 hd[4];
/* state transition table, including constellation to dibit pair mapping */
guint8 next_words[4][4] = {
{0x2, 0xC, 0x1, 0xF},
{0xE, 0x0, 0xD, 0x3},
{0x9, 0x7, 0xA, 0x4},
{0x5, 0xB, 0x6, 0x8}
};
/* step through 4 bit codewords in input */
for (i = 0; i < 196; i += 4) {
codeword = (encoded[i / 8] >> (4 - (i % 8))) & 0xF;
/* try each codeword in a row of the state transition table */
for (j = 0; j < 4; j++) {
/* find Hamming distance for candidate */
hd[j] = count_bits(codeword ^ next_words[state][j]);
}
/* find the dibit that matches the most codeword bits (minimum Hamming distance) */
state = find_min(hd, 4);
/* error if minimum can't be found */
DISSECTOR_ASSERT(state != -1);
/* It also might be nice to report a condition where the minimum is
* non-zero, i.e. an error has been corrected. It probably shouldn't
* be a permanent failure, though.
*
* DISSECTOR_ASSERT(hd[state] == 0);
*/
/* append dibit onto output buffer */
if (i < 192)
decoded[(i / 16) + offset] |= state << (6 - (i / 2) % 8);
}
}
/* 3/4 rate trellis decoder. Assumes output buffer is already zeroed. */
void
trellis_3_4_decode(guint8 *encoded, guint8 *decoded, int offset)
{
int i, j;
int state = 0;
guint8 codeword;
/* Hamming distance */
guint8 hd[8];
/* state transition table, including constellation to dibit pair mapping */
guint8 next_words[8][8] = {
{0x2, 0xD, 0xE, 0x1, 0x7, 0x8, 0xB, 0x4},
{0xE, 0x1, 0x7, 0x8, 0xB, 0x4, 0x2, 0xD},
{0xA, 0x5, 0x6, 0x9, 0xF, 0x0, 0x3, 0xC},
{0x6, 0x9, 0xF, 0x0, 0x3, 0xC, 0xA, 0x5},
{0xF, 0x0, 0x3, 0xC, 0xA, 0x5, 0x6, 0x9},
{0x3, 0xC, 0xA, 0x5, 0x6, 0x9, 0xF, 0x0},
{0x7, 0x8, 0xB, 0x4, 0x2, 0xD, 0xE, 0x1},
{0xB, 0x4, 0x2, 0xD, 0xE, 0x1, 0x7, 0x8}
};
/* step through 4 bit codewords in input */
for (i = 0; i < 196; i += 4) {
codeword = (encoded[i / 8] >> (4 - (i % 8))) & 0xF;
/* try each codeword in a row of the state transition table */
for (j = 0; j < 8; j++) {
/* find Hamming distance for candidate */
hd[j] = count_bits(codeword ^ next_words[state][j]);
}
/* find the dibit that matches the most codeword bits (minimum Hamming distance) */
state = find_min(hd, 8);
/* error if minimum can't be found */
DISSECTOR_ASSERT(state != -1);
/* It also might be nice to report a condition where the minimum is
* non-zero, i.e. an error has been corrected. It probably shouldn't
* be a permanent failure, though.
*
* DISSECTOR_ASSERT(hd[state] == 0);
*/
/* append tribit onto output buffer */
if (i < 192)
/* FIXME adapt from 1/2 to 3/4 rate */
decoded[(i / 16) + offset] |= state << (6 - (i / 2) % 8);
}
}
/* Error correction decoders
*
* TODO: For now these are fake. They pull out the original bits without
* actually doing any error correction or detection.
*/
/* fake (18,6,8) shortened Golay decoder, no error correction */
/* TODO: make less fake */
guint8
golay_18_6_8_decode(guint32 codeword)
{
return (codeword >> 12) & 0x3F;
}
/* fake (24,12,8) extended Golay decoder, no error correction */
/* TODO: make less fake */
guint16
golay_24_12_8_decode(guint32 codeword)
{
return (codeword >> 12) & 0xFFF;
}
/* fake (10,6,3) shortened Hamming decoder, no error correction */
/* TODO: make less fake */
guint8
hamming_10_6_3_decode(guint32 codeword)
{
return (codeword >> 4) & 0x3F;
}
/* fake (16,8,5) shortened cyclic decoder, no error correction */
/* TODO: make less fake */
void
cyclic_16_8_5_decode(guint32 codeword, guint8 *decoded)
{
/* Take the first byte of each 16 bit word. */
decoded[0] = codeword >> 24;
decoded[1] = codeword >> 8;
}
/* fake (24,12,13) Reed-Solomon decoder, no error correction */
/* TODO: make less fake */
void
rs_24_12_13_decode(guint8 *codeword, guint8 *decoded)
{
int i;
/* Just grab the first 9 bytes (12 sets of six bits) */
for (i = 0; i < 9; i++)
decoded[i] = codeword[i];
}
/* fake (24,16,9) Reed-Solomon decoder, no error correction */
/* TODO: make less fake */
void
rs_24_16_9_decode(guint8 *codeword, guint8 *decoded)
{
int i;
/* Just grab the first 12 bytes (16 sets of six bits) */
for (i = 0; i < 12; i++)
decoded[i] = codeword[i];
}
/* fake (36,20,17) Reed-Solomon decoder, no error correction */
/* TODO: make less fake */
void
rs_36_20_17_decode(guint8 *codeword, guint8 *decoded)
{
int i;
/* Just grab the first 15 bytes (20 sets of six bits) */
for (i = 0; i < 15; i++)
decoded[i] = codeword[i];
}
/* Register the protocol with Wireshark */
void
proto_register_p25cai(void)
{
/* Setup list of fields */
static hf_register_info hf[] = {
{ &hf_p25cai_fs,
{ "Frame Synchronization", "p25cai.fs",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_nid,
{ "Network ID Codeword", "p25cai.nid",
FT_UINT64, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_nac,
{ "Network Access Code", "p25cai.nac",
FT_UINT16, BASE_HEX, VALS(network_access_codes), 0xFFF0,
NULL, HFILL }
},
{ &hf_p25cai_duid,
{ "Data Unit ID", "p25cai.duid",
FT_UINT16, BASE_HEX, VALS(data_unit_ids), 0x000F,
NULL, HFILL }
},
{ &hf_p25cai_hdu,
{ "Header Data Unit", "p25cai.hdu",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_tsbk,
{ "Trunking Signaling Block", "p25cai.tsbk",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_pdu,
{ "Packet Data Unit", "p25cai.pdu",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_ldu1,
{ "Logical Link Data Unit 1", "p25cai.ldu1",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_ldu2,
{ "Logical Link Data Unit 2", "p25cai.ldu2",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_mi,
{ "Message Indicator", "p25cai.mi",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_mfid,
{ "Manufacturer's ID", "p25cai.mfid",
FT_UINT8, BASE_HEX, VALS(manufacturer_ids), 0x0,
NULL, HFILL }
},
{ &hf_p25cai_algid,
{ "Algorithm ID", "p25cai.algid",
FT_UINT8, BASE_HEX, VALS(algorithm_ids), 0x0,
NULL, HFILL }
},
{ &hf_p25cai_kid,
{ "Key ID", "p25cai.kid",
FT_UINT16, BASE_HEX, VALS(key_ids), 0x0,
NULL, HFILL }
},
{ &hf_p25cai_tgid,
{ "Talk Group ID", "p25cai.tgid",
FT_UINT16, BASE_HEX, VALS(talk_group_ids), 0x0,
NULL, HFILL }
},
{ &hf_p25cai_ss_parent,
{ "Status Symbols", "p25cai.ss_parent",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_ss,
{ "Status Symbol", "p25cai.ss",
FT_UINT8, BASE_HEX, VALS(status_symbols), 0x3,
NULL, HFILL }
},
{ &hf_p25cai_lc,
{ "Link Control", "p25cai.lc",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_lcf,
{ "Link Control Format", "p25cai.lcf",
FT_UINT8, BASE_HEX, VALS(link_control_formats), 0x0,
NULL, HFILL }
},
{ &hf_p25cai_lbf,
{ "Last Block Flag", "p25cai.lbf",
FT_BOOLEAN, BASE_NONE, NULL, 0x80,
NULL, HFILL }
},
{ &hf_p25cai_ptbf,
{ "Protected Trunking Block Flag", "p25cai.ptbf",
FT_BOOLEAN, BASE_NONE, NULL, 0x40,
NULL, HFILL }
},
{ &hf_p25cai_isp_opcode,
{ "Opcode", "p25cai.isp.opcode",
FT_UINT8, BASE_HEX, VALS(isp_opcodes), 0x3F,
NULL, HFILL }
},
{ &hf_p25cai_osp_opcode,
{ "Opcode", "p25cai.osp.opcode",
FT_UINT8, BASE_HEX, VALS(osp_opcodes), 0x3F,
NULL, HFILL }
},
{ &hf_p25cai_unknown_opcode,
{ "Unknown Opcode (non-standard MFID)", "p25cai.unknown.opcode",
FT_UINT8, BASE_HEX, NULL, 0x3F,
NULL, HFILL }
},
{ &hf_p25cai_args,
{ "Arguments", "p25cai.args",
FT_UINT64, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_crc,
{ "CRC", "p25cai.crc",
FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_imbe,
{ "Raw IMBE Frame", "p25cai.imbe",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_lsd,
{ "Low Speed Data", "p25cai.lsd",
FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_es,
{ "Encryption Sync", "p25cai.es",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_an,
{ "A/N", "p25cai.an",
FT_BOOLEAN, BASE_NONE, NULL, 0x40,
NULL, HFILL }
},
{ &hf_p25cai_io,
{ "I/O", "p25cai.io",
FT_BOOLEAN, BASE_NONE, TFS(&inbound_outbound), 0x20,
NULL, HFILL }
},
{ &hf_p25cai_pdu_format,
{ "Format", "p25cai.pdu.format",
FT_UINT8, BASE_HEX, VALS(pdu_formats), 0x1F,
NULL, HFILL }
},
{ &hf_p25cai_sapid,
{ "SAP ID", "p25cai.sapid",
FT_UINT8, BASE_HEX, VALS(service_access_points), 0x3F,
NULL, HFILL }
},
{ &hf_p25cai_llid,
{ "Logical Link ID", "p25cai.llid",
FT_UINT24, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_fmf,
{ "Full Message Flag", "p25cai.fmf",
FT_BOOLEAN, BASE_NONE, NULL, 0x80,
NULL, HFILL }
},
{ &hf_p25cai_btf,
{ "Blocks to Follow", "p25cai.btf",
FT_UINT8, BASE_DEC, NULL, 0x7F,
NULL, HFILL }
},
{ &hf_p25cai_poc,
{ "Pad Octet Count", "p25cai.poc",
FT_UINT8, BASE_HEX, NULL, 0x1F,
NULL, HFILL }
},
{ &hf_p25cai_syn,
{ "Syn", "p25cai.syn",
FT_BOOLEAN, BASE_NONE, NULL, 0x80,
NULL, HFILL }
},
{ &hf_p25cai_ns,
{ "N(S)", "p25cai.ns",
FT_UINT8, BASE_HEX, NULL, 0x70,
NULL, HFILL }
},
{ &hf_p25cai_fsnf,
{ "Fragment Sequence Number Field", "p25cai.fsnf",
FT_UINT8, BASE_HEX, NULL, 0x0F,
NULL, HFILL }
},
{ &hf_p25cai_dho,
{ "Data Header Offset", "p25cai.dho",
FT_UINT8, BASE_HEX, NULL, 0x3F,
NULL, HFILL }
},
{ &hf_p25cai_db,
{ "Data Block", "p25cai.db",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_dbsn,
{ "Data Block Serial Number", "p25cai.dbsn",
FT_UINT8, BASE_HEX, NULL, 0xFE,
NULL, HFILL }
},
{ &hf_p25cai_crc9,
{ "CRC", "p25cai.crc9",
FT_UINT16, BASE_HEX, NULL, 0x1FF,
NULL, HFILL }
},
{ &hf_p25cai_ud,
{ "User Data", "p25cai.ud",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_packet_crc,
{ "Packet CRC", "p25cai.packet_crc",
FT_UINT32, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_p25cai_class,
{ "Response Class", "p25cai.class",
FT_UINT8, BASE_HEX, NULL, 0xA0,
NULL, HFILL }
},
{ &hf_p25cai_type,
{ "Response Type", "p25cai.type",
FT_UINT8, BASE_HEX, NULL, 0x38,
NULL, HFILL }
},
{ &hf_p25cai_status,
{ "Response Status", "p25cai.status",
FT_UINT8, BASE_HEX, NULL, 0x07,
NULL, HFILL }
},
{ &hf_p25cai_x,
{ "X", "p25cai.x",
FT_BOOLEAN, BASE_HEX, NULL, 0x80,
NULL, HFILL }
},
{ &hf_p25cai_sllid,
{ "Source Logical Link ID", "p25cai.sllid",
FT_UINT24, BASE_HEX, NULL, 0x0,
NULL, HFILL }
}
};
/* Setup protocol subtree arrays */
static gint *ett[] = {
&ett_p25cai,
&ett_ss,
&ett_nid,
&ett_du,
&ett_lc,
&ett_es,
&ett_db
};
/* Register the protocol name and description */
proto_p25cai = proto_register_protocol(
"APCO Project 25 Common Air Interface", /* full name */
"P25 CAI", /* short name */
"p25cai" /* abbreviation (e.g. for filters) */
);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_p25cai, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
void
proto_reg_handoff_p25cai(void)
{
static gboolean inited = FALSE;
if (!inited) {
dissector_handle_t p25cai_handle;
/* Use new_create_dissector_handle() to indicate that dissect_p25cai()
* returns the number of bytes it dissected (or 0 if it thinks the packet
* does not belong to APCO Project 25 Common Air Interface).
*/
p25cai_handle = new_create_dissector_handle(dissect_p25cai, proto_p25cai);
/* FIXME: Temporarily using an unassigned UDP port! Application
* for an IANA-assigned port number has been made.
*/
dissector_add("udp.port", 23456, p25cai_handle);
inited = TRUE;
}
}
/* Utility functions */
/* count the number of 1 bits in an int */
int
count_bits(unsigned int n)
{
int i = 0;
for (i = 0; n != 0; i++)
n &= n - 1;
return i;
}
/* return the index of the lowest value in a list */
int
find_min(guint8 list[], int len)
{
int min = list[0];
int index = 0;
int unique = 1;
int i;
for (i = 1; i < len; i++) {
if (list[i] < min) {
min = list[i];
index = i;
unique = 1;
} else if (list[i] == min) {
unique = 0;
}
}
/* return -1 if a minimum can't be found */
if (!unique)
return -1;
return index;
}