aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLajos Olah <lajos.olah.jr@gmail.com>2018-09-09 11:09:40 +0000
committerAnders Broman <a.broman58@gmail.com>2018-09-13 03:56:42 +0000
commitc3cf33d1ef37a7cb9901d7c1310fd776a73a198f (patch)
tree2be9e34dddd864ce5eb037b081e353038a09f143
parent57a397bded9ad2e07e6a0f8fd4834a530202717e (diff)
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 <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
-rw-r--r--epan/dissectors/packet-mtp2.c881
1 files changed, 878 insertions, 3 deletions
diff --git a/epan/dissectors/packet-mtp2.c b/epan/dissectors/packet-mtp2.c
index 055278f..68ade55 100644
--- a/epan/dissectors/packet-mtp2.c
+++ b/epan/dissectors/packet-mtp2.c
@@ -21,12 +21,86 @@
#include <epan/crc16-tvb.h>
#include <epan/expert.h>
#include <wiretap/wtap.h>
+#include <epan/conversation.h>
+#include <epan/proto_data.h>
+#include <epan/reassemble.h>
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 <glib/gprintf.h>
+#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);
+ }
}
/*