From c3cf33d1ef37a7cb9901d7c1310fd776a73a198f Mon Sep 17 00:00:00 2001 From: Lajos Olah Date: Sun, 9 Sep 2018 11:09:40 +0000 Subject: MTP2 bitstream dissector - dissect MTP2 packets from RTP stream Change-Id: I704c68caa8cd8aa60c6417e6ee038db5f6507686 Reviewed-on: https://code.wireshark.org/review/29506 Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- epan/dissectors/packet-mtp2.c | 881 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 878 insertions(+), 3 deletions(-) diff --git a/epan/dissectors/packet-mtp2.c b/epan/dissectors/packet-mtp2.c index 055278f8cc..68ade55f47 100644 --- a/epan/dissectors/packet-mtp2.c +++ b/epan/dissectors/packet-mtp2.c @@ -21,12 +21,86 @@ #include #include #include +#include +#include +#include void proto_register_mtp2(void); void proto_reg_handoff_mtp2(void); static dissector_handle_t mtp2_handle; +/* possible packet states */ +enum packet_direction_state_mtp2 {FORWARD, BACKWARD}; + +/* structure for telling the bitstream dissector if it shall use a mtp2_flag_search value from the prev. packet */ +typedef struct mtp2_flag_search { + gboolean set; /* shows if the mtp2_flag_search value is valid, needed to be set in the dissect function */ + guint8 mtp2_flag_search; /* mtp2_flag_search value itself */ +} mtp2_mtp2_flag_search_t; + +/* Possible states of the state machine for decoding the MTP2 bitstream */ +enum mtp2_bitstream_states {OUT_OF_SYNC, FLAGS, DATA}; + +/* data type for chained list of found MTP2 packets in RTP stream */ +typedef struct mtp2_recognized_packet { + tvbuff_t *data; /* data of the actual packet */ + guint8 unalignment_offset; /* !=0 signals if this packet was not a multiple of 8 bits in the stream */ +} mtp2_recognized_packet_t; + +/* structure used in mtp2_dissect_tvb_res + this contains the tvb's before the first and after last header */ +typedef struct mtp2_remain_data { + tvbuff_t *before_first_flag; /* data found before the first flag */ + tvbuff_t *after_last_flag; /* data found after the last flag */ + guint8 before_fh_unalignment_offset; /* !=0 signals if the before_fh data was not a multiple of 8 bits in the stream */ + gboolean before_fh_frame_reset; /* signals if there was a frame reset in the data before the 1st flag */ +} mtp2_remain_data_t; + + +/* structure to store the result of dissect_mtp2_tvb function */ +typedef struct mtp2_dissect_tvb_res { + mtp2_remain_data_t mtp2_remain_data; /* stores the tvbuffs found before 1st and after last flags in the packet */ + mtp2_mtp2_flag_search_t mtp2_flag_search; /* this contains the mtp2_flag_search's value at the end of the packet dissection */ + wmem_list_t *found_packets; /* contains the packets found in tvbuff */ + guint8 data_buff; /* to store the data_buff value */ + guint8 data_buff_offset; /* to store the data_buff_offset value */ + guint8 last_flag_beginning_offset_for_align_check; /* the offset of the last flag's beginning have to be stored */ + gboolean flag_found; /* boolean value to sign if there was a flag in the RTP packet or not */ + enum mtp2_bitstream_states state; /* to store the value of the state of the dissection after finish */ +} mtp2_dissect_tvb_res_t; + +/* mtp2 per-packet data */ +typedef struct mtp2_ppd { + mtp2_mtp2_flag_search_t mtp2_flag_search; /* flag search needed to pass to dissect_mtp2_tvb - it was derived from the prev. packet in the same direction */ + guint8 data_buff; /* data buff needed to pass to dissect_mtp2_tvb - it was derived from the prev. packet in the same direction */ + guint8 data_buff_offset; /* data buff offset needed to pass to dissect_mtp2_tvb - it was derived from the prev. packet in the same direction */ + guint8 last_flag_beginning_offset_for_align_check; /* variable for align check, stores the last flag's beginning's offset */ + guint32 reass_seq_num_for_reass_check_before_fh; /* this is the id (reass_seq_num) which should be used for looking up reassembled data found before the first flag */ + guint32 reass_seq_num_for_reass_check_after_lh; /* this is the id (reass_seq_num) which should be used for looking up reassembled data found after the last flag */ + enum mtp2_bitstream_states state; /* state needed to pass to dissect_mtp2_tvb - it was derived from the prev. packet in the same direction */ +} mtp2_ppd_t; + +/* conversation data about the previous packet in the conversation (in one direction) */ +typedef struct mtp2_convo_data_prev_packet { + mtp2_mtp2_flag_search_t mtp2_flag_search; /* storing the prev. packet's flag search */ + guint8 data_buff; /* storing the prev. packet's data buffer */ + guint8 data_buff_offset; /* storing the prev. packet's data buffer offset */ + guint8 last_flag_beginning_offset_for_align_check; /* storing the prev. packet's last flag's offset */ + guint32 reass_seq_num; /* storing the prev. packet's reassemble seq. num */ + enum mtp2_bitstream_states state; /* storing the prev. packet's state in the forward direction */ +} mtp2_convo_data_prev_packet_t; + +/* conversation data for MTP2 dissection from RTP payload */ +typedef struct mtp2_convo_data { + address addr_a; /* storing the first packet's originating address */ + address addr_b; /* storing the first packet's terminating address */ + guint32 port_a; /* storing the first packet's originating port */ + guint32 port_b; /* storing the first packet's terminating port */ + mtp2_convo_data_prev_packet_t *forward; /* storing needed info about the prev. packet's in forward direction */ + mtp2_convo_data_prev_packet_t *backward; /* storing needed info about the prev. packet's in backward direction */ +} mtp2_convo_data_t; + /* Initialize the protocol and registered fields */ static int proto_mtp2 = -1; static int hf_mtp2_bsn = -1; @@ -46,6 +120,48 @@ static int hf_mtp2_sf = -1; static int hf_mtp2_sf_extra = -1; static int hf_mtp2_fcs_16 = -1; static int hf_mtp2_fcs_16_status = -1; +static int hf_mtp2_unexpect_end = -1; +static int hf_mtp2_frame_reset = -1; + +/* reassemble variables */ +static int hf_mtp2_fragments = -1; +static int hf_mtp2_fragment = -1; +static int hf_mtp2_fragment_overlap = -1; +static int hf_mtp2_fragment_overlap_conflicts = -1; +static int hf_mtp2_fragment_multiple_tails = -1; +static int hf_mtp2_fragment_too_long_fragment = -1; +static int hf_mtp2_fragment_error = -1; +static int hf_mtp2_fragment_count = -1; +static int hf_mtp2_reassembled_in = -1; +static int hf_mtp2_reassembled_length = -1; +static gint ett_mtp2_fragment = -1; +static gint ett_mtp2_fragments = -1; + +/* local static const needed for reassembly */ +static const fragment_items mtp2_frag_items = { + &ett_mtp2_fragment, + &ett_mtp2_fragments, + &hf_mtp2_fragments, + &hf_mtp2_fragment, + &hf_mtp2_fragment_overlap, + &hf_mtp2_fragment_overlap_conflicts, + &hf_mtp2_fragment_multiple_tails, + &hf_mtp2_fragment_too_long_fragment, + &hf_mtp2_fragment_error, + &hf_mtp2_fragment_count, + &hf_mtp2_reassembled_in, + &hf_mtp2_reassembled_length, + NULL, + "MTP2 Message fragments" +}; + +/* needed for packet reassembly */ +static reassembly_table mtp2_reassembly_table; + +/* variables needed for property registration to wireshark menu */ +static range_t *mtp2_rtp_payload_types; +static range_t *pref_mtp2_rtp_payload_types; +static gboolean reverse_bit_order_mtp2 = FALSE; static expert_field ei_mtp2_checksum_error = EI_INIT; static expert_field ei_mtp2_li_bad = EI_INIT; @@ -56,6 +172,13 @@ static gint ett_mtp2 = -1; static dissector_handle_t mtp3_handle; static gboolean use_extended_sequence_numbers_default = FALSE; +/* sequence number of the actual packet to be reassembled + * this is needed because the reassemble handler uses a key based on the + * source and destination IP addresses + * therefore if there are multiple streams between 2 IP end-points + * the reassemble sequence numbers can conflict if they are based on conversations */ +static guint32 mtp2_absolute_reass_seq_num = 0; + #define BSN_BIB_LENGTH 1 #define FSN_FIB_LENGTH 1 #define LI_LENGTH 1 @@ -91,6 +214,27 @@ static gboolean use_extended_sequence_numbers_default = FALSE; #define EXTENDED_LI_MASK 0x01ff #define EXTENDED_SPARE_MASK 0xfe00 +/* remove comment to enable debugging of bitstream dissector + * if enabled this produces printout to stderr like this for every packet: + +start_dissect_bitstream_packet: 2120 +11010010 SKIPPED ZEROS: 3. +10000000 +01100001 +01101111 +10011111 SKIPPED ZEROS: 2. +10011001 FLAG FOUND +11110100 SKIPPED ZEROS: 5. +10100000 +00011000 + * under the development it can be very helpful to see RTP packet content like this + * to identify and solve problems regarding bitstream parsing*/ +/*#define MTP2_BITSTREAM_DEBUG 1*/ + +#ifdef MTP2_BITSTREAM_DEBUG +#include +#endif + static void dissect_mtp2_header(tvbuff_t *su_tvb, packet_info *pinfo, proto_item *mtp2_tree, gboolean use_extended_sequence_numbers, gboolean validate_crc, guint32 *li) { @@ -378,6 +522,670 @@ dissect_mtp2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_ return tvb_captured_length(tvb); } +static void +mtp2_init_routine(void) +{ + reassembly_table_register(&mtp2_reassembly_table, &addresses_ports_reassembly_table_functions); +} + +/* get one bit of a guint8 byte +* based on the order set in the preferences +* reverse_bit_order_mtp2 = FALSE: as the Q.703 states +* reverse_bit_order_mtp2 = TRUE: just the opposite +*/ +static gboolean +get_bit(guint8 byte, guint8 bit) +{ + if (reverse_bit_order_mtp2 == FALSE) { + return byte & ((0x80 >> (bit-1))) ? TRUE : FALSE; + } else { + return byte & ((0x01 << (bit-1))) ? TRUE : FALSE; + } +} + +/* store new byte of an MTP2 frame in an array + * after the whole packet is stored the array will be used to construct a new tvb */ +static void +new_byte(char full_byte, guint8 **data, guint8 *data_len) +{ + guint8 *new_data = NULL; + int i = 0; + + if ((*data_len) == 0) { + /* if data was never stored in this buffer before */ + *data = (guint8 *)wmem_alloc(wmem_packet_scope(), sizeof(guint8)); + (**data) = full_byte; + (*data_len)++; + } else { + /* if this buffer is used -> create a completely new one + * note, that after the dissection of this packet + * the old data will be freed automatically (because of the wmem_alloc) */ + new_data = (guint8 *)wmem_alloc(wmem_packet_scope(), sizeof(guint8)*((*data_len)+1)); + /* copy the old one's content */ + for (i = 0;i<(*data_len);i++) { + *(new_data+i) = *((*data)+i); + } + /* store the new data */ + *(new_data+*data_len) = full_byte; + /* re-point the pointer to the new structure's head */ + *data = new_data; + (*data_len)++; + } +} + +#ifdef MTP2_BITSTREAM_DEBUG +/* print debug info to stderr if debug is enabled + * this function prints the packet bytes as bits separated by new lines + * and adds extra info to bytes (flag found, frame reset, zeros were skipped, etc. */ +static void +debug(char *format, ...) +{ + guint32 max_buffer_length = 256; + gchar *buffer = NULL; + va_list args; + + buffer = (gchar *) wmem_alloc(wmem_packet_scope(), max_buffer_length); + buffer[0] = '\0'; + + va_start(args,format); + g_vsnprintf(buffer,max_buffer_length,format,args); + g_printf("%s",buffer); + va_end (args); +} +#endif + +/* based on the actual packet's addresses and ports, + * this function determines the packet's direction */ +static enum packet_direction_state_mtp2 +get_direction_state(packet_info *pinfo, mtp2_convo_data_t *convo_data) +{ + if (convo_data != NULL) { + if (addresses_equal(&convo_data->addr_a, &pinfo->src) + && addresses_equal(&convo_data->addr_b, &pinfo->dst) + && convo_data->port_a == pinfo->srcport + && convo_data->port_b == pinfo->destport) { + return FORWARD; + } else if (addresses_equal(&convo_data->addr_b, &pinfo->src) + && addresses_equal(&convo_data->addr_a, &pinfo->dst) + && convo_data->port_b == pinfo->srcport + && convo_data->port_a == pinfo->destport) { + return BACKWARD; + } + } + + return FORWARD; +} + +/* prepares the data to be stored as found packet in wmem_list */ +static mtp2_recognized_packet_t* +prepare_data_for_found_packet(tvbuff_t *tvb, guint8 unalignment_offset) +{ + mtp2_recognized_packet_t *packet; + + packet = (mtp2_recognized_packet_t *)wmem_alloc(wmem_packet_scope(), sizeof(mtp2_recognized_packet_t)); + /* store values */ + packet->data = tvb; + packet->unalignment_offset = unalignment_offset; + + return packet; +} + +/* this function does the actual dissection of a tvb got from the RTP dissector + * sets the mtp2_flag_search, data_buffer, it's offset and the state to the one which was stored + * at the end of the previous packet's dissection in the same direction */ +static mtp2_dissect_tvb_res_t* +dissect_mtp2_tvb(tvbuff_t* tvb, mtp2_mtp2_flag_search_t back_mtp2_flag_search, guint8 back_data_buff, guint8 back_data_buff_offset, + enum mtp2_bitstream_states back_state, guint8 back_last_flag_beginning_offset_for_align_check) +{ + guint8 mtp2_flag_search = 0x00, /* this helps to detect the flags in the bitstream */ + data_buff = 0x00, /* buffer to store the found bits without the stuffed zeros */ + data_buff_offset = 0, /* index of the data_buff_offset, where to store the next bit */ + available_bytes_in_rtp_payload = 0, /* stores the tvb's length which need to be analized */ + *found_data_buff_byte = NULL, /* buffer to store the found data_buff bytes till they are assembled to a tvb */ + offset = 0, /* offset of the tvb, needed to get the appropriate byte */ + data_len = 0, /* the length of the array where the data_buff's are stored */ + flag_beginning_offset_for_align_check = 0; /* this stores the offset of the fist bit in a flag */ +#ifdef MTP2_BITSTREAM_DEBUG + gboolean zero_skip0 = 0, /* needed for debug output */ + zero_skip1 = 0, /* needed for debug output */ + flag = FALSE, /* needed for debug to print flag found message. reseted at every new octet read from tvb */ + frame_reset = FALSE; /* needed for debug, informs about a frame reset */ +#endif + enum mtp2_bitstream_states state = OUT_OF_SYNC; /* actual state of the dissection */ + tvbuff_t *new_tvb = NULL; /* tvbuff which stores the assembled data from the data pointer */ + mtp2_dissect_tvb_res_t *result = NULL; /* the result structure */ + + /* initialize the result structure, this will be returned at the end */ + result = (mtp2_dissect_tvb_res_t *)wmem_alloc(wmem_packet_scope(), sizeof(mtp2_dissect_tvb_res_t)); + result->mtp2_remain_data.before_first_flag = NULL; + result->mtp2_remain_data.before_fh_unalignment_offset = 0; + result->mtp2_remain_data.before_fh_frame_reset = FALSE; + result->mtp2_remain_data.after_last_flag = NULL; + result->found_packets = wmem_list_new(wmem_packet_scope()); + result->flag_found = FALSE; + result->last_flag_beginning_offset_for_align_check = 0; + + /* set the mtp2_flag_search if it is set */ + if (back_mtp2_flag_search.set == TRUE) { + mtp2_flag_search = back_mtp2_flag_search.mtp2_flag_search; + } + /* set every other variables from the prev. packet's end in the same direction */ + data_buff = back_data_buff; + data_buff_offset = back_data_buff_offset; + state = back_state; + flag_beginning_offset_for_align_check = back_last_flag_beginning_offset_for_align_check; + + /* determine how many byte are in the RTP payload */ + available_bytes_in_rtp_payload = tvb_reported_length_remaining(tvb, offset); + + /* walk through the tvb in means of octets */ + while (offset < available_bytes_in_rtp_payload) { + /* get actual packet's byte */ + guint8 byte = tvb_get_guint8(tvb,offset); + /* for every bit in the byte */ + for (guint8 i=1; i <= 8; i++) { + /* get the bit's boolean value got from byte[i] */ + gboolean bit = get_bit(byte, i); + +#ifdef MTP2_BITSTREAM_DEBUG + /* in case of debug, print just the pure RTP payload, not the previous packet's end */ + debug("%u",(bit==FALSE?0:1)); +#endif + + /* update the mtp2_flag_search */ + mtp2_flag_search = (mtp2_flag_search << 1) | bit; + + /* this section contains actions to be taken when the state is not OUT_OF_SYNC like + * skipping zeros after every 5 bits */ + if (state != OUT_OF_SYNC) { + /* The only values of mtp2_flag_search if we need to drop a zero is 0xBE and 0x3E */ + if ( (mtp2_flag_search == 0xBE || mtp2_flag_search == 0x3E)) { +#ifdef MTP2_BITSTREAM_DEBUG + /* set the debug variables */ + if (zero_skip0 == 0) + zero_skip0 = i; + else + zero_skip1 = i; +#endif + /* if we need to skip a zero, the next flag offset have to be incremented because the raw data + * between 2 flags is not a multiple of 8 bits now */ + flag_beginning_offset_for_align_check = (flag_beginning_offset_for_align_check + 1) % 8; + } else { + /* No drop -> store the value */ + data_buff = data_buff | (bit << data_buff_offset); + data_buff_offset++; + /* when a new complete byte without zeros was found */ + if (data_buff_offset == 8) { + /* we don't store flags */ + if (data_buff != 0x7E) { + /* store the data and change the state */ + state = DATA; + new_byte(data_buff, &found_data_buff_byte, &data_len); + } + /* clear data_buff and it's offset */ + data_buff = 0x00; + data_buff_offset = 0; + } + } + } + /* we have a flag */ + if (mtp2_flag_search == 0x7E && + !(offset == 0 && i < 8 && back_mtp2_flag_search.set == FALSE)) + /* the second part of the '&&' is not to recognize the 1111110x pattern as flag in the beginning of the 1st packet in every direction + * the 1111110 would be shifted into the mtp2_flag_search variable, this has a 0x00 initial value + * so after shifting 7 bits, the value of mtp2_flag_search would be 01111110 however there was no leading 0*/ + { + /* set the state */ + state = FLAGS; + /* if before this flag, we found some real packet related btyes */ + if (data_len != 0) { + guint8 unaligned_packet_offset = 0; /* !=0 signals if the packet just found was not a multiple of 8 bits in the bitstream */ + /* here we check if the just found MTP2 packet is unaligned or not + * 0 is not valid value meaning the flag_beginning_offset_for_align_check was not set at the beginning of the func. + * if flag_beginning_offset_for_align_check != i, we have an unaligned packet */ + if (flag_beginning_offset_for_align_check != 0 && flag_beginning_offset_for_align_check != i) { + /* set the unaligned offset */ + unaligned_packet_offset = i; + /* clear the data_buff and offset + * this is needed because at the next flag we would have data_buff offset unaligned + * and we would find a 1 length packet with a part of the flag in it */ + data_buff = 0x00; + data_buff_offset = 0; + } + /* fill the temporary buffer with data */ + guint8 *buff = (guint8 *) wmem_memdup(wmem_packet_scope(), found_data_buff_byte, data_len); + /* Allocate new tvb for the proto frame */ + new_tvb = tvb_new_child_real_data(tvb, buff, data_len, data_len); + /* if there were no flags before, we've found the bytes before the first flag */ + if (result->flag_found == FALSE) { + /* this tvb is the one we found before the 1st flag */ + result->mtp2_remain_data.before_first_flag = new_tvb; + /* if the bytes before the first flag was unaligned -> the calling function needs this info */ + result->mtp2_remain_data.before_fh_unalignment_offset = unaligned_packet_offset; + } else { + /* add the packet to the processable packet's list */ + wmem_list_append(result->found_packets, prepare_data_for_found_packet(new_tvb,unaligned_packet_offset)); + } + /* clear data array (free will be done automatically) */ + data_len = 0; + found_data_buff_byte = NULL; + } + + flag_beginning_offset_for_align_check = i; +#ifdef MTP2_BITSTREAM_DEBUG + /* for local debug purposes */ + flag = TRUE; +#endif + /* set the result found in the result to TRUE */ + result->flag_found = TRUE; + /* 7 consecutive 1s => out of sync */ + } else if (mtp2_flag_search == 0x7F || mtp2_flag_search == 0xFE || mtp2_flag_search == 0xFF) { + /* set the state and clear everything */ + state = OUT_OF_SYNC; + data_len = 0; + found_data_buff_byte = NULL; + data_buff = 0x00; + data_buff_offset = 0; +#ifdef MTP2_BITSTREAM_DEBUG + frame_reset = TRUE; +#endif + if (result->flag_found == FALSE) + result->mtp2_remain_data.before_fh_frame_reset = TRUE; + } + } + +#ifdef MTP2_BITSTREAM_DEBUG + /* if there were flag, print debug info */ + if (flag) { + debug("\tFLAG FOUND"); + } + /* if there were zeros skipped, print the debug data */ + if (!zero_skip0 == 0) { + debug("\tSKIPPED ZEROS: %u.",zero_skip0); + if (!zero_skip1 == 0) + debug(" %u",zero_skip1); + } + /* if there was frame reset, print debug info */ + if (frame_reset) { + debug("\tFRAME RESET"); + } + /* print a \n to print the next byte in a different row */ + debug("\n"); + /* after every byte read from tvb clear debug stuff */ + zero_skip0 = 0; + zero_skip1 = 0; + /* set the debug variables as well */ + flag = FALSE; + frame_reset = FALSE; +#endif + /* increment tvb offset */ + offset++; + } + + if (data_len != 0) { + /* fill the temporary buffer with data */ + guint8 * buff = (guint8 *) wmem_memdup(wmem_packet_scope(), found_data_buff_byte, data_len); + /* Allocate new tvb for the MTP2 frame */ + new_tvb = tvb_new_child_real_data(tvb, buff, data_len, data_len); + /* this tvb is the one we found after the last flag */ + result->mtp2_remain_data.after_last_flag = new_tvb; + } + + /* we do not return NULL in before_first_flag because then the reassemble will not work + * we have to add a 0 length tvb with the flag "no more packets" */ + if (result->mtp2_remain_data.before_first_flag == NULL) { + /* fill the temporary buffer with data */ + guint8 *buff = (guint8 *) wmem_memdup(wmem_packet_scope(), found_data_buff_byte, 0); + /* Allocate new tvb for the MTP2 frame */ + new_tvb = tvb_new_child_real_data(tvb, buff, 0, 0); + /* this tvb is the one we found after the last flag */ + result->mtp2_remain_data.before_first_flag = new_tvb; + } + + /* don't set mtp2_flag_search and other stuff if the packet ended in out_of_sync */ + if (state != OUT_OF_SYNC) { + result->mtp2_flag_search.set = TRUE; + result->mtp2_flag_search.mtp2_flag_search = mtp2_flag_search; + result->data_buff = data_buff; + result->data_buff_offset = data_buff_offset; + } else { + result->mtp2_flag_search.set = FALSE; + result->mtp2_flag_search.mtp2_flag_search = result->data_buff = result->data_buff_offset = 0x00; + } + + /* set the state as well, in every case */ + result->state = state; + + /* set the last_flag_beginning_offset_for_align_check in the result structure */ + result->last_flag_beginning_offset_for_align_check = flag_beginning_offset_for_align_check; + + /* return the result structure */ + return result; +} + +/* function to get a new reass. sequence number */ +static guint32 +get_new_reass_seq_num(void) +{ + /* fail if it reached the max value */ + DISSECTOR_ASSERT(mtp2_absolute_reass_seq_num < 0xFFFFFFFE); + mtp2_absolute_reass_seq_num++; + + return mtp2_absolute_reass_seq_num; +} + + +/* sign if the packet is unaligned in proto tree */ +static void +issue_unaligned_info(proto_tree *tree, tvbuff_t *tvb, guint8 unalignment_offset) +{ + proto_tree_add_none_format(tree, hf_mtp2_unexpect_end, tvb, 0, tvb_reported_length_remaining(tvb,0), + "[Packet ended in the middle of an octet. Octet: last, Offset: %u]", + unalignment_offset); +} + +/* sign if the packet is unaligned in proto tree */ +static void +issue_frame_reset_info(proto_tree *tree, tvbuff_t *tvb) +{ + proto_tree_add_none_format(tree, hf_mtp2_frame_reset, tvb, 0, 0, + "[Frame Reset Occurred, No Reassembly]"); +} + +/* set per packet data based on direction data */ +static void +set_ppd_fields_based_on_convo_directon_data(mtp2_ppd_t *mtp2_ppd, mtp2_convo_data_prev_packet_t *direction_data) +{ + mtp2_ppd->mtp2_flag_search = direction_data->mtp2_flag_search; + mtp2_ppd->data_buff = direction_data->data_buff; + mtp2_ppd->data_buff_offset = direction_data->data_buff_offset; + mtp2_ppd->state = direction_data->state; + /* this is because the segment which will be reassembled in this packet + * is stored with the reass_seq_num stored in the convo data + * therefore we have to save this value for dissection in the future */ + mtp2_ppd->reass_seq_num_for_reass_check_before_fh = direction_data->reass_seq_num; + mtp2_ppd->last_flag_beginning_offset_for_align_check = direction_data->last_flag_beginning_offset_for_align_check; +} + +/* set convo data based on dissection result and reass_seq_num */ +static void +set_direction_fields_based_on_result_and_reass_seq_num(mtp2_convo_data_prev_packet_t *direction_data, mtp2_dissect_tvb_res_t *result, guint32 reass_seq_num) +{ + direction_data->mtp2_flag_search = result->mtp2_flag_search; + direction_data->data_buff = result->data_buff; + direction_data->data_buff_offset = result->data_buff_offset; + direction_data->state = result->state; + direction_data->reass_seq_num = reass_seq_num; + direction_data->last_flag_beginning_offset_for_align_check = result->last_flag_beginning_offset_for_align_check; +} + +/* function to dissect bitstream data of MTP2 */ +static int +dissect_mtp2_bitstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * user_data _U_) +{ + guint32 reass_seq_num = 0; /* reassemble sequence number at the beginning of this packet's dissection */ + conversation_t *conversation = NULL; /* conversation of the mtp2 dissection */ + mtp2_convo_data_t *convo_data = NULL; /* conversation data of the mtp2 dissection */ + mtp2_dissect_tvb_res_t *result = NULL; /* variable to store the result of dissect_mtp2_tvb */ + enum packet_direction_state_mtp2 dir_state = FORWARD; /* direction state of this packet in the conversation */ + mtp2_ppd_t *mtp2_ppd = NULL; /* per-packet data of this packet */ + +#ifdef MTP2_BITSTREAM_DEBUG + debug("start_dissect_bitstream_packet: %u\n",pinfo->fd->num); +#endif + + /* find conversation related to this packet */ + conversation = find_conversation(pinfo->fd->num,&pinfo->src, &pinfo->dst,conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0); + /* if there is no conversation or it does not contain the per packet data we need */ + if (conversation == NULL) { + /* there was no conversation => this packet is the first in a new conversation => let's create it */ + /* here we decide about the direction, every following packet with the same direction as this first one will be a forward packet */ + conversation = conversation_new(pinfo->fd->num,&pinfo->src, &pinfo->dst,conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0); + } + + /* there is no proto data in the conversation */ + if (conversation_get_proto_data(conversation, proto_mtp2) == NULL) { + /* create a new convo data and fill it with initial data */ + convo_data = (mtp2_convo_data_t *) wmem_alloc(wmem_file_scope(), sizeof(mtp2_convo_data_t)); + copy_address_wmem(wmem_file_scope(), &convo_data->addr_a, &pinfo->src); + copy_address_wmem(wmem_file_scope(), &convo_data->addr_b, &pinfo->dst); + convo_data->port_a = pinfo->srcport; + convo_data->port_b = pinfo->destport; + convo_data->forward = (mtp2_convo_data_prev_packet_t *) wmem_alloc(wmem_file_scope(), sizeof(mtp2_convo_data_prev_packet_t)); + convo_data->backward = (mtp2_convo_data_prev_packet_t *) wmem_alloc(wmem_file_scope(), sizeof(mtp2_convo_data_prev_packet_t)); + convo_data->forward->mtp2_flag_search.set = convo_data->backward->mtp2_flag_search.set= FALSE; + convo_data->forward->mtp2_flag_search.mtp2_flag_search = convo_data->backward->mtp2_flag_search.mtp2_flag_search = 0x00; + convo_data->forward->data_buff = convo_data->backward->data_buff = 0x00; + convo_data->forward->data_buff_offset = convo_data->backward->data_buff_offset = 0; + convo_data->forward->state = convo_data->backward->state = OUT_OF_SYNC; + convo_data->forward->reass_seq_num = get_new_reass_seq_num(); + convo_data->backward->reass_seq_num = get_new_reass_seq_num(); + convo_data->forward->last_flag_beginning_offset_for_align_check = convo_data->backward->last_flag_beginning_offset_for_align_check = 0; + /* store the convo data */ + conversation_add_proto_data(conversation, proto_mtp2, convo_data); + } else { + /* the packet is part of an existing conversation => get the conversation data */ + convo_data = (mtp2_convo_data_t*)conversation_get_proto_data(conversation, proto_mtp2); + } + + /* get the packet's state */ + dir_state = get_direction_state(pinfo, convo_data); + + /* get the per packet data */ + mtp2_ppd = (mtp2_ppd_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_mtp2, pinfo->fd->num); + + /* if there is no per packet data -> create it */ + if (mtp2_ppd == NULL) { + mtp2_ppd = (mtp2_ppd_t *) wmem_alloc(wmem_file_scope(), sizeof(mtp2_ppd_t)); + /* set the the proto_data_fields + * because these are the values which we would like to see + * if this packet is seen again */ + if (dir_state == FORWARD) { + set_ppd_fields_based_on_convo_directon_data(mtp2_ppd, convo_data->forward); + } else { + set_ppd_fields_based_on_convo_directon_data(mtp2_ppd, convo_data->backward); + } + /* store the ppd to be able to get in the next time we see this packet */ + p_add_proto_data(wmem_file_scope(), pinfo, proto_mtp2, pinfo->fd->num, mtp2_ppd); + } + + /* get the reass. seq num from the ppd + * this is needed because it is modified and stored in the convo_data */ + reass_seq_num = mtp2_ppd->reass_seq_num_for_reass_check_before_fh; + + /* call the function to dissect this actual tvb */ + result = dissect_mtp2_tvb(tvb, + mtp2_ppd->mtp2_flag_search, + mtp2_ppd->data_buff, + mtp2_ppd->data_buff_offset, + mtp2_ppd->state, + mtp2_ppd->last_flag_beginning_offset_for_align_check); + + /* if this is the first time, do the reassemble things + * else just check for reassembled data */ + if (pinfo->fd->flags.visited == FALSE) { + /* if there was a flag in this tvb, the data found before the 1st flag + * have to be treated differently than the data found after the last flag + * this means we need to use different reass_seq_num when adding them to the reass. handler */ + if (result->flag_found == TRUE) { + /* add the data found before the first flag with the same reass_seq_num as the + * data found after the last flag in the previous packet in this direction */ + fragment_add_seq_next(&mtp2_reassembly_table, /* bookkeeping table */ + result->mtp2_remain_data.before_first_flag, /* tvb containing the data which was unidentified before the first flag */ + 0, /* offset is 0 because this tvb contains the unidentified data only */ + pinfo, + mtp2_ppd->reass_seq_num_for_reass_check_before_fh, /* sequence number of the fragment stream */ + NULL, /* additional data to identify the segment */ + tvb_reported_length_remaining(result->mtp2_remain_data.before_first_flag, 0), /* length is the whole tvb's length */ + FALSE); /* there are no more fragments */ + + /* get a new reass seq num for the data found after the last flag */ + mtp2_ppd->reass_seq_num_for_reass_check_after_lh = reass_seq_num = get_new_reass_seq_num(); + + /* if there were data found after the last flag, add it to the reass. handler with the new reass_seq_num */ + if (result->mtp2_remain_data.after_last_flag != NULL) { + fragment_add_seq_next(&mtp2_reassembly_table, /* bookkeeping table */ + result->mtp2_remain_data.after_last_flag, /* tvb containing the data which was unidentified before the first flag */ + 0, /* offset is 0 because this tvb contains the unidentified data only */ + pinfo, + mtp2_ppd->reass_seq_num_for_reass_check_after_lh, /* sequence number of the fragment stream */ + NULL, /* additional data to identify the segment */ + tvb_reported_length_remaining(result->mtp2_remain_data.after_last_flag, 0), /* length is the whole tvb's length */ + TRUE); /* there are more fragments */ + } + } else { + /* here the increment of the reass_seq_num is not needed because this RTP frame was completely part + * of an MTP2 frame beginning in the previous packet + * this need to be added with the same reass_seq_num */ + if (result->mtp2_remain_data.after_last_flag != NULL) { + fragment_add_seq_next(&mtp2_reassembly_table, /* bookkeeping table */ + result->mtp2_remain_data.after_last_flag, /* tvb containing the data which was unidentified before the first flag */ + 0, /* offset is 0 because this tvb contains the unidentified data only */ + pinfo, + mtp2_ppd->reass_seq_num_for_reass_check_before_fh, /* sequence number of the fragment stream */ + NULL, /* additional data to identify the segment */ + tvb_reported_length_remaining(result->mtp2_remain_data.after_last_flag, 0), /* length is the whole tvb's length */ + TRUE); /* there are more fragments */ + } + } + /* store the values in convo_data + * but just in case if this packet was not seen before + * if it was + * then the convo data shall not be used (contains inappropriate info for us + * the actual values needed for reassembly should be get from the mtp2_ppd + * therefore no need to set the convo data */ + /* differentiate between forward and backward directions */ + if (dir_state == FORWARD) { + set_direction_fields_based_on_result_and_reass_seq_num(convo_data->forward, result, reass_seq_num); + } else { + set_direction_fields_based_on_result_and_reass_seq_num(convo_data->backward, result, reass_seq_num); + } + + /* if the packet was seen before */ + } else { + tvbuff_t *new_tvb = NULL; + fragment_head *frag_msg_before_fh = NULL; + fragment_head *frag_msg_after_lh = NULL; + gchar *col_info_str = NULL; /* char array to store temporary string for col info update */ + + /* get the fragment data both for before first and after last flags */ + /* before first flag */ + frag_msg_before_fh = fragment_get_reassembled_id(&mtp2_reassembly_table, + pinfo, + mtp2_ppd->reass_seq_num_for_reass_check_before_fh); + /* after last flag */ + frag_msg_after_lh = fragment_get_reassembled_id(&mtp2_reassembly_table, + pinfo, + mtp2_ppd->reass_seq_num_for_reass_check_after_lh); + /* if there is reassembled data before the first flag */ + if (frag_msg_before_fh != NULL) { + /* get the reassembled tvb */ + new_tvb = process_reassembled_data(result->mtp2_remain_data.before_first_flag, + 0, + pinfo, + (result->mtp2_remain_data.before_fh_unalignment_offset != 0 + ?"Reassembled MTP2 Packet [Unaligned]" + :"Reassembled MTP2 Packet"), + frag_msg_before_fh, + &mtp2_frag_items, + NULL, + tree); + /* there is reassembled data */ + if (new_tvb != NULL && tvb_reported_length_remaining(new_tvb, 0) > 0) { + + /* if there was a frame reset before the first flag */ + if (result->mtp2_remain_data.before_fh_frame_reset == TRUE) { + /* issue frame reset */ + issue_frame_reset_info(tree, new_tvb); + /* prepare col_info string */ + col_info_str = "[Frame Reset in reassebmly]"; + } else { + /* append the reassembled packet to the head of the packet list */ + wmem_list_prepend(result->found_packets, prepare_data_for_found_packet(new_tvb,result->mtp2_remain_data.before_fh_unalignment_offset)); + /* set the protocol name */ + col_add_str(pinfo->cinfo, COL_PROTOCOL, "MTP2"); + } + } + } + + /* if there were packets found */ + if (wmem_list_count(result->found_packets) != 0) { + /* boolean variable to help to print proper col_info if unaligned packet is found */ + gboolean was_unaligned_packet = FALSE; + /* pointer walking through the list of found packets */ + wmem_list_frame_t *recognized_packet = wmem_list_head(result->found_packets); + + /* info field pre-set, we can see the MTP2 strings even if there is an error in the dissection */ + col_add_str(pinfo->cinfo, COL_PROTOCOL, "MTP2"); + col_add_str(pinfo->cinfo, COL_INFO, "MTP2"); + /* while there are available packets */ + while (recognized_packet != NULL) { + mtp2_recognized_packet_t *recognized_packet_data = (mtp2_recognized_packet_t *) wmem_list_frame_data(recognized_packet); + if (recognized_packet_data->unalignment_offset == 0) { + /* pass the data to the mtp2 dissector */ + add_new_data_source(pinfo, recognized_packet_data->data, "MTP2 packet"); + dissect_mtp2_common(recognized_packet_data->data, pinfo, tree, FALSE, use_extended_sequence_numbers_default); + } else { + add_new_data_source(pinfo, recognized_packet_data->data, "MTP2 packet [Unaligned]"); + issue_unaligned_info(tree, recognized_packet_data->data, recognized_packet_data->unalignment_offset); + was_unaligned_packet = TRUE; + } + + /* increment the pointer */ + recognized_packet = wmem_list_frame_next(recognized_packet); + } + /* insert how many packets were found */ + col_info_str = g_strdup_printf("%s: %u Packet%s%s%s", + "MTP2", + wmem_list_count(result->found_packets), + (wmem_list_count(result->found_packets) > 1 + ?"s" + :"" + ), + (was_unaligned_packet + ?g_strdup_printf(" [Unaligned Packet%s]", (wmem_list_count(result->found_packets)>1 + ?"s" + :"")) + :"" + ), + (col_info_str == NULL + ?"" + :col_info_str + ) + ); + col_add_str(pinfo->cinfo, COL_INFO, col_info_str); + g_free(col_info_str); + /* if there were no packets found */ + } else { + if (tvb_reported_length_remaining(result->mtp2_remain_data.before_first_flag,0) == 0 + && result->mtp2_remain_data.after_last_flag != NULL + && frag_msg_before_fh) + { + col_add_str(pinfo->cinfo, COL_PROTOCOL, "MTP2"); + col_info_str = g_strdup_printf("[MTP2 Reassembled in: %u]", frag_msg_before_fh->reassembled_in); + col_add_str(pinfo->cinfo, COL_INFO, col_info_str); + g_free(col_info_str); + } else { + col_add_str(pinfo->cinfo, COL_PROTOCOL, "MTP2"); + col_info_str = "[MTP2 No Packets]"; + col_add_str(pinfo->cinfo, COL_INFO, col_info_str); + } + } + /* this adds the "Reassembled in" text to the proto tree to the packet where there is leftover data at the end */ + process_reassembled_data(result->mtp2_remain_data.after_last_flag, + 0, + pinfo, + "Reassembled MTP2 Packet", + frag_msg_after_lh, + &mtp2_frag_items, + NULL, + tree); + } + + /* the whole tvb was processed */ + return tvb_captured_length(tvb); +} + void proto_register_mtp2(void) { @@ -400,10 +1208,48 @@ proto_register_mtp2(void) { &hf_mtp2_sf_extra, { "Status field extra octet", "mtp2.sf_extra", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_mtp2_fcs_16, { "FCS 16", "mtp2.fcs_16", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_mtp2_fcs_16_status, { "FCS 16", "mtp2.fcs_16.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, NULL, HFILL } }, + { &hf_mtp2_unexpect_end, { "Unexpected packet end","mtp2.unexpected_end", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_mtp2_frame_reset, { "Frame reset", "mtp2.frame_reset", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + /* extend header fields with the reassemble ones */ + {&hf_mtp2_fragments, + {"Message fragments", "mtp2.msg.fragments", + FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment, + {"Message fragment", "mtp2.msg.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment_overlap, + {"Message fragment overlap", "mtp2.msg.fragment.overlap", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment_overlap_conflicts, + {"Message fragment overlapping with conflicting data", + "mtp2.msg.fragment.overlap.conflicts", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment_multiple_tails, + {"Message has multiple tail fragments", + "mtp2.msg.fragment.multiple_tails", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment_too_long_fragment, + {"Message fragment too long", "mtp2.msg.fragment.too_long_fragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment_error, + {"Message defragmentation error", "mtp2.msg.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_fragment_count, + {"Message defragmentation count", "mtp2.msg.fragment.count", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_reassembled_in, + {"Reassembled in", "mtp2.msg.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mtp2_reassembled_length, + {"Reassembled length", "mtp2.msg.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } } }; static gint *ett[] = { - &ett_mtp2 + &ett_mtp2, + /* extend ett with the fragment fields */ + &ett_mtp2_fragment, + &ett_mtp2_fragments }; static ei_register_info ei[] = { @@ -423,20 +1269,34 @@ proto_register_mtp2(void) expert_mtp2 = expert_register_protocol(proto_mtp2); expert_register_field_array(expert_mtp2, ei, array_length(ei)); - mtp2_module = prefs_register_protocol(proto_mtp2, NULL); + mtp2_module = prefs_register_protocol(proto_mtp2, proto_reg_handoff_mtp2); prefs_register_bool_preference(mtp2_module, "use_extended_sequence_numbers", "Use extended sequence numbers", "Whether the MTP2 dissector should use extended sequence numbers as described in Q.703, Annex A as a default.", &use_extended_sequence_numbers_default); - + /* register bool and range preferences */ + prefs_register_bool_preference(mtp2_module, + "reverse_bit_order_mtp2", + "Reverse bit order inside bytes", + "Reverse the bit order inside bytes specified in Q.703.", + &reverse_bit_order_mtp2); + prefs_register_range_preference(mtp2_module, "rtp_payload_type", + "RTP payload types for embedded packets in RTP stream", + "RTP payload types for embedded packets in RTP stream. Must be of the dynamic types " + "from 96 to 127.", + &pref_mtp2_rtp_payload_types, + 127); + register_init_routine(&mtp2_init_routine); } void proto_reg_handoff_mtp2(void) { dissector_handle_t mtp2_with_phdr_handle; + static dissector_handle_t mtp2_bitstream_handle; + static gboolean init = FALSE; dissector_add_uint("wtap_encap", WTAP_ENCAP_MTP2, mtp2_handle); mtp2_with_phdr_handle = create_dissector_handle(dissect_mtp2_with_phdr, @@ -445,6 +1305,21 @@ proto_reg_handoff_mtp2(void) mtp2_with_phdr_handle); mtp3_handle = find_dissector_add_dependency("mtp3", proto_mtp2); + + if (!init) { + mtp2_bitstream_handle = create_dissector_handle(dissect_mtp2_bitstream, proto_mtp2); + dissector_add_string("rtp_dyn_payload_type", "MTP2", mtp2_bitstream_handle); + init = TRUE; + } else { + if (!value_is_in_range(mtp2_rtp_payload_types, 0)) { + dissector_delete_uint_range("rtp.pt", mtp2_rtp_payload_types, mtp2_bitstream_handle); + } + } + + mtp2_rtp_payload_types = range_copy(wmem_epan_scope(), pref_mtp2_rtp_payload_types); + if (!value_is_in_range(mtp2_rtp_payload_types, 0)) { + dissector_add_uint_range("rtp.pt", mtp2_rtp_payload_types, mtp2_bitstream_handle); + } } /* -- cgit v1.2.3