wireshark/packet-rtcp.c

1224 lines
30 KiB
C
Raw Normal View History

/* 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 <h323@ramdyne.nl>
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* 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 <glib.h>
#include <epan/packet.h>
#include <stdio.h>
#include <string.h>
#include "packet-rtcp.h"
#if 0
#include "packet-ntp.h"
#endif
#include <epan/conversation.h>
/* 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 );
Allow a length of -1 to be specified when adding FT_NONE and FT_PROTOCOL items to the protocol tree; it's interpreted as "the rest of the data in the tvbuff". This can be used if 1) the item covers the entire packet or the remaining payload in the packet or 2) the item's length won't be known until it's dissected, and will be then set with "proto_item_set_len()" - if an exception is thrown in the dissection, it means the item ran *past* the end of the tvbuff, so saying it runs to the end of the tvbuff is reasonable. Convert a number of "proto_tree_add_XXX()" calls using "tvb_length_remaining()", values derived from the result of "tvb_length()", or 0 (in the case of items whose length is unknown) to use -1 instead (using 0 means that if an exception is thrown, selecting the item highlights nothing; using -1 means it highlights all the data for that item that's available). In some places where "tvb_length()" or "tvb_length_remaining()" was used to determine how large a packet is, use "tvb_reported_length()" or "tvb_reported_length_remaining()", instead - the first two calls indicate how much captured data was in the packet, the latter two calls indicate how large the packet actually was (and the fact that using the latter could cause BoundsError exceptions to be thrown is a feature - if such an exception is thrown, the frame really *was* short, and it should be tagged as such). Replace some "proto_tree_add_XXX()" calls with equivalent "proto_tree_add_item()" calls. Fix some indentation. svn path=/trunk/; revision=4578
2002-01-20 22:12:39 +00:00
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;
Allow a length of -1 to be specified when adding FT_NONE and FT_PROTOCOL items to the protocol tree; it's interpreted as "the rest of the data in the tvbuff". This can be used if 1) the item covers the entire packet or the remaining payload in the packet or 2) the item's length won't be known until it's dissected, and will be then set with "proto_item_set_len()" - if an exception is thrown in the dissection, it means the item ran *past* the end of the tvbuff, so saying it runs to the end of the tvbuff is reasonable. Convert a number of "proto_tree_add_XXX()" calls using "tvb_length_remaining()", values derived from the result of "tvb_length()", or 0 (in the case of items whose length is unknown) to use -1 instead (using 0 means that if an exception is thrown, selecting the item highlights nothing; using -1 means it highlights all the data for that item that's available). In some places where "tvb_length()" or "tvb_length_remaining()" was used to determine how large a packet is, use "tvb_reported_length()" or "tvb_reported_length_remaining()", instead - the first two calls indicate how much captured data was in the packet, the latter two calls indicate how large the packet actually was (and the fact that using the latter could cause BoundsError exceptions to be thrown is a feature - if such an exception is thrown, the frame really *was* short, and it should be tagged as such). Replace some "proto_tree_add_XXX()" calls with equivalent "proto_tree_add_item()" calls. Fix some indentation. svn path=/trunk/; revision=4578
2002-01-20 22:12:39 +00:00
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;
/*
Get rid of the lists of conversation dissectors; instead, have a dissector table contain both a hash table, to use to look up port numbers to find a dissector, and a list of all dissectors that *could* be assigned to ports in that hash table, to be used by user interface code. Make the "Decode As" dialog box code use that. Also make it *not* let you choose whether to set the dissector for both the UDP and TCP versions of a port; some protocols run only atop TCP, some run only atop UDP, and even those that can run atop both may have different dissector handles to use over TCP and UDP, so handling a single merged list would be a mess. (If the user is setting the dissector for a TCP port, only those protocols that Ethereal can handle over TCP should be listed; if the user is setting the dissector for a UDP port, only those protocols that Ethereal can handle over TCP should be listed; if the user is setting a dissector for both, only those protocols that Ethereal can handle over *both* TCP *and* UDP should be listed, *and* there needs to be a way to let the "Decode As" code get both the TCP handle *and* the UDP handle and use the right ones. If somebody really wants that, they need to implement all of the above if they want the code to be correct.) Fix the code that handles setting the dissection for the IP protocol number to correctly update the lists of protocols being dissected as TCP and as UDP; the code before this change wasn't updating the single such list to add new protocols. svn path=/trunk/; revision=4311
2001-12-03 08:47:31 +00:00
* Register this dissector as one that can be selected by a
* UDP port number.
*/
rtcp_handle = find_dissector("rtcp");
Get rid of the lists of conversation dissectors; instead, have a dissector table contain both a hash table, to use to look up port numbers to find a dissector, and a list of all dissectors that *could* be assigned to ports in that hash table, to be used by user interface code. Make the "Decode As" dialog box code use that. Also make it *not* let you choose whether to set the dissector for both the UDP and TCP versions of a port; some protocols run only atop TCP, some run only atop UDP, and even those that can run atop both may have different dissector handles to use over TCP and UDP, so handling a single merged list would be a mess. (If the user is setting the dissector for a TCP port, only those protocols that Ethereal can handle over TCP should be listed; if the user is setting the dissector for a UDP port, only those protocols that Ethereal can handle over TCP should be listed; if the user is setting a dissector for both, only those protocols that Ethereal can handle over *both* TCP *and* UDP should be listed, *and* there needs to be a way to let the "Decode As" code get both the TCP handle *and* the UDP handle and use the right ones. If somebody really wants that, they need to implement all of the above if they want the code to be correct.) Fix the code that handles setting the dissection for the IP protocol number to correctly update the lists of protocols being dissected as TCP and as UDP; the code before this change wasn't updating the single such list to add new protocols. svn path=/trunk/; revision=4311
2001-12-03 08:47:31 +00:00
dissector_add_handle("udp.port", rtcp_handle);
}