/**************************************************************************** ** File: rtcp.c ** ** Author: Mike Borella ** ** Comments: Dump RTCP header information. ** ** $Id: rtcp.c,v 1.3 2001/11/02 23:59:29 mborella Exp $ ** ** 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 Library 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. ** *****************************************************************************/ #include "rtcp.h" /* * Static part of RTCP header * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |V=2|P| RC | PT | length | header * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ typedef struct rtcp_header { #if defined(WORDS_BIGENDIAN) u_int8_t version:2, padding:1, rc_sc:5; #else u_int8_t rc_sc:5, padding:1, version:2; #endif u_int8_t packet_type; u_int16_t length; } rtcp_header_t; /* * RTCP SR packet type sender info portion * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | SSRC of sender | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * | NTP timestamp, most significant word | sender * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ info * | NTP timestamp, least significant word | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RTP timestamp | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | sender's packet count | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | sender's octet count | * +---------------------------------------------------------------+ */ typedef struct rtcp_sr_senderinfo { u_int32_t sender_ssrc; u_int32_t timestamp_MSW; u_int32_t timestamp_LSW; u_int32_t timestamp_RTP; u_int32_t sender_pkt_cnt; u_int32_t sender_octet_cnt; } rtcp_sr_senderinfo_t; /* * RTCP SR report block * * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * | SSRC_1 (SSRC of first source) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | fraction lost | cumulative number of packets lost | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | extended highest sequence number received | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | interarrival jitter | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | last SR (LSR) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | delay since last SR (DLSR) | * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ */ typedef struct rtcp_sr_reportblock { u_int32_t ssrc; u_int8_t frac_lost; u_int8_t packets_lost[3]; u_int32_t ext_seqno_recvd; u_int32_t jitter; u_int32_t lsr; u_int32_t delay_since_lsr; } rtcp_sr_reportblock_t; /* * RTCP packet type definitions */ #define RTCP_PACKETTYPE_SR 200 #define RTCP_PACKETTYPE_RR 201 #define RTCP_PACKETTYPE_SDES 202 #define RTCP_PACKETTYPE_BYE 203 #define RTCP_PACKETTYPE_APP 204 /* * RTCP payload type map */ strmap_t rtcp_packettype_map[] = { { RTCP_PACKETTYPE_SR, "sender report" }, { RTCP_PACKETTYPE_RR, "receiver report" }, { RTCP_PACKETTYPE_SDES, "source description" }, { RTCP_PACKETTYPE_BYE, "bye" }, { RTCP_PACKETTYPE_APP, "application" }, { 0, ""} }; extern struct arg_t * my_args; /*---------------------------------------------------------------------------- ** ** dump_rtcp_sr() ** ** Parse RTCP sender report fields ** **---------------------------------------------------------------------------- */ void dump_rtcp_sr(packet_t * pkt, int count) { rtcp_sr_senderinfo_t senderinfo; rtcp_sr_reportblock_t reportblock; int reports_seen; /* Get the sender info */ if (get_packet_bytes((u_int8_t *) &senderinfo, pkt, sizeof(senderinfo)) == 0) return; /* Conversions */ senderinfo.sender_ssrc = ntohl(senderinfo.sender_ssrc); senderinfo.timestamp_MSW = ntohl(senderinfo.timestamp_MSW); senderinfo.timestamp_LSW = ntohl(senderinfo.timestamp_LSW); senderinfo.timestamp_RTP = ntohl(senderinfo.timestamp_RTP); senderinfo.sender_pkt_cnt = ntohl(senderinfo.sender_pkt_cnt); senderinfo.sender_octet_cnt = ntohl(senderinfo.sender_octet_cnt); /* Display */ if (my_args->m) { display_minimal((u_int8_t *) &senderinfo.sender_ssrc, 4, DISP_DEC); display_minimal_string(" "); } else { display("Sender SSRC", (u_int8_t *) &senderinfo.sender_ssrc, 4, DISP_DEC); display("Timestamp MSW", (u_int8_t *) &senderinfo.timestamp_MSW, 4, DISP_DEC); display("Timestamp LSW", (u_int8_t *) &senderinfo.timestamp_LSW, 4, DISP_DEC); display("RTP timestamp", (u_int8_t *) &senderinfo.timestamp_RTP, 4, DISP_DEC); display("Sender packet count", (u_int8_t *) &senderinfo.sender_pkt_cnt, 4, DISP_DEC); display("Sender octet count", (u_int8_t *) &senderinfo.sender_octet_cnt, 4, DISP_DEC); } /* Loop over report blocks */ reports_seen = 0; while(reports_seen < count) { /* Get the report block */ if (get_packet_bytes((u_int8_t *) &reportblock, pkt, sizeof(reportblock)) == 0) break; /* Conversions */ reportblock.ssrc = ntohl(reportblock.ssrc); reportblock.ext_seqno_recvd = ntohl(reportblock.ext_seqno_recvd); reportblock.jitter = ntohl(reportblock.jitter); reportblock.lsr = ntohl(reportblock.lsr); reportblock.delay_since_lsr = ntohl(reportblock.delay_since_lsr); /* Display */ if (my_args->m) { display_minimal((u_int8_t *) &reportblock.ssrc, 4, DISP_DEC); display_minimal_string(" "); } else { display("SSRC", (u_int8_t *) &reportblock.ssrc, 4, DISP_DEC); display(" Fraction lost", (u_int8_t *) &reportblock.frac_lost, 1, DISP_DEC); display(" Packets lost", (u_int8_t *) &reportblock.packets_lost, 3, DISP_DEC); display(" Highest seqno received", (u_int8_t *) &reportblock.ext_seqno_recvd, 4, DISP_DEC); display(" Jitter", (u_int8_t *) &reportblock.jitter, 4, DISP_DEC); display(" Last SR", (u_int8_t *) &reportblock.lsr, 4, DISP_DEC); display(" Delay since last SR", (u_int8_t *) &reportblock.delay_since_lsr, 4, DISP_DEC); } reports_seen ++; } } /*---------------------------------------------------------------------------- ** ** dump_rtcp_rr() ** ** Parse RTCP receiver report fields ** **---------------------------------------------------------------------------- */ void dump_rtcp_rr(packet_t * pkt, int count) { rtcp_sr_reportblock_t reportblock; int reports_seen; u_int32_t ssrc; /* Get the SSRC */ if (get_packet_bytes((u_int8_t *) &ssrc, pkt, 4) == 0) return; /* Conversions */ ssrc = ntohl(ssrc); /* Display */ if (my_args->m) { display_minimal((u_int8_t *) &ssrc, 4, DISP_DEC); display_minimal_string(" "); } else display("SSRC", (u_int8_t *) &ssrc, 4, DISP_DEC); /* Loop over report blocks */ reports_seen = 0; while(reports_seen < count) { /* Get the report block */ if (get_packet_bytes((u_int8_t *) &reportblock, pkt, sizeof(reportblock)) == 0) break; /* Conversions */ reportblock.ssrc = ntohl(reportblock.ssrc); reportblock.ext_seqno_recvd = ntohl(reportblock.ext_seqno_recvd); reportblock.jitter = ntohl(reportblock.jitter); reportblock.lsr = ntohl(reportblock.lsr); reportblock.delay_since_lsr = ntohl(reportblock.delay_since_lsr); /* Display */ if (my_args->m) { display_minimal((u_int8_t *) &reportblock.ssrc, 4, DISP_DEC); display_minimal_string(" "); } else { display("SSRC", (u_int8_t *) &reportblock.ssrc, 4, DISP_DEC); display(" Fraction lost", (u_int8_t *) &reportblock.frac_lost, 1, DISP_DEC); display(" Packets lost", (u_int8_t *) &reportblock.packets_lost, 3, DISP_DEC); display(" Highest seqno received", (u_int8_t *) &reportblock.ext_seqno_recvd, 4, DISP_DEC); display(" Jitter", (u_int8_t *) &reportblock.jitter, 4, DISP_DEC); display(" Last SR", (u_int8_t *) &reportblock.lsr, 4, DISP_DEC); display(" Delay since last SR", (u_int8_t *) &reportblock.delay_since_lsr, 4, DISP_DEC); } reports_seen ++; } } /*---------------------------------------------------------------------------- ** ** dump_rtcp_sdes() ** ** Parse RTCP source description fields ** **---------------------------------------------------------------------------- */ void dump_rtcp_sdes(packet_t * pkt, int count) { u_int32_t ssrc; u_int8_t type; u_int8_t length; u_int8_t * string; int chunks_read; int pad_len; chunks_read = 0; while(chunks_read < count) { /* Get the ssrc, type and length */ if (get_packet_bytes((u_int8_t *) &ssrc, pkt, 4) == 0) break; /* Conversions */ ssrc = ntohl(ssrc); /* Display */ if (my_args->m) { display_minimal((u_int8_t *) &ssrc, 4, DISP_DEC); display_minimal_string(" "); } else display("SSRC/CSRC", (u_int8_t *) &ssrc, 4, DISP_DEC); /* Loop through items */ while (1) { u_int8_t byte; if (get_packet_bytes((u_int8_t *) &type, pkt, 1) == 0) break; if (get_packet_bytes((u_int8_t *) &length, pkt, 1) == 0) break; /* Allocate memory for the string then get it */ string = my_malloc(length+1); if (get_packet_bytes(string, pkt, length) == 0) break; string[length] = '\0'; /* Display */ if (my_args->m) { display_minimal_string(string); display_minimal_string(" "); } else { display(" Type", (u_int8_t *) &type, 1, DISP_DEC); display(" Length", (u_int8_t *) &length, 1, DISP_DEC); display_string(" SDES", string); } /* Free string memory */ my_free(string); /* Look for a null terminator */ if (look_packet_bytes((u_int8_t *) &byte, pkt, 1) == 0) break; if (byte == 0) break; } /* Figure out the pad and skip by it */ pad_len = 4 - (length+2) % 4; if (skip_packet_bytes(pkt, pad_len) == 0) break; chunks_read ++; } } /*---------------------------------------------------------------------------- ** ** dump_rtcp() ** ** Parse RTCP packet and dump fields ** **---------------------------------------------------------------------------- */ void dump_rtcp(packet_t * pkt) { rtcp_header_t rtcp; u_int8_t packet_type; u_int8_t padding; u_int8_t version; u_int8_t count; u_int16_t bytes_remaining; /* Set the layer */ set_layer(LAYER_APPLICATION); while(1) { /* Get the fixed RTCP header */ if (get_packet_bytes((u_int8_t *) &rtcp, pkt, sizeof(rtcp)) == 0) break; /* Conversions */ packet_type = rtcp.packet_type; padding = rtcp.padding; version = rtcp.version; count = rtcp.rc_sc; rtcp.length = ntohs(rtcp.length); /* Set the number of bytes remaining */ bytes_remaining = 4 * rtcp.length; /* Display */ if (my_args->m) { display_minimal_string("| RTCPv"); display_minimal((u_int8_t *) &version, 1, DISP_DEC); display_minimal_string(" "); display_minimal_string(map2str(rtcp_packettype_map, packet_type)); display_minimal_string(" "); } else { display_header_banner("RTCP Header"); display("Version", (u_int8_t *) &version, 1, DISP_DEC); display("Padding", (u_int8_t *) &padding, 1, DISP_DEC); display("Report/source count", (u_int8_t *) &count, 1, DISP_DEC); display_strmap("Packet type", packet_type, rtcp_packettype_map); display("Length", (u_int8_t *) &rtcp.length, 2, DISP_DEC); } switch(packet_type) { case RTCP_PACKETTYPE_SR: dump_rtcp_sr(pkt, count); break; case RTCP_PACKETTYPE_RR: dump_rtcp_rr(pkt, count); break; case RTCP_PACKETTYPE_SDES: dump_rtcp_sdes(pkt, count); break; default: break; } } /* Dump the hex buffer */ hexbuffer_flush(); }