/* packet-rtcp.c * * $Id: packet-rtcp.c,v 1.40 2004/02/25 09:31:06 guy Exp $ * * Routines for RTCP dissection * RTCP = Real-time Transport Control Protocol * * Copyright 2000, Philips Electronics N.V. * Written by Andreas Sikkema * * Ethereal - 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This dissector tries to dissect the RTCP protocol according to Annex A * of ITU-T Recommendation H.225.0 (02/98) and RFC 1889 * H.225.0 literally copies RFC 1889, but omitting a few sections. * * RTCP traffic is handled by an uneven UDP portnumber. This can be any * port number, but there is a registered port available, port 5005 * See Annex B of ITU-T Recommendation H.225.0, section B.7 * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "packet-rtcp.h" #if 0 #include "packet-ntp.h" #endif #include /* Version is the first 2 bits of the first octet*/ #define RTCP_VERSION(octet) ((octet) >> 6) /* Padding is the third bit; no need to shift, because true is any value other than 0! */ #define RTCP_PADDING(octet) ((octet) & 0x20) /* Receiver/ Sender count is the 5 last bits */ #define RTCP_COUNT(octet) ((octet) & 0x1F) static const value_string rtcp_version_vals[] = { { 0, "Old VAT Version" }, { 1, "First Draft Version" }, { 2, "RFC 1889 Version" }, { 0, NULL }, }; /* RTCP packet types according to Section A.11.1 */ #define RTCP_SR 200 #define RTCP_RR 201 #define RTCP_SDES 202 #define RTCP_BYE 203 #define RTCP_APP 204 /* Supplemental H.261 specific RTCP packet types according to Section C.3.5 */ #define RTCP_FIR 192 #define RTCP_NACK 193 static const value_string rtcp_packet_type_vals[] = { { RTCP_SR, "Sender Report" }, { RTCP_RR, "Receiver Report" }, { RTCP_SDES, "Source description" }, { RTCP_BYE, "Goodbye" }, { RTCP_APP, "Application specific" }, { RTCP_FIR, "Full Intra-frame Request (H.261)" }, { RTCP_NACK, "Negative Acknowledgement (H.261)" }, { 0, NULL }, }; /* RTCP SDES types (Section A.11.2) */ #define RTCP_SDES_END 0 #define RTCP_SDES_CNAME 1 #define RTCP_SDES_NAME 2 #define RTCP_SDES_EMAIL 3 #define RTCP_SDES_PHONE 4 #define RTCP_SDES_LOC 5 #define RTCP_SDES_TOOL 6 #define RTCP_SDES_NOTE 7 #define RTCP_SDES_PRIV 8 static const value_string rtcp_sdes_type_vals[] = { { RTCP_SDES_END, "END" }, { RTCP_SDES_CNAME, "CNAME (user and domain)" }, { RTCP_SDES_NAME, "NAME (common name)" }, { RTCP_SDES_EMAIL, "EMAIL (e-mail address)" }, { RTCP_SDES_PHONE, "PHONE (phone number)" }, { RTCP_SDES_LOC, "LOC (geographic location)" }, { RTCP_SDES_TOOL, "TOOL (name/version of source app)" }, { RTCP_SDES_NOTE, "NOTE (note about source)" }, { RTCP_SDES_PRIV, "PRIV (private extensions)" }, { 0, NULL }, }; /* RTCP header fields */ static int proto_rtcp = -1; static int hf_rtcp_version = -1; static int hf_rtcp_padding = -1; static int hf_rtcp_rc = -1; static int hf_rtcp_sc = -1; static int hf_rtcp_pt = -1; static int hf_rtcp_length = -1; static int hf_rtcp_ssrc_sender = -1; static int hf_rtcp_ntp = -1; static int hf_rtcp_rtp_timestamp = -1; static int hf_rtcp_sender_pkt_cnt = -1; static int hf_rtcp_sender_oct_cnt = -1; static int hf_rtcp_ssrc_source = -1; static int hf_rtcp_ssrc_fraction = -1; static int hf_rtcp_ssrc_cum_nr = -1; /* First the 32 bit number, then the split * up 16 bit values */ /* These two are added to a subtree */ static int hf_rtcp_ssrc_ext_high_seq = -1; static int hf_rtcp_ssrc_high_seq = -1; static int hf_rtcp_ssrc_high_cycles = -1; static int hf_rtcp_ssrc_jitter = -1; static int hf_rtcp_ssrc_lsr = -1; static int hf_rtcp_ssrc_dlsr = -1; static int hf_rtcp_ssrc_csrc = -1; static int hf_rtcp_ssrc_type = -1; static int hf_rtcp_ssrc_length = -1; static int hf_rtcp_ssrc_text = -1; static int hf_rtcp_ssrc_prefix_len = -1; static int hf_rtcp_ssrc_prefix_string= -1; static int hf_rtcp_subtype = -1; static int hf_rtcp_name_ascii = -1; static int hf_rtcp_app_data = -1; static int hf_rtcp_fsn = -1; static int hf_rtcp_blp = -1; static int hf_rtcp_padding_count = -1; static int hf_rtcp_padding_data = -1; /* RTCP fields defining a sub tree */ static gint ett_rtcp = -1; static gint ett_ssrc = -1; static gint ett_ssrc_item = -1; static gint ett_ssrc_ext_high = -1; static gint ett_sdes = -1; static gint ett_sdes_item = -1; static address fake_addr; static int heur_init = FALSE; static gboolean dissect_rtcp_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ); static void dissect_rtcp( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ); void rtcp_add_address( packet_info *pinfo, const unsigned char* ip_addr, int prt ) { address src_addr; conversation_t* pconv; /* * If this isn't the first time this packet has been processed, * we've already done this work, so we don't need to do it * again. */ if (pinfo->fd->flags.visited) return; src_addr.type = AT_IPv4; src_addr.len = 4; src_addr.data = ip_addr; /* * The first time the function is called let the udp dissector * know that we're interested in traffic */ if ( ! heur_init ) { heur_dissector_add( "udp", dissect_rtcp_heur, proto_rtcp ); heur_init = TRUE; } /* * Check if the ip address and port combination is not * already registered */ pconv = find_conversation( &src_addr, &fake_addr, PT_UDP, prt, 0, 0 ); /* * If not, add * XXX - use wildcard address and port B? */ if ( ! pconv ) { pconv = conversation_new( &src_addr, &fake_addr, PT_UDP, (guint32) prt, (guint32) 0, 0 ); conversation_add_proto_data(pconv, proto_rtcp, NULL); } } #if 0 static void rtcp_init( void ) { unsigned char* tmp_data; int i; /* Create a fake adddress... */ fake_addr.type = AT_IPv4; fake_addr.len = 4; tmp_data = g_malloc( fake_addr.len ); for ( i = 0; i < fake_addr.len; i++) { tmp_data[i] = 0; } fake_addr.data = tmp_data; } #endif static gboolean dissect_rtcp_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) { conversation_t* pconv; /* This is a heuristic dissector, which means we get all the UDP * traffic not sent to a known dissector and not claimed by * a heuristic dissector called before us! * So we first check if the frame is really meant for us. */ if ( ( pconv = find_conversation( &pinfo->src, &fake_addr, pinfo->ptype, pinfo->srcport, 0, 0 ) ) == NULL ) { /* * The source ip:port combination was not what we were * looking for, check the destination */ if ( ( pconv = find_conversation( &pinfo->dst, &fake_addr, pinfo->ptype, pinfo->destport, 0, 0 ) ) == NULL ) { return FALSE; } } /* * An RTCP conversation always has a data item for RTCP. * (Its existence is sufficient to indicate that this is an RTCP * conversation.) */ if (conversation_get_proto_data(pconv, proto_rtcp) == NULL) return FALSE; /* * The message is a valid RTCP message! */ dissect_rtcp( tvb, pinfo, tree ); return TRUE; } static int dissect_rtcp_nack( tvbuff_t *tvb, int offset, proto_tree *tree ) { /* Packet type = FIR (H261) */ proto_tree_add_uint( tree, hf_rtcp_rc, tvb, offset, 1, tvb_get_guint8( tvb, offset ) ); offset++; /* Packet type, 8 bits = APP */ proto_tree_add_item( tree, hf_rtcp_pt, tvb, offset, 1, FALSE ); offset++; /* Packet length in 32 bit words minus one */ proto_tree_add_uint( tree, hf_rtcp_length, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; /* SSRC */ proto_tree_add_uint( tree, hf_rtcp_ssrc_source, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; /* FSN, 16 bits */ proto_tree_add_uint( tree, hf_rtcp_fsn, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; /* BLP, 16 bits */ proto_tree_add_uint( tree, hf_rtcp_blp, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; return offset; } static int dissect_rtcp_fir( tvbuff_t *tvb, int offset, proto_tree *tree ) { /* Packet type = FIR (H261) */ proto_tree_add_uint( tree, hf_rtcp_rc, tvb, offset, 1, tvb_get_guint8( tvb, offset ) ); offset++; /* Packet type, 8 bits = APP */ proto_tree_add_item( tree, hf_rtcp_pt, tvb, offset, 1, FALSE ); offset++; /* Packet length in 32 bit words minus one */ proto_tree_add_uint( tree, hf_rtcp_length, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; /* SSRC */ proto_tree_add_uint( tree, hf_rtcp_ssrc_source, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; return offset; } static int dissect_rtcp_app( tvbuff_t *tvb, int offset, proto_tree *tree, unsigned int padding, unsigned int packet_len ) { unsigned int counter = 0; char ascii_name[5]; /* SSRC / CSRC */ proto_tree_add_uint( tree, hf_rtcp_ssrc_source, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; packet_len -= 4; /* Name (ASCII) */ for( counter = 0; counter < 4; counter++ ) ascii_name[ counter ] = tvb_get_guint8( tvb, offset + counter ); /* strncpy( ascii_name, pd + offset, 4 ); */ ascii_name[4] = '\0'; proto_tree_add_string( tree, hf_rtcp_name_ascii, tvb, offset, 4, ascii_name ); offset += 4; packet_len -= 4; /* Applications specific data */ if ( padding ) { /* If there's padding present, we have to remove that from the data part * The last octet of the packet contains the length of the padding */ packet_len -= tvb_get_guint8( tvb, offset + packet_len - 1 ); } proto_tree_add_item( tree, hf_rtcp_app_data, tvb, offset, packet_len, FALSE ); offset += packet_len; return offset; } static int dissect_rtcp_bye( tvbuff_t *tvb, int offset, proto_tree *tree, unsigned int count ) { unsigned int chunk = 1; unsigned int reason_length = 0; char* reason_text = NULL; while ( chunk <= count ) { /* source identifier, 32 bits */ proto_tree_add_item( tree, hf_rtcp_ssrc_source, tvb, offset, 4, FALSE); offset += 4; chunk++; } if ( tvb_reported_length_remaining( tvb, offset ) > 0 ) { /* Bye reason consists of an 8 bit length l and a string with length l */ reason_length = tvb_get_guint8( tvb, offset ); proto_tree_add_item( tree, hf_rtcp_ssrc_length, tvb, offset, 1, FALSE ); offset++; reason_text = tvb_get_string(tvb, offset, reason_length); proto_tree_add_string( tree, hf_rtcp_ssrc_text, tvb, offset, reason_length, reason_text ); g_free( reason_text ); offset += reason_length; } return offset; } static void dissect_rtcp_sdes( tvbuff_t *tvb, int offset, proto_tree *tree, unsigned int count ) { unsigned int chunk = 1; proto_item *sdes_item; proto_tree *sdes_tree; proto_tree *sdes_item_tree; proto_item *ti; int start_offset; int items_start_offset; guint32 ssrc; unsigned int item_len = 0; unsigned int sdes_type = 0; unsigned int counter = 0; unsigned int prefix_len = 0; char *prefix_string = NULL; while ( chunk <= count ) { /* Create a subtree for this chunk; we don't yet know the length. */ start_offset = offset; ssrc = tvb_get_ntohl( tvb, offset ); sdes_item = proto_tree_add_text(tree, tvb, offset, -1, "Chunk %u, SSRC/CSRC %u", chunk, ssrc); sdes_tree = proto_item_add_subtree( sdes_item, ett_sdes ); /* SSRC_n source identifier, 32 bits */ proto_tree_add_uint( sdes_tree, hf_rtcp_ssrc_source, tvb, offset, 4, ssrc ); offset += 4; /* Create a subtree for the SDES items; we don't yet know the length */ items_start_offset = offset; ti = proto_tree_add_text(sdes_tree, tvb, offset, -1, "SDES items" ); sdes_item_tree = proto_item_add_subtree( ti, ett_sdes_item ); /* * Not every message is ended with "null" bytes, so check for * end of frame instead. */ while ( ( tvb_reported_length_remaining( tvb, offset ) > 0 ) && ( tvb_get_guint8( tvb, offset ) != RTCP_SDES_END ) ) { /* ID, 8 bits */ sdes_type = tvb_get_guint8( tvb, offset ); proto_tree_add_item( sdes_item_tree, hf_rtcp_ssrc_type, tvb, offset, 1, FALSE ); offset++; /* Item length, 8 bits */ item_len = tvb_get_guint8( tvb, offset ); proto_tree_add_item( sdes_item_tree, hf_rtcp_ssrc_length, tvb, offset, 1, FALSE ); offset++; if ( sdes_type == RTCP_SDES_PRIV ) { /* PRIV adds two items between the SDES length * and value - an 8 bit length giving the * length of a "prefix string", and the string. */ prefix_len = tvb_get_guint8( tvb, offset ); proto_tree_add_item( sdes_item_tree, hf_rtcp_ssrc_prefix_len, tvb, offset, 1, FALSE ); offset++; prefix_string = g_malloc( prefix_len + 1 ); for ( counter = 0; counter < prefix_len; counter++ ) prefix_string[ counter ] = tvb_get_guint8( tvb, offset + counter ); /* strncpy( prefix_string, pd + offset, prefix_len ); */ prefix_string[ prefix_len ] = '\0'; proto_tree_add_string( sdes_item_tree, hf_rtcp_ssrc_prefix_string, tvb, offset, prefix_len, prefix_string ); g_free( prefix_string ); offset += prefix_len; } prefix_string = g_malloc( item_len + 1 ); for ( counter = 0; counter < item_len; counter++ ) prefix_string[ counter ] = tvb_get_guint8( tvb, offset + counter ); /* strncpy( prefix_string, pd + offset, item_len ); */ prefix_string[ item_len] = 0; proto_tree_add_string( sdes_item_tree, hf_rtcp_ssrc_text, tvb, offset, item_len, prefix_string ); g_free( prefix_string ); offset += item_len; } /* Set the length of the items subtree. */ proto_item_set_len(ti, offset - items_start_offset); /* 32 bits = 4 bytes, so..... * If offset % 4 != 0, we divide offset by 4, add one and then * multiply by 4 again to reach the boundary */ if ( offset % 4 != 0 ) offset = ((offset / 4) + 1 ) * 4; /* Set the length of this chunk. */ proto_item_set_len(sdes_item, offset - start_offset); chunk++; } } static int dissect_rtcp_rr( tvbuff_t *tvb, int offset, proto_tree *tree, unsigned int count ) { unsigned int counter = 1; proto_tree *ssrc_tree = (proto_tree*) NULL; proto_tree *ssrc_sub_tree = (proto_tree*) NULL; proto_tree *high_sec_tree = (proto_tree*) NULL; proto_item *ti = (proto_item*) NULL; guint8 rr_flt; unsigned int cum_nr = 0; while ( counter <= count ) { /* Create a new subtree for a length of 24 bytes */ ti = proto_tree_add_text(tree, tvb, offset, 24, "Source %u", counter ); ssrc_tree = proto_item_add_subtree( ti, ett_ssrc ); /* SSRC_n source identifier, 32 bits */ proto_tree_add_uint( ssrc_tree, hf_rtcp_ssrc_source, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; ti = proto_tree_add_text(ssrc_tree, tvb, offset, 20, "SSRC contents" ); ssrc_sub_tree = proto_item_add_subtree( ti, ett_ssrc_item ); /* Fraction lost, 8bits */ rr_flt = tvb_get_guint8( tvb, offset ); proto_tree_add_uint_format( ssrc_sub_tree, hf_rtcp_ssrc_fraction, tvb, offset, 1, rr_flt, "Fraction lost: %u / 256", rr_flt ); offset++; /* Cumulative number of packets lost, 24 bits */ cum_nr = tvb_get_ntohl( tvb, offset ) >> 8; proto_tree_add_uint( ssrc_sub_tree, hf_rtcp_ssrc_cum_nr, tvb, offset, 3, cum_nr ); offset += 3; /* Extended highest sequence nr received, 32 bits * Just for the sake of it, let's add another subtree * because this might be a little clearer */ ti = proto_tree_add_uint( ssrc_tree, hf_rtcp_ssrc_ext_high_seq, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); high_sec_tree = proto_item_add_subtree( ti, ett_ssrc_ext_high ); /* Sequence number cycles */ proto_tree_add_uint( high_sec_tree, hf_rtcp_ssrc_high_cycles, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; /* highest sequence number received */ proto_tree_add_uint( high_sec_tree, hf_rtcp_ssrc_high_seq, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; /* Interarrival jitter */ proto_tree_add_uint( ssrc_tree, hf_rtcp_ssrc_jitter, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; /* Last SR timestamp */ proto_tree_add_uint( ssrc_tree, hf_rtcp_ssrc_lsr, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; /* Delay since last SR timestamp */ proto_tree_add_uint( ssrc_tree, hf_rtcp_ssrc_dlsr, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; counter++; } return offset; } static int dissect_rtcp_sr( tvbuff_t *tvb, int offset, proto_tree *tree, unsigned int count ) { #if 0 gchar buff[ NTP_TS_SIZE ]; char* ptime = tvb_get_ptr( tvb, offset, 8 ); /* Retreive the NTP timestamp. Using the NTP dissector for this */ ntp_fmt_ts( ptime, buff ); proto_tree_add_string_format( tree, hf_rtcp_ntp, tvb, offset, 8, ( const char* ) &buff, "NTP timestamp: %s", &buff ); free( ptime ); ?????????????????????????????????????????????????????????????????? offset += 8; #else /* * XXX - RFC 1889 says this is an NTP timestamp, but that appears * not to be the case. */ proto_tree_add_text(tree, tvb, offset, 4, "Timestamp, MSW: %u", tvb_get_ntohl(tvb, offset)); offset += 4; proto_tree_add_text(tree, tvb, offset, 4, "Timestamp, LSW: %u", tvb_get_ntohl(tvb, offset)); offset += 4; #endif /* RTP timestamp, 32 bits */ proto_tree_add_uint( tree, hf_rtcp_rtp_timestamp, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; /* Sender's packet count, 32 bits */ proto_tree_add_uint( tree, hf_rtcp_sender_pkt_cnt, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; /* Sender's octet count, 32 bits */ proto_tree_add_uint( tree, hf_rtcp_sender_oct_cnt, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; /* The rest of the packet is equal to the RR packet */ if ( count != 0 ) offset = dissect_rtcp_rr( tvb, offset, tree, count ); return offset; } static void dissect_rtcp( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) { proto_item *ti = NULL; proto_tree *rtcp_tree = NULL; unsigned int temp_byte = 0; unsigned int padding_set = 0; unsigned int elem_count = 0; unsigned int packet_type = 0; unsigned int offset = 0; guint16 packet_length = 0; if ( check_col( pinfo->cinfo, COL_PROTOCOL ) ) { col_set_str( pinfo->cinfo, COL_PROTOCOL, "RTCP" ); } if ( check_col( pinfo->cinfo, COL_INFO) ) { /* The second octet contains the packet type */ /* switch ( pd[ offset + 1 ] ) { */ switch ( tvb_get_guint8( tvb, 1 ) ) { case RTCP_SR: col_set_str( pinfo->cinfo, COL_INFO, "Sender Report"); break; case RTCP_RR: col_set_str( pinfo->cinfo, COL_INFO, "Receiver Report"); break; case RTCP_SDES: col_set_str( pinfo->cinfo, COL_INFO, "Source Description"); break; case RTCP_BYE: col_set_str( pinfo->cinfo, COL_INFO, "Goodbye"); break; case RTCP_APP: col_set_str( pinfo->cinfo, COL_INFO, "Application defined"); break; case RTCP_FIR: col_set_str( pinfo->cinfo, COL_INFO, "Full Intra-frame Request (H.261)"); break; case RTCP_NACK: col_set_str( pinfo->cinfo, COL_INFO, "Negative Acknowledgement (H.261)"); break; default: col_set_str( pinfo->cinfo, COL_INFO, "Unknown packet type"); break; } } if ( tree ) { /* * Check if there are at least 4 bytes left in the frame, * the last 16 bits of those is the length of the current * RTCP message. The last compound message contains padding, * that enables us to break from the while loop. */ while ( tvb_bytes_exist( tvb, offset, 4) ) { /* * First retreive the packet_type */ packet_type = tvb_get_guint8( tvb, offset + 1 ); /* * Check if it's a valid type */ if ( ( packet_type < 192 ) || ( packet_type > 204 ) ) break; /* * get the packet-length for the complete RTCP packet */ packet_length = ( tvb_get_ntohs( tvb, offset + 2 ) + 1 ) * 4; ti = proto_tree_add_item(tree, proto_rtcp, tvb, offset, packet_length, FALSE ); rtcp_tree = proto_item_add_subtree( ti, ett_rtcp ); temp_byte = tvb_get_guint8( tvb, offset ); proto_tree_add_uint( rtcp_tree, hf_rtcp_version, tvb, offset, 1, temp_byte); padding_set = RTCP_PADDING( temp_byte ); proto_tree_add_boolean( rtcp_tree, hf_rtcp_padding, tvb, offset, 1, temp_byte ); elem_count = RTCP_COUNT( temp_byte ); switch ( packet_type ) { case RTCP_SR: case RTCP_RR: /* Receiver report count, 5 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_rc, tvb, offset, 1, temp_byte ); offset++; /* Packet type, 8 bits */ proto_tree_add_item( rtcp_tree, hf_rtcp_pt, tvb, offset, 1, FALSE ); offset++; /* Packet length in 32 bit words MINUS one, 16 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_length, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; /* Sender Synchronization source, 32 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_ssrc_sender, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); offset += 4; if ( packet_type == RTCP_SR ) offset = dissect_rtcp_sr( tvb, offset, rtcp_tree, elem_count ); else offset = dissect_rtcp_rr( tvb, offset, rtcp_tree, elem_count ); break; case RTCP_SDES: /* Source count, 5 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_sc, tvb, offset, 1, temp_byte ); offset++; /* Packet type, 8 bits */ proto_tree_add_item( rtcp_tree, hf_rtcp_pt, tvb, offset, 1, FALSE ); offset++; /* Packet length in 32 bit words MINUS one, 16 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_length, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; dissect_rtcp_sdes( tvb, offset, rtcp_tree, elem_count ); offset += packet_length - 4; break; case RTCP_BYE: /* Source count, 5 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_sc, tvb, offset, 1, temp_byte ); offset++; /* Packet type, 8 bits */ proto_tree_add_item( rtcp_tree, hf_rtcp_pt, tvb, offset, 1, FALSE ); offset++; /* Packet length in 32 bit words MINUS one, 16 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_length, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; offset = dissect_rtcp_bye( tvb, offset, rtcp_tree, elem_count ); break; case RTCP_APP: /* Subtype, 5 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_subtype, tvb, offset, 1, elem_count ); offset++; /* Packet type, 8 bits */ proto_tree_add_item( rtcp_tree, hf_rtcp_pt, tvb, offset, 1, FALSE ); offset++; /* Packet length in 32 bit words MINUS one, 16 bits */ proto_tree_add_uint( rtcp_tree, hf_rtcp_length, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); offset += 2; offset = dissect_rtcp_app( tvb, offset, rtcp_tree, padding_set, packet_length - 4 ); break; case RTCP_FIR: offset = dissect_rtcp_fir( tvb, offset, rtcp_tree ); break; case RTCP_NACK: offset = dissect_rtcp_nack( tvb, offset, rtcp_tree ); break; default: /* * To prevent endless loops in case of an unknown message type * increase offset. Some time the while will end :-) */ offset++; break; } } /* If the padding bit is set, the last octet of the * packet contains the length of the padding * We only have to check for this at the end of the LAST RTCP message */ if ( padding_set ) { /* If everything went according to plan offset should now point to the * first octet of the padding */ proto_tree_add_item( rtcp_tree, hf_rtcp_padding_data, tvb, offset, tvb_length_remaining( tvb, offset) - 1, FALSE ); offset += tvb_length_remaining( tvb, offset) - 1; proto_tree_add_item( rtcp_tree, hf_rtcp_padding_count, tvb, offset, 1, FALSE ); } } } void proto_register_rtcp(void) { static hf_register_info hf[] = { { &hf_rtcp_version, { "Version", "rtcp.version", FT_UINT8, BASE_DEC, VALS(rtcp_version_vals), 0xC0, "", HFILL } }, { &hf_rtcp_padding, { "Padding", "rtcp.padding", FT_BOOLEAN, 8, NULL, 0x20, "", HFILL } }, { &hf_rtcp_rc, { "Reception report count", "rtcp.rc", FT_UINT8, BASE_DEC, NULL, 0x1F, "", HFILL } }, { &hf_rtcp_sc, { "Source count", "rtcp.sc", FT_UINT8, BASE_DEC, NULL, 0x1F, "", HFILL } }, { &hf_rtcp_pt, { "Packet type", "rtcp.pt", FT_UINT8, BASE_DEC, VALS( rtcp_packet_type_vals ), 0x0, "", HFILL } }, { &hf_rtcp_length, { "Length", "rtcp.length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_sender, { "Sender SSRC", "rtcp.senderssrc", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ntp, { "NTP timestamp", "rtcp.timestamp.ntp", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_rtcp_rtp_timestamp, { "RTP timestamp", "rtcp.timestamp.rtp", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_sender_pkt_cnt, { "Sender's packet count", "rtcp.sender.packetcount", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_sender_oct_cnt, { "Sender's octet count", "rtcp.sender.octetcount", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_source, { "Identifier", "rtcp.ssrc.identifier", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_fraction, { "Fraction lost", "rtcp.ssrc.fraction", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_cum_nr, { "Cumulative number of packets lost", "rtcp.ssrc.cum_nr", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_ext_high_seq, { "Extended highest sequence number received", "rtcp.ssrc.ext_high", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_high_seq, { "Highest sequence number received", "rtcp.ssrc.high_seq", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_high_cycles, { "Sequence number cycles count", "rtcp.ssrc.high_cycles", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_jitter, { "Interarrival jitter", "rtcp.ssrc.jitter", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_lsr, { "Last SR timestamp", "rtcp.ssrc.lsr", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_dlsr, { "Delay since last SR timestamp", "rtcp.ssrc.dlsr", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_csrc, { "SSRC / CSRC identifier", "rtcp.sdes.ssrc_csrc", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_type, { "Type", "rtcp.sdes.type", FT_UINT8, BASE_DEC, VALS( rtcp_sdes_type_vals ), 0x0, "", HFILL } }, { &hf_rtcp_ssrc_length, { "Length", "rtcp.sdes.length", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_text, { "Text", "rtcp.sdes.text", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_prefix_len, { "Prefix length", "rtcp.sdes.prefix.length", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_ssrc_prefix_string, { "Prefix string", "rtcp.sdes.prefix.string", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_rtcp_subtype, { "Subtype", "rtcp.app.subtype", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_name_ascii, { "Name (ASCII)", "rtcp.app.name", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_rtcp_app_data, { "Application specific data", "rtcp.app.data", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL } }, { &hf_rtcp_fsn, { "First sequence number", "rtcp.nack.fsn", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_blp, { "Bitmask of following lost packets", "rtcp.nack.blp", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_padding_count, { "Padding count", "rtcp.padding.count", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rtcp_padding_data, { "Padding data", "rtcp.padding.data", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL } }, }; static gint *ett[] = { &ett_rtcp, &ett_ssrc, &ett_ssrc_item, &ett_ssrc_ext_high, &ett_sdes, &ett_sdes_item, }; proto_rtcp = proto_register_protocol("Real-time Transport Control Protocol", "RTCP", "rtcp"); proto_register_field_array(proto_rtcp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); register_dissector("rtcp", dissect_rtcp, proto_rtcp); #if 0 register_init_routine( &rtcp_init ); #endif } void proto_reg_handoff_rtcp(void) { dissector_handle_t rtcp_handle; /* * Register this dissector as one that can be selected by a * UDP port number. */ rtcp_handle = find_dissector("rtcp"); dissector_add_handle("udp.port", rtcp_handle); }