diff options
Diffstat (limited to 'packet-rtp.c')
-rw-r--r-- | packet-rtp.c | 708 |
1 files changed, 555 insertions, 153 deletions
diff --git a/packet-rtp.c b/packet-rtp.c index 88ee2a15bc..6f6008b6ac 100644 --- a/packet-rtp.c +++ b/packet-rtp.c @@ -1,9 +1,10 @@ /* packet-rtp.c - * Routines for RTP packet disassembly * - * Jason Lango <jal@netapp.com> - * - * $Id: packet-rtp.c,v 1.5 2000/08/13 14:08:43 deniel Exp $ + * Routines for RTP dissection + * RTP = Real time Transport Protocol + * + * Copyright 2000, Philips Electronics N.V. + * Written by Andreas Sikkema <andreas.sikkema@philips.com> * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -23,195 +24,596 @@ * 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 RTP protocol according to Annex A + * of ITU-T Recommendation H.225.0 (02/98) or RFC 1889 * - * + * RTP traffic is handled by an even UDP portnumber. This can be any + * port number, but there is a registered port available, port 5004 + * See Annex B of ITU-T Recommendation H.225.0, section B.7 */ -#include "config.h" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glib.h> +#include "packet.h" #ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> +# include <sys/types.h> #endif #ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> +# include <netinet/in.h> #endif +#include <stdio.h> #include <string.h> -#include <ctype.h> -#include <stddef.h> -#include <glib.h> -#include "packet.h" #include "packet-rtp.h" +#include "packet-h261.h" +#include "conversation.h" + +/* RTP header fields */ +static int proto_rtp = -1; +static int hf_rtp_version = -1; +static int hf_rtp_padding = -1; +static int hf_rtp_extension = -1; +static int hf_rtp_csrc_count = -1; +static int hf_rtp_marker = -1; +static int hf_rtp_payload_type = -1; +static int hf_rtp_seq_nr = -1; +static int hf_rtp_timestamp = -1; +static int hf_rtp_ssrc = -1; +static int hf_rtp_csrc_item = -1; +static int hf_rtp_data = -1; +static int hf_rtp_padding_data = -1; +static int hf_rtp_padding_count= -1; + +/* RTP header extension fields */ +static int hf_rtp_prof_define = -1; +static int hf_rtp_length = -1; +static int hf_rtp_hdr_ext = -1; + +/* RTP fields defining a sub tree */ +static gint ett_rtp = -1; +static gint ett_csrc_list = -1; +static gint ett_hdr_ext = -1; + +/* + * Fields in the first octet of the RTP header. + */ -static int proto_rtp = -1; +/* Version is the first 2 bits of the first octet*/ +#define RTP_VERSION(octet) ((octet) >> 6) -static gint ett_rtp = -1; +/* Padding is the third bit; No need to shift, because true is any value + other than 0! */ +#define RTP_PADDING(octet) ((octet) & 0x20) -#define _RTP_FLAG_BITS(hdr, s, n) \ - ((u_int)(((hdr)->rtp_flag_bits >> (8 - (s) - (n))) & ((1 << (n)) - 1))) -#define RTP_VERSION(hdr) _RTP_FLAG_BITS(hdr, 0, 2) -#define RTP_PADDING(hdr) _RTP_FLAG_BITS(hdr, 2, 1) -#define RTP_EXTENSION(hdr) _RTP_FLAG_BITS(hdr, 3, 1) -#define RTP_CSRC_COUNT(hdr) _RTP_FLAG_BITS(hdr, 4, 4) +/* Extension bit is the fourth bit */ +#define RTP_EXTENSION(octet) ((octet) & 0x10) -#define RTP_MARKER(hdr) ((u_int)((hdr)->rtp_type_bits >> 7)) -#define RTP_PAYLOAD_TYPE(hdr) ((u_int)((hdr)->rtp_type_bits & 0x7F)) +/* CSRC count is the last four bits */ +#define RTP_CSRC_COUNT(octet) ((octet) & 0xF) -typedef struct rtp_hdr { - guint8 rtp_flag_bits; - guint8 rtp_type_bits; - guint16 rtp_seq; - guint32 rtp_timestamp; - guint32 rtp_ssrc; -} rtp_hdr_t; +static const value_string rtp_version_vals[] = +{ + { 0, "Old VAT Version" }, + { 1, "First Draft Version" }, + { 2, "RFC 1889 Version" }, + { 0, NULL }, +}; + +/* + * Fields in the second octet of the RTP header. + */ -typedef struct rtp_hdr_ext { - guint16 rtp_ext_app; /* defined by RTP profile */ - guint16 rtp_ext_length; /* length of extension data in 32 bit words */ -} rtp_hdr_ext_t; +/* Marker is the first bit of the second octet */ +#define RTP_MARKER(octet) ((octet) & 0x80) -void -dissect_rtp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) +/* Payload type is the last 7 bits */ +#define RTP_PAYLOAD_TYPE(octet) ((octet) & 0x7F) + +/* + * RTP Payload types + * Table B.2 / H.225.0 + */ +#define PT_PCMU 0 +#define PT_PCMA 8 +#define PT_G722 9 +#define PT_G723 4 +#define PT_G728 15 +#define PT_G729 18 +#define PT_H261 31 +#define PT_H263 34 + +static const value_string rtp_payload_type_vals[] = +{ + { PT_PCMU, "ITU-T G.711 PCMU" }, + { PT_PCMA, "ITU-T G.711 PCMA" }, + { PT_G722, "ITU-T G.722" }, + { PT_G723, "ITU-T G.723" }, + { PT_G728, "ITU-T G.728" }, + { PT_G729, "ITU-T G.729" }, + { PT_H261, "ITU-T H.261" }, + { PT_H263, "ITU-T H.263" }, + { 0, NULL }, +}; + +static address fake_addr; +static int heur_init = FALSE; + +static const char rtp_proto[] = "RTP"; + +void rtp_add_address( const unsigned char* ip_addr, int prt ) { - proto_tree *rtp_tree; - proto_item *ti; - const u_char *data, *dataend; - rtp_hdr_t hdr; - int end_offset; - int ii; - guint32 *csrc_ptr; - rtp_hdr_ext_t ext; - - OLD_CHECK_DISPLAY_AS_DATA(proto_rtp, pd, offset, fd, tree); - - data = &pd[offset]; - dataend = data + END_OF_FRAME; - end_offset = offset + END_OF_FRAME; - - memcpy(&hdr, data, END_OF_FRAME < sizeof(rtp_hdr_t) ? - END_OF_FRAME : sizeof(rtp_hdr_t)); - hdr.rtp_seq = ntohs(hdr.rtp_seq); - hdr.rtp_timestamp = ntohl(hdr.rtp_timestamp); - hdr.rtp_ssrc = ntohl(hdr.rtp_ssrc); - - if (check_col(fd, COL_PROTOCOL)) - col_add_str(fd, COL_PROTOCOL, "RTP"); - if (check_col(fd, COL_INFO)) { - col_add_fstr(fd, COL_INFO, "SSRC=%lu, Seq=%u, Time=%lu%s", - (u_long) hdr.rtp_ssrc, - (u_int) hdr.rtp_seq, - (u_long) hdr.rtp_timestamp, - RTP_MARKER(&hdr) ? ", Mark" : ""); + address src_addr; + conversation_t* pconv = ( conversation_t* ) NULL; + + src_addr.type = AT_IPv4; + src_addr.len = 4; + src_addr.data = ip_addr; + + /* + * The first time the function is called let the tcp dissector + * know that we're interested in traffic + */ + if ( ! heur_init ) { + heur_dissector_add( "udp", dissect_rtp_heur ); + heur_init = TRUE; } - rtp_tree = NULL; + /* + * Check if the ip address an dport combination is not + * already registered + */ + pconv = find_conversation( &src_addr, &fake_addr, PT_UDP, prt, 0 ); + + /* + * If not, add + */ + if ( ! pconv ) { + conversation_new( &src_addr, &fake_addr, PT_UDP, (guint32) prt, (guint32) 0, ( void * ) rtp_proto ); + } + +} + +#if 0 +static void rtp_init( void ) +{ + unsigned char* tmp_data; + int i; + + /* Create a fake adddress... */ + fake_addr.type = AT_IPv4; + fake_addr.len = 4; - if (tree) { - ti = proto_tree_add_item(tree, proto_rtp, NullTVB, offset, END_OF_FRAME, - FALSE); - rtp_tree = proto_item_add_subtree(ti, ett_rtp); + tmp_data = malloc( fake_addr.len ); + for ( i = 0; i < fake_addr.len; i++) { + tmp_data[i] = 0; } + fake_addr.data = tmp_data; +} +#endif - if (!rtp_tree) - return; - - if (offset >= end_offset) - goto bad_len; - proto_tree_add_text(rtp_tree, NullTVB, offset, 1, "Version: %u (%s)", - RTP_VERSION(&hdr), - RTP_VERSION(&hdr) == 3 ? "New Unknown Version" : - RTP_VERSION(&hdr) == 2 ? "RFC 1889 Version" : - RTP_VERSION(&hdr) == 1 ? "First Draft Version" : - "Old Vat Version"); - proto_tree_add_text(rtp_tree, NullTVB, offset, 1, "Padding: %u", - RTP_PADDING(&hdr)); - proto_tree_add_text(rtp_tree, NullTVB, offset, 1, "Extension: %u", - RTP_EXTENSION(&hdr)); - proto_tree_add_text(rtp_tree, NullTVB, offset, 1, "CSRC Count: %u", - RTP_CSRC_COUNT(&hdr)); - offset++; - - if (offset >= end_offset) - goto bad_len; - proto_tree_add_text(rtp_tree, NullTVB, offset, 1, "Marker: %u", - RTP_MARKER(&hdr)); - proto_tree_add_text(rtp_tree, NullTVB, offset, 1, "Payload Type: %u", - RTP_PAYLOAD_TYPE(&hdr)); - offset++; - - if (offset >= end_offset) - goto bad_len; - proto_tree_add_text(rtp_tree, NullTVB, offset, 2, "Seq: %u", - (u_int) hdr.rtp_seq); - offset += 2; - - if (offset >= end_offset) - goto bad_len; - proto_tree_add_text(rtp_tree, NullTVB, offset, 4, "Timestamp: %lu", - (u_long) hdr.rtp_timestamp); - offset += 4; - - if (offset >= end_offset) - goto bad_len; - proto_tree_add_text(rtp_tree, NullTVB, offset, 4, "SSRC: %lu", - (u_long) hdr.rtp_ssrc); - offset += 4; - - csrc_ptr = (guint32*) (data + sizeof(rtp_hdr_t)); - for (ii = 0; ii < RTP_CSRC_COUNT(&hdr); ii++) { - guint32 csrc; - if (offset >= end_offset) - goto bad_len; - csrc = pntohl(csrc_ptr); - proto_tree_add_text(rtp_tree, NullTVB, offset, 4, "CSRC %d: %lu", - ii + 1, (u_long) csrc); - offset += 4; - csrc_ptr++; +gboolean +dissect_rtp_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) +{ + /* This is a heuristic dissector, which means we get all the tcp traffic + * not send to a known dissector! + * So we first check if the frame is really meant for us. + */ + conversation_t* pconv; + if ( ( pconv = find_conversation( &pi.src, &fake_addr, pi.ptype, pi.srcport, 0 ) ) == NULL ) { + /* + * The source ip:port combination was not what we were looking for, check the destination + */ + if ( ( pconv = find_conversation( &pi.dst, &fake_addr, pi.ptype, pi.destport, 0 ) ) == NULL ) { + return FALSE; + } } - if (RTP_EXTENSION(&hdr)) { - memcpy(&ext, data + sizeof(rtp_hdr_t), - END_OF_FRAME < sizeof(rtp_hdr_ext_t) ? - END_OF_FRAME : sizeof(rtp_hdr_ext_t)); - ext.rtp_ext_app = ntohs(ext.rtp_ext_app); - ext.rtp_ext_length = ntohs(ext.rtp_ext_length); + /* + * An RTP conversation always contains data + */ + if ( pconv->data == NULL ) + return FALSE; - proto_tree_add_text(rtp_tree, NullTVB, offset, 2, - "Extension-defined: %x", (u_int) ext.rtp_ext_app); - offset += 2; - proto_tree_add_text(rtp_tree, NullTVB, offset, 2, - "Extension length: %u", (u_int) ext.rtp_ext_length); - offset += 2; - proto_tree_add_text(rtp_tree, NullTVB, offset, 4 * ext.rtp_ext_length, - "Extension Data (%d bytes)", - (int) 4 * ext.rtp_ext_length); - offset += 4 * ext.rtp_ext_length; + /* + * An RTP conversation data always contains "RTP" + */ + if ( strcmp( pconv->data, rtp_proto ) != 0 ) + return FALSE; + + dissect_rtp( tvb, pinfo, tree ); + + return TRUE; +} + +void +dissect_rtp_data( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *rtp_tree, int offset, unsigned int data_len, unsigned int payload_type ) +{ + tvbuff_t *newtvb; + + switch( payload_type ) { + case PT_H261: + /* + * What does reported length DO? + */ + newtvb = tvb_new_subset( tvb, offset, data_len, -1 ); + dissect_h261(newtvb, pinfo, tree); + break; + default: + proto_tree_add_bytes( rtp_tree, hf_rtp_data, tvb, offset, data_len, tvb_get_ptr( tvb, offset, data_len ) ); + break; } +} - proto_tree_add_text(rtp_tree, NullTVB, offset, END_OF_FRAME, - "Data (%d bytes)", END_OF_FRAME); +void +dissect_rtp( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) +{ + proto_item *ti = NULL; + proto_tree *rtp_tree = NULL; + proto_tree *rtp_csrc_tree = NULL; + guint8 octet; + unsigned int version; + gboolean padding_set; + gboolean extension_set; + unsigned int csrc_count; + gboolean marker_set; + unsigned int payload_type; + unsigned int i = 0; + unsigned int hdr_extension= 0; + unsigned int padding_count= 0; + unsigned int offset = 0; + guint16 seq_num; + guint32 timestamp; + guint32 sync_src; + guint32 csrc_item; + + pinfo->current_proto = "RTP"; + + /* Get the fields in the first octet */ + octet = tvb_get_guint8( tvb, offset ); + version = RTP_VERSION( octet ); + padding_set = RTP_PADDING( octet ); + extension_set = RTP_EXTENSION( octet ); + csrc_count = RTP_CSRC_COUNT( octet ); + + /* Get the fields in the second octet */ + octet = tvb_get_guint8( tvb, offset + 1 ); + marker_set = RTP_MARKER( octet ); + payload_type = RTP_PAYLOAD_TYPE( octet ); + + /* Get the subsequent fields */ + seq_num = tvb_get_ntohs( tvb, offset + 2 ); + timestamp = tvb_get_ntohl( tvb, offset + 4 ); + sync_src = tvb_get_ntohl( tvb, offset + 8 ); + + if ( check_col( pinfo->fd, COL_PROTOCOL ) ) { + col_add_str( pinfo->fd, COL_PROTOCOL, "RTP" ); + } + + if ( check_col( pinfo->fd, COL_INFO) ) { + col_add_fstr( pinfo->fd, COL_INFO, + "Payload type=%s, SSRC=%u, Seq=%u, Time=%u%s", + val_to_str( payload_type, rtp_payload_type_vals, + "Unknown (%u)" ), + sync_src, + seq_num, + timestamp, + marker_set ? ", Mark" : ""); + } + + if ( tree ) { + ti = proto_tree_add_item( tree, proto_rtp, tvb, offset, tvb_length_remaining( tvb, offset ), FALSE ); + rtp_tree = proto_item_add_subtree( ti, ett_rtp ); + + proto_tree_add_uint( rtp_tree, hf_rtp_version, tvb, + offset, 1, version ); + proto_tree_add_boolean( rtp_tree, hf_rtp_padding, tvb, + offset, 1, padding_set ); + proto_tree_add_boolean( rtp_tree, hf_rtp_extension, tvb, + offset, 1, extension_set ); + proto_tree_add_uint( rtp_tree, hf_rtp_csrc_count, tvb, + offset, 1, csrc_count ); + offset++; + + proto_tree_add_boolean( rtp_tree, hf_rtp_marker, tvb, offset, + 1, marker_set ); + proto_tree_add_uint( rtp_tree, hf_rtp_payload_type, tvb, + offset, 1, payload_type ); + offset++; + + /* Sequence number 16 bits (2 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_seq_nr, tvb, offset, 2, seq_num ); + offset += 2; + + /* Timestamp 32 bits (4 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_timestamp, tvb, offset, 4, timestamp ); + offset += 4; - return; + /* Synchronization source identifier 32 bits (4 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_ssrc, tvb, offset, 4, sync_src ); + offset += 4; -bad_len: - proto_tree_add_text(rtp_tree, NullTVB, end_offset, 0, - "Unexpected end of packet"); + /* CSRC list*/ + if ( csrc_count > 0 ) { + ti = proto_tree_add_text(rtp_tree, tvb, offset, csrc_count * 4, "Contributing Source identifiers"); + rtp_csrc_tree = proto_item_add_subtree( ti, ett_csrc_list ); + for (i = 0; i < csrc_count; i++ ) { + csrc_item = tvb_get_ntohl( tvb, offset ); + proto_tree_add_uint_format( rtp_csrc_tree, + hf_rtp_csrc_item, tvb, offset, 4, + csrc_item, + "CSRC item %d: %u", + i, csrc_item ); + offset += 4; + } + } + + /* Optional RTP header extension */ + if ( extension_set ) { + /* Defined by profile field is 16 bits (2 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_prof_define, tvb, offset, 2, tvb_get_ntohs( tvb, offset ) ); + offset += 2; + + hdr_extension = tvb_get_ntohs( tvb, offset ); + proto_tree_add_uint( rtp_tree, hf_rtp_length, tvb, + offset, 2, hdr_extension); + if ( hdr_extension > 0 ) { + ti = proto_tree_add_text(rtp_tree, tvb, offset, csrc_count * 4, "Header extensions"); + /* I'm re-using the old tree variable here + from the CSRC list!*/ + rtp_csrc_tree = proto_item_add_subtree( ti, + ett_hdr_ext ); + for (i = 0; i < hdr_extension; i++ ) { + proto_tree_add_uint( rtp_csrc_tree, hf_rtp_hdr_ext, tvb, offset, 4, tvb_get_ntohl( tvb, offset ) ); + offset += 4; + } + } + } + /* Find the padding + * The padding count is found in the LAST octet of the packet + * This contains the number of octets that can be ignored at + * the end of the packet + */ + if ( padding_set ) { + padding_count = tvb_get_guint8( tvb, tvb_length( tvb ) - 1 ); + if ( padding_count > 0 ) { + dissect_rtp_data( tvb, pinfo, tree, rtp_tree, offset, tvb_length( tvb ) - padding_count, payload_type ); + offset = tvb_length( tvb ) - padding_count; + proto_tree_add_item( rtp_tree, hf_rtp_padding_data, tvb, offset, padding_count - 1, FALSE ); + offset += padding_count - 1; + proto_tree_add_item( rtp_tree, hf_rtp_padding_count, tvb, offset, 1, FALSE ); + } + else { + proto_tree_add_item( rtp_tree, hf_rtp_padding_count, tvb, tvb_length( tvb ) - 1, 1, FALSE ); + } + } + else { + dissect_rtp_data( tvb, pinfo, tree, rtp_tree, offset, tvb_length_remaining( tvb, offset ) - padding_count, payload_type ); + } + } } void proto_register_rtp(void) { -/* static hf_register_info hf[] = { - { &variable, - { "Name", "rtp.abbreviation", TYPE, VALS_POINTER }}, - };*/ - static gint *ett[] = { + static hf_register_info hf[] = + { + { + &hf_rtp_version, + { + "Version", + "rtp.version", + FT_UINT8, + BASE_DEC, + VALS(rtp_version_vals), + 0x0, + "" + } + }, + { + &hf_rtp_padding, + { + "Padding", + "rtp.padding", + FT_BOOLEAN, + BASE_NONE, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_extension, + { + "Extension", + "rtp.ext", + FT_BOOLEAN, + BASE_NONE, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_csrc_count, + { + "Contributing source identifiers count", + "rtp.cc", + FT_UINT8, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_marker, + { + "Marker", + "rtp.marker", + FT_BOOLEAN, + BASE_NONE, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_payload_type, + { + "Payload type", + "rtp.p_type", + FT_UINT8, + BASE_DEC, + VALS(rtp_payload_type_vals), + 0x0, + "" + } + }, + { + &hf_rtp_seq_nr, + { + "Sequence number", + "rtp.seq", + FT_UINT16, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_timestamp, + { + "Timestamp", + "rtp.timestamp", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_ssrc, + { + "Synchronization Source identifier", + "rtp.ssrc", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_prof_define, + { + "Defined by profile", + "rtp.ext.profile", + FT_UINT16, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_length, + { + "Extension length", + "rtp.ext.len", + FT_UINT16, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_csrc_item, + { + "CSRC item", + "rtp.csrc.item", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_hdr_ext, + { + "Header extension", + "rtp.hdr_ext", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_data, + { + "Payload", + "rtp.payload", + FT_BYTES, + BASE_HEX, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_padding_data, + { + "Padding data", + "rtp.padding.data", + FT_BYTES, + BASE_HEX, + NULL, + 0x0, + "" + } + }, + { + &hf_rtp_padding_count, + { + "Padding count", + "rtp.padding.count", + FT_UINT8, + BASE_DEC, + NULL, + 0x0, + "" + } + }, +}; + + static gint *ett[] = + { &ett_rtp, + &ett_csrc_list, + &ett_hdr_ext, }; - proto_rtp = proto_register_protocol("Realtime Transport Protocol", "rtp"); - /* proto_register_field_array(proto_rtp, hf, array_length(hf));*/ + + proto_rtp = proto_register_protocol("Real-Time Transport Protocol", "rtp"); + proto_register_field_array(proto_rtp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); + +#if 0 + register_init_routine( &rtp_init ); +#endif } |