/* packet-dcp-etsi.c * Routines for ETSI Distribution & Communication Protocol * Copyright 2006, British Broadcasting Corporation * * Wireshark - Network traffic analyzer * By Gerald Combs * 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. * * Protocol info * Ref: ETSI DCP (ETSI TS 102 821) */ #include "config.h" #include #include #include #include #include /* forward reference */ void proto_register_dcp_etsi(void); void proto_reg_handoff_dcp_etsi(void); static int dissect_af (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void* data); static int dissect_pft (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void* data); static dissector_table_t dcp_dissector_table; static dissector_table_t af_dissector_table; static dissector_table_t tpl_dissector_table; static int proto_dcp_etsi = -1; static int proto_af = -1; static int proto_pft = -1; static int proto_tpl = -1; static int hf_edcp_sync = -1; static int hf_edcp_len = -1; static int hf_edcp_seq = -1; static int hf_edcp_crcflag = -1; static int hf_edcp_maj = -1; static int hf_edcp_min = -1; static int hf_edcp_pt = -1; static int hf_edcp_crc = -1; static int hf_edcp_crc_ok = -1; /* static int hf_edcp_pft_pt = -1; */ static int hf_edcp_pseq = -1; static int hf_edcp_findex = -1; static int hf_edcp_fcount = -1; static int hf_edcp_fecflag = -1; static int hf_edcp_addrflag = -1; static int hf_edcp_plen = -1; static int hf_edcp_rsk = -1; static int hf_edcp_rsz = -1; static int hf_edcp_source = -1; static int hf_edcp_dest = -1; static int hf_edcp_hcrc = -1; static int hf_edcp_hcrc_ok = -1; /* static int hf_edcp_c_max = -1; */ /* static int hf_edcp_rx_min = -1; */ /* static int hf_edcp_rs_corrected = -1; */ static int hf_edcp_rs_ok = -1; static int hf_edcp_pft_payload = -1; static int hf_tpl_tlv = -1; /* static int hf_tpl_ptr = -1; */ static int hf_edcp_fragments = -1; static int hf_edcp_fragment = -1; static int hf_edcp_fragment_overlap = -1; static int hf_edcp_fragment_overlap_conflicts = -1; static int hf_edcp_fragment_multiple_tails = -1; static int hf_edcp_fragment_too_long_fragment = -1; static int hf_edcp_fragment_error = -1; static int hf_edcp_fragment_count = -1; static int hf_edcp_reassembled_in = -1; static int hf_edcp_reassembled_length = -1; /* Initialize the subtree pointers */ static gint ett_edcp = -1; static gint ett_af = -1; static gint ett_pft = -1; static gint ett_tpl = -1; static gint ett_edcp_fragment = -1; static gint ett_edcp_fragments = -1; static expert_field ei_edcp_reassembly = EI_INIT; static expert_field ei_edcp_reassembly_info = EI_INIT; static reassembly_table dcp_reassembly_table; static const fragment_items dcp_frag_items = { /* Fragment subtrees */ &ett_edcp_fragment, &ett_edcp_fragments, /* Fragment fields */ &hf_edcp_fragments, &hf_edcp_fragment, &hf_edcp_fragment_overlap, &hf_edcp_fragment_overlap_conflicts, &hf_edcp_fragment_multiple_tails, &hf_edcp_fragment_too_long_fragment, &hf_edcp_fragment_error, &hf_edcp_fragment_count, /* Reassembled in field */ &hf_edcp_reassembled_in, /* Reassembled length field */ &hf_edcp_reassembled_length, /* Reassembled data field */ NULL, /* Tag */ "Message fragments" }; /** initialise the DCP protocol. Details follow * here. */ static void dcp_init_protocol(void) { reassembly_table_init (&dcp_reassembly_table, &addresses_reassembly_table_functions); } static void dcp_cleanup_protocol(void) { reassembly_table_destroy(&dcp_reassembly_table); } /** Dissect a DCP packet. Details follow * here. * \param[in,out] tvb The buffer containing the packet * \param[in,out] pinfo The packet info structure * \param[in,out] tree The structure containing the details which will be displayed, filtered, etc. static void */ static gboolean dissect_dcp_etsi (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void * data _U_) { guint8 *sync; proto_tree *dcp_tree; proto_item *ti; guint16 word; /* 6.1 AF packet structure * * AF Header * SYNC LEN SEQ AR PT * 2 bytes 4 bytes 2 bytes 1 byte 1 byte * * SYNC: two-byte ASCII representation of "AF". * LEN: length of the payload, in bytes. * SEQ: sequence number * AR: AF protocol Revision - a field combining the CF, MAJ and MIN fields * CF: CRC Flag, 0 if the CRC field is not used * MAJ: major revision of the AF protocol in use, see clause 6.2. * MIN: minor revision of the AF protocol in use, see clause 6.2. * Protocol Type (PT): single byte encoding the protocol of the data carried in the payload. For TAG Packets, the value * shall be the ASCII representation of "T". * * 7.1 PFT fragment structure * PFT Header * 14, 16, 18 or 20 bytes (depending on options) Optional present if FEC=1 Optional present if Addr = 1 * Psync Pseq Findex Fcount FEC HCRC Addr Plen | RSk RSz | Source Dest * 16 bits 16 bits 24 bits 24 bits 1 bit 16 bits 1 bit 14 bits | 8 bits 8 bits | 16 bits 16 bits * * Psync: the ASCII string "PF" is used as the synchronization word for the PFT Layer * * Don't accept this packet unless at least a full AF header present(10 bytes). * It should be possible to strengthen the heuristic further if need be. */ if(tvb_captured_length(tvb) < 11) return FALSE; word = tvb_get_ntohs(tvb,0); /* Check for 'AF or 'PF' */ if(word != 0x4146 && word != 0x5046) return FALSE; /* Clear out stuff in the info column */ col_clear(pinfo->cinfo, COL_INFO); col_set_str (pinfo->cinfo, COL_PROTOCOL, "DCP (ETSI)"); /*col_append_fstr (pinfo->cinfo, COL_INFO, " tvb %d", tvb_length(tvb));*/ ti = proto_tree_add_item (tree, proto_dcp_etsi, tvb, 0, -1, ENC_NA); dcp_tree = proto_item_add_subtree (ti, ett_edcp); sync = tvb_get_string_enc(wmem_packet_scope(), tvb, 0, 2, ENC_ASCII); dissector_try_string(dcp_dissector_table, (char*)sync, tvb, pinfo, dcp_tree, NULL); return TRUE; } #define PFT_RS_N_MAX 207 #define PFT_RS_K 255 #define PFT_RS_P (PFT_RS_K - PFT_RS_N_MAX) static void rs_deinterleave(const guint8 *input, guint8 *output, guint16 plen, guint32 fcount) { guint fidx; for(fidx=0; fidx MAX_FRAGMENTS) { proto_tree_add_expert_format(tree, pinfo, &ei_edcp_reassembly, tvb , 0, -1, "[Reassembly of %d fragments not attempted]", fcount); return NULL; } decoded_size = fcount*plen; c_max = fcount*plen/(rsk+PFT_RS_P); /* rounded down */ rx_min = fcount - (c_max*PFT_RS_P/plen); if (fdx) new_tvb = process_reassembled_data (tvb, offset, pinfo, "Reassembled DCP (ETSI)", fdx, &dcp_frag_items, NULL, tree); else { guint fragments=0; guint32 *got; fragment_item *fd; fragment_head *fd_head; proto_tree_add_expert_format(tree, pinfo, &ei_edcp_reassembly_info, tvb, 0, -1, "want %d, got %d need %d", fcount, fragments, rx_min); got = (guint32 *)wmem_alloc(wmem_packet_scope(), fcount*sizeof(guint32)); /* make a list of the findex (offset) numbers of the fragments we have */ fd = fragment_get(&dcp_reassembly_table, pinfo, seq, NULL); for (fd_head = fd; fd_head != NULL && fragments < fcount; fd_head = fd_head->next) { if(fd_head->tvb_data) { got[fragments++] = fd_head->offset; /* this is the findex of the fragment */ } } /* have we got enough for Reed Solomon to try to correct ? */ if(fragments>=rx_min) { /* yes, in theory */ guint i,current_findex; fragment_head *frag=NULL; guint8 *dummy_data = (guint8*) wmem_alloc0 (wmem_packet_scope(), plen); tvbuff_t *dummytvb = tvb_new_real_data(dummy_data, plen, plen); /* try and decode with missing fragments */ proto_tree_add_expert_format(tree, pinfo, &ei_edcp_reassembly_info, tvb, 0, -1, "want %d, got %d need %d", fcount, fragments, rx_min); /* fill the fragment table with empty fragments */ current_findex = 0; for(i=0; i MAX_FRAGMENTS) { proto_tree_add_expert_format(tree, pinfo, &ei_edcp_reassembly, tvb , 0, -1, "[Reassembly of %d fragments not attempted]", next_fragment_we_have); return NULL; } if (next_fragment_we_have-current_findex > MAX_FRAG_GAP) { proto_tree_add_expert_format(tree, pinfo, &ei_edcp_reassembly, tvb, 0, -1, "[Missing %d consecutive packets. Don't attempt reassembly]", next_fragment_we_have-current_findex); return NULL; } for(; current_findex 0) { gboolean decoded; tvbuff_t *dtvb = NULL; const guint8 *input = tvb_get_ptr(new_tvb, 0, -1); guint32 reassembled_size = tvb_captured_length(new_tvb); guint8 *deinterleaved = (guint8*) wmem_alloc(pinfo->pool, reassembled_size); guint8 *output = (guint8*) wmem_alloc(pinfo->pool, decoded_size); rs_deinterleave(input, deinterleaved, plen, fcount); dtvb = tvb_new_child_real_data(tvb, deinterleaved, reassembled_size, reassembled_size); add_new_data_source(pinfo, dtvb, "Deinterleaved"); decoded = rs_correct_data(deinterleaved, output, c_max, rsk, rsz); proto_tree_add_boolean (tree, hf_edcp_rs_ok, tvb, offset, 2, decoded); new_tvb = tvb_new_child_real_data(dtvb, output, decoded_size, decoded_size); add_new_data_source(pinfo, new_tvb, "RS Error Corrected Data"); } return new_tvb; } /** Handle a PFT packet which has the fragmentation header. This uses the * standard wireshark methods for reassembling fragments. If FEC is used, * the FEC is handled too. For the moment, all the fragments must be * available but this could be improved. * \param[in,out] tvb The buffer containing the current fragment * \param[in,out] pinfo The packet info structure * \param[in,out] tree The structure containing the details which will be displayed, filtered, etc. * \param[in] findex the fragment count * \param[in] fcount the number of fragments * \param[in] seq the sequence number of the reassembled packet * \param[in] offset the offset into the tvb of the fragment * \param[in] plen the length of each fragment * \param[in] fec is fec used * \param[in] rsk the number of useful bytes in each chunk * \param[in] rsz the number of padding bytes in each chunk */ static tvbuff_t * dissect_pft_fragmented(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, guint32 findex, guint32 fcount, guint16 seq, gint offset, guint16 plen, gboolean fec, guint16 rsk, guint16 rsz ) { gboolean first, last; tvbuff_t *new_tvb=NULL; fragment_head *frag_edcp = NULL; pinfo->fragmented = TRUE; first = findex == 0; last = fcount == (findex+1); frag_edcp = fragment_add_seq_check ( &dcp_reassembly_table, tvb, offset, pinfo, seq, NULL, findex, plen, !last); if(fec) { new_tvb = dissect_pft_fec_detailed( tvb, pinfo, tree, findex, fcount, seq, offset, plen, fec, rsk, rsz, frag_edcp ); } else { new_tvb = process_reassembled_data (tvb, offset, pinfo, "Reassembled DCP (ETSI)", frag_edcp, &dcp_frag_items, NULL, tree); } if(new_tvb) { col_append_str (pinfo->cinfo, COL_INFO, " (Message Reassembled)"); } else { if(last) { col_append_str (pinfo->cinfo, COL_INFO, " (Message Reassembly failure)"); } else { col_append_fstr (pinfo->cinfo, COL_INFO, " (Message fragment %u)", findex); } } if(first) col_append_str (pinfo->cinfo, COL_INFO, " (first)"); if(last) col_append_str (pinfo->cinfo, COL_INFO, " (last)"); return new_tvb; } /** Dissect a PFT packet. Details follow * here. * \param[in,out] tvb The buffer containing the packet * \param[in,out] pinfo The packet info structure * \param[in,out] tree The structure containing the details which will be displayed, filtered, etc. */ static int dissect_pft(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void* data) { guint16 plen; gint offset = 0; guint16 seq, payload_len; guint32 findex, fcount; proto_tree *pft_tree; proto_item *ti, *li; tvbuff_t *next_tvb = NULL; gboolean fec = FALSE; guint16 rsk=0, rsz=0; col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCP-PFT"); ti = proto_tree_add_item (tree, proto_pft, tvb, 0, -1, ENC_NA); pft_tree = proto_item_add_subtree (ti, ett_pft); proto_tree_add_item (pft_tree, hf_edcp_sync, tvb, offset, 2, ENC_ASCII|ENC_NA); offset += 2; seq = tvb_get_ntohs (tvb, offset); proto_tree_add_item (pft_tree, hf_edcp_pseq, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; findex = tvb_get_ntoh24 (tvb, offset); proto_tree_add_item (pft_tree, hf_edcp_findex, tvb, offset, 3, ENC_BIG_ENDIAN); offset += 3; fcount = tvb_get_ntoh24 (tvb, offset); proto_tree_add_item (pft_tree, hf_edcp_fcount, tvb, offset, 3, ENC_BIG_ENDIAN); offset += 3; plen = tvb_get_ntohs (tvb, offset); payload_len = plen & 0x3fff; proto_tree_add_item (pft_tree, hf_edcp_fecflag, tvb, offset, 2, ENC_BIG_ENDIAN); proto_tree_add_item (pft_tree, hf_edcp_addrflag, tvb, offset, 2, ENC_BIG_ENDIAN); li = proto_tree_add_item (pft_tree, hf_edcp_plen, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; if (plen & 0x8000) { fec = TRUE; rsk = tvb_get_guint8 (tvb, offset); proto_tree_add_item (pft_tree, hf_edcp_rsk, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; rsz = tvb_get_guint8 (tvb, offset); proto_tree_add_item (pft_tree, hf_edcp_rsz, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } if (plen & 0x4000) { proto_tree_add_item (pft_tree, hf_edcp_source, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item (pft_tree, hf_edcp_dest, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; } if (tree) { proto_item *ci = NULL; guint header_len = offset+2; guint16 c = crc16_x25_ccitt_tvb(tvb, header_len); ci = proto_tree_add_item (pft_tree, hf_edcp_hcrc, tvb, offset, 2, ENC_BIG_ENDIAN); proto_item_append_text(ci, " (%s)", (c==0x1D0F)?"Ok":"bad"); proto_tree_add_boolean(pft_tree, hf_edcp_hcrc_ok, tvb, offset, 2, c==0x1D0F); } offset += 2; if (fcount > 1) { /* fragmented*/ gboolean save_fragmented = pinfo->fragmented; guint16 real_len = tvb_captured_length(tvb)-offset; proto_tree_add_item (pft_tree, hf_edcp_pft_payload, tvb, offset, real_len, ENC_NA); if(real_len != payload_len || real_len == 0) { proto_item_append_text(li, " (length error (%d))", real_len); } else { next_tvb = dissect_pft_fragmented(tvb, pinfo, pft_tree, findex, fcount, seq, offset, real_len, fec, rsk, rsz); } pinfo->fragmented = save_fragmented; } else { next_tvb = tvb_new_subset_remaining (tvb, offset); } if(next_tvb) { dissect_af(next_tvb, pinfo, tree, data); } return tvb_captured_length(tvb); } /** Dissect an AF Packet. Parse an AF packet, checking the CRC if the CRC valid * flag is set and calling any registered sub dissectors on the payload type. * Currently only a payload type 'T' is defined which is the tag packet layer. * If any others are defined then they can register themselves. * \param[in,out] tvb The buffer containing the packet * \param[in,out] pinfo The packet info structure * \param[in,out] tree The structure containing the details which will be displayed, filtered, etc. */ static int dissect_af (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void* data _U_) { gint offset = 0; proto_item *ti; proto_item *li = NULL; proto_item *ci; proto_tree *af_tree; guint8 ver, pt; guint32 payload_len; tvbuff_t *next_tvb = NULL; col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCP-AF"); ti = proto_tree_add_item (tree, proto_af, tvb, 0, -1, ENC_NA); af_tree = proto_item_add_subtree (ti, ett_af); proto_tree_add_item (af_tree, hf_edcp_sync, tvb, offset, 2, ENC_ASCII|ENC_NA); offset += 2; payload_len = tvb_get_ntohl(tvb, offset); if (tree) { guint32 real_payload_len = tvb_captured_length(tvb)-12; li = proto_tree_add_item (af_tree, hf_edcp_len, tvb, offset, 4, ENC_BIG_ENDIAN); if(real_payload_len < payload_len) { proto_item_append_text (li, " (wrong len claims %d is %d)", payload_len, real_payload_len ); } else if(real_payload_len > payload_len) { proto_item_append_text (li, " (%d bytes in packet after end of AF frame)", real_payload_len-payload_len ); } } offset += 4; proto_tree_add_item (af_tree, hf_edcp_seq, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; ver = tvb_get_guint8 (tvb, offset); proto_tree_add_item (af_tree, hf_edcp_crcflag, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item (af_tree, hf_edcp_maj, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item (af_tree, hf_edcp_min, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; pt = tvb_get_guint8 (tvb, offset); proto_tree_add_item (af_tree, hf_edcp_pt, tvb, offset, 1, ENC_ASCII|ENC_NA); offset += 1; next_tvb = tvb_new_subset (tvb, offset, payload_len, payload_len); offset += payload_len; ci = proto_tree_add_item (af_tree, hf_edcp_crc, tvb, offset, 2, ENC_BIG_ENDIAN); if (ver & 0x80) { /* crc valid */ guint len = offset+2; guint16 c = crc16_x25_ccitt_tvb(tvb, len); proto_item_append_text(ci, " (%s)", (c==0x1D0F)?"Ok":"bad"); proto_tree_add_boolean(af_tree, hf_edcp_crc_ok, tvb, offset, 2, c==0x1D0F); } /*offset += 2;*/ dissector_try_uint(af_dissector_table, pt, next_tvb, pinfo, tree); return tvb_captured_length(tvb); } /** Dissect the Tag Packet Layer. * Split the AF packet into its tag items. Each tag item has a 4 character * tag, a length in bits and a value. The *ptr tag is dissected in the routine. * All other tags are listed and may be handled by other dissectors. * Child dissectors are tied to the parent tree, not to this tree, so that * they appear at the same level as DCP. * \param[in,out] tvb The buffer containing the packet * \param[in,out] pinfo The packet info structure * \param[in,out] tree The structure containing the details which will be displayed, filtered, etc. */ static int dissect_tpl(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, void* data _U_) { proto_tree *tpl_tree; guint offset=0; proto_item *ti; col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCP-TPL"); ti = proto_tree_add_item (tree, proto_tpl, tvb, 0, -1, ENC_NA); tpl_tree = proto_item_add_subtree (ti, ett_tpl); while(offset