* By Gerald Combs <gerald@wireshark.org>
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
-#include <glib.h>
-
+#include <stdlib.h>
+#include <string.h>
#include <math.h>
-#include "globals.h"
-#include <string.h>
+#include <glib.h>
+
#include <epan/rtp_pt.h>
#include <epan/addr_resolv.h>
+#include <epan/proto_data.h>
#include <epan/dissectors/packet-rtp.h>
+#include <wsutil/pint.h>
#include "rtp_stream.h"
#include "tap-rtp-common.h"
/****************************************************************************/
/* Type for storing and writing rtpdump information */
typedef struct st_rtpdump_info {
- double rec_time; /**< milliseconds since start of recording */
+ double rec_time; /**< milliseconds since start of recording */
guint16 num_samples; /**< number of bytes in *frame */
- const guint8 *samples; /**< data bytes */
+ const guint8 *samples; /**< data bytes */
} rtpdump_info_t;
/****************************************************************************/
-/* GCompareFunc style comparison function for _rtp_stream_info */
-static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
+/* init rtpstream_info_t structure */
+void rtpstream_info_init(rtpstream_info_t *info)
+{
+ memset(info, 0, sizeof(rtpstream_info_t));
+}
+
+/****************************************************************************/
+/* malloc and init rtpstream_info_t structure */
+rtpstream_info_t *rtpstream_info_malloc_and_init(void)
+{
+ rtpstream_info_t *dest;
+
+ dest = (rtpstream_info_t *)g_malloc(sizeof(rtpstream_info_t));
+ rtpstream_info_init(dest);
+
+ return dest;
+}
+
+/****************************************************************************/
+/* deep copy of rtpstream_info_t */
+void rtpstream_info_copy_deep(rtpstream_info_t *dest, const rtpstream_info_t *src)
+{
+ /* Deep clone of contents */
+ *dest = *src; /* memberwise copy of struct */
+ copy_address(&(dest->id.src_addr), &(src->id.src_addr));
+ copy_address(&(dest->id.dst_addr), &(src->id.dst_addr));
+ dest->all_payload_type_names = g_strdup(src->all_payload_type_names);
+}
+
+/****************************************************************************/
+/* malloc and deep copy rtpstream_info_t structure */
+rtpstream_info_t *rtpstream_info_malloc_and_copy_deep(const rtpstream_info_t *src)
{
- const struct _rtp_stream_info* a = (const struct _rtp_stream_info*)aa;
- const struct _rtp_stream_info* b = (const struct _rtp_stream_info*)bb;
-
- if (a==b)
- return 0;
- if (a==NULL || b==NULL)
- return 1;
- if (addresses_equal(&(a->src_addr), &(b->src_addr))
- && (a->src_port == b->src_port)
- && addresses_equal(&(a->dest_addr), &(b->dest_addr))
- && (a->dest_port == b->dest_port)
- && (a->ssrc == b->ssrc))
- return 0;
- else
- return 1;
+ rtpstream_info_t *dest;
+
+ dest = (rtpstream_info_t *)g_malloc(sizeof(rtpstream_info_t));
+ rtpstream_info_copy_deep(dest, src);
+
+ return dest;
}
+/****************************************************************************/
+/* free rtpstream_info_t referenced values */
+void rtpstream_info_free_data(rtpstream_info_t *info)
+{
+ if (info->all_payload_type_names != NULL) {
+ g_free(info->all_payload_type_names);
+ }
+
+ rtpstream_id_free(&info->id);
+}
+
+/****************************************************************************/
+/* free rtpstream_info_t referenced values and whole structure */
+void rtpstream_info_free_all(rtpstream_info_t *info)
+{
+ rtpstream_info_free_data(info);
+ g_free(info);
+}
+
+/****************************************************************************/
+/* GCompareFunc style comparison function for rtpstream_info_t */
+gint rtpstream_info_cmp(gconstpointer aa, gconstpointer bb)
+{
+ const rtpstream_info_t *a = (const rtpstream_info_t *)aa;
+ const rtpstream_info_t *b = (const rtpstream_info_t *)bb;
+
+ if (a==b)
+ return 0;
+ if (a==NULL || b==NULL)
+ return 1;
+ if (rtpstream_id_equal(&(a->id),&(b->id),RTPSTREAM_ID_EQUAL_SSRC))
+ return 0;
+ else
+ return 1;
+}
+
+/****************************************************************************/
+/* compare the endpoints of two RTP streams */
+gboolean rtpstream_info_is_reverse(const rtpstream_info_t *stream_a, rtpstream_info_t *stream_b)
+{
+ if (stream_a == NULL || stream_b == NULL)
+ return FALSE;
+
+ if ((addresses_equal(&(stream_a->id.src_addr), &(stream_b->id.dst_addr)))
+ && (stream_a->id.src_port == stream_b->id.dst_port)
+ && (addresses_equal(&(stream_a->id.dst_addr), &(stream_b->id.src_addr)))
+ && (stream_a->id.dst_port == stream_b->id.src_port))
+ return TRUE;
+ else
+ return FALSE;
+}
/****************************************************************************/
/* when there is a [re]reading of packet's */
void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
{
- GList* list;
-
- if (tapinfo->mode == TAP_ANALYSE) {
- /* free the data items first */
- list = g_list_first(tapinfo->strinfo_list);
- while (list)
- {
- g_free(list->data);
- list = g_list_next(list);
- }
- g_list_free(tapinfo->strinfo_list);
- tapinfo->strinfo_list = NULL;
- tapinfo->nstreams = 0;
- tapinfo->npackets = 0;
- }
-
- return;
+ GList* list;
+ rtpstream_info_t *stream_info;
+
+ if (tapinfo->mode == TAP_ANALYSE) {
+ /* free the data items first */
+ list = g_list_first(tapinfo->strinfo_list);
+ while (list)
+ {
+ stream_info = (rtpstream_info_t *)(list->data);
+ rtpstream_info_free_data(stream_info);
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(tapinfo->strinfo_list);
+ tapinfo->strinfo_list = NULL;
+ tapinfo->nstreams = 0;
+ tapinfo->npackets = 0;
+ }
+
+ return;
}
void rtpstream_reset_cb(void *arg)
{
- rtpstream_tapinfo_t *ti =(rtpstream_tapinfo_t *)arg;
- if (ti->tap_reset) {
- /* Give listeners a chance to cleanup references. */
- ti->tap_reset(ti);
- }
- rtpstream_reset(ti);
+ rtpstream_tapinfo_t *ti =(rtpstream_tapinfo_t *)arg;
+ if (ti->tap_reset) {
+ /* Give listeners a chance to cleanup references. */
+ ti->tap_reset(ti);
+ }
+ rtpstream_reset(ti);
+}
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+
+/****************************************************************************/
+/* redraw the output */
+static void rtpstream_draw_cb(void *ti_ptr)
+{
+ rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)ti_ptr;
+/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
+ g_signal_emit_by_name(top_level, "signal_rtpstream_update");
+*/
+ if (tapinfo && tapinfo->tap_draw) {
+ /* RTP_STREAM_DEBUG("streams: %d packets: %d", tapinfo->nstreams, tapinfo->npackets); */
+ tapinfo->tap_draw(tapinfo);
+ }
+ return;
+}
+
+
+
+/****************************************************************************/
+void
+remove_tap_listener_rtpstream(rtpstream_tapinfo_t *tapinfo)
+{
+ if (tapinfo && tapinfo->is_registered) {
+ remove_tap_listener(tapinfo);
+ tapinfo->is_registered = FALSE;
+ }
+}
+
+/****************************************************************************/
+void
+register_tap_listener_rtpstream(rtpstream_tapinfo_t *tapinfo, const char *fstring, rtpstream_tap_error_cb tap_error)
+{
+ GString *error_string;
+
+ if (!tapinfo) {
+ return;
+ }
+
+ if (!tapinfo->is_registered) {
+ error_string = register_tap_listener("rtp", tapinfo,
+ fstring, 0, rtpstream_reset_cb, rtpstream_packet_cb,
+ rtpstream_draw_cb, NULL);
+
+ if (error_string != NULL) {
+ if (tap_error) {
+ tap_error(error_string);
+ }
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ tapinfo->is_registered = TRUE;
+ }
}
/*
* the RTP/RTCP header and (optionally) the actual payload.
*/
+static const gchar *PAYLOAD_UNKNOWN_STR = "Unknown";
+
+static void update_payload_names(rtpstream_info_t *stream_info, const struct _rtp_info *rtpinfo)
+{
+ GString *payload_type_names;
+ const gchar *new_payload_type_str;
+
+ /* Ensure that we have non empty payload_type_str */
+ if (rtpinfo->info_payload_type_str != NULL) {
+ new_payload_type_str = rtpinfo->info_payload_type_str;
+ }
+ else {
+ /* String is created from const strings only */
+ new_payload_type_str = val_to_str_ext_const(rtpinfo->info_payload_type,
+ &rtp_payload_type_short_vals_ext,
+ PAYLOAD_UNKNOWN_STR
+ );
+ }
+ stream_info->payload_type_names[rtpinfo->info_payload_type] = new_payload_type_str;
+
+ /* Join all existing payload names to one string */
+ payload_type_names = g_string_sized_new(40); /* Preallocate memory */
+ for(int i=0; i<256; i++) {
+ if (stream_info->payload_type_names[i] != NULL) {
+ if (payload_type_names->len > 0) {
+ g_string_append(payload_type_names, ", ");
+ }
+ g_string_append(payload_type_names, stream_info->payload_type_names[i]);
+ }
+ }
+ if (stream_info->all_payload_type_names != NULL) {
+ g_free(stream_info->all_payload_type_names);
+ }
+ stream_info->all_payload_type_names = payload_type_names->str;
+ g_string_free(payload_type_names, FALSE);
+}
+
#define RTPFILE_VERSION "1.0"
/*
* The header consists of an identifying string, followed
* by a binary structure.
*/
-void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
+void rtp_write_header(rtpstream_info_t *strinfo, FILE *file)
{
- guint32 start_sec; /* start of recording (GMT) (seconds) */
- guint32 start_usec; /* start of recording (GMT) (microseconds)*/
- guint32 source; /* network source (multicast address) */
- size_t sourcelen;
- guint16 port; /* UDP port */
- guint16 padding; /* 2 padding bytes */
- char* addr_str = (char*)address_to_display(NULL, &(strinfo->dest_addr));
-
- fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
- addr_str,
- strinfo->dest_port);
- wmem_free(NULL, addr_str);
-
- start_sec = g_htonl(strinfo->start_fd->abs_ts.secs);
- start_usec = g_htonl(strinfo->start_fd->abs_ts.nsecs / 1000000);
- /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
- memset(&source, 0, sizeof source);
- sourcelen = strinfo->src_addr.len;
- if (sourcelen > sizeof source)
- sourcelen = sizeof source;
- memcpy(&source, strinfo->src_addr.data, sourcelen);
- port = g_htons(strinfo->src_port);
- padding = 0;
-
- if (fwrite(&start_sec, 4, 1, file) == 0)
- return;
- if (fwrite(&start_usec, 4, 1, file) == 0)
- return;
- if (fwrite(&source, 4, 1, file) == 0)
- return;
- if (fwrite(&port, 2, 1, file) == 0)
- return;
- if (fwrite(&padding, 2, 1, file) == 0)
- return;
+ guint32 start_sec; /* start of recording (GMT) (seconds) */
+ guint32 start_usec; /* start of recording (GMT) (microseconds)*/
+ guint32 source; /* network source (multicast address) */
+ size_t sourcelen;
+ guint16 port; /* UDP port */
+ guint16 padding; /* 2 padding bytes */
+ char* addr_str = address_to_display(NULL, &(strinfo->id.dst_addr));
+
+ fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
+ addr_str,
+ strinfo->id.dst_port);
+ wmem_free(NULL, addr_str);
+
+ start_sec = g_htonl(strinfo->start_fd->abs_ts.secs);
+ start_usec = g_htonl(strinfo->start_fd->abs_ts.nsecs / 1000000);
+ /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
+ memset(&source, 0, sizeof source);
+ sourcelen = strinfo->id.src_addr.len;
+ if (sourcelen > sizeof source)
+ sourcelen = sizeof source;
+ memcpy(&source, strinfo->id.src_addr.data, sourcelen);
+ port = g_htons(strinfo->id.src_port);
+ padding = 0;
+
+ if (fwrite(&start_sec, 4, 1, file) == 0)
+ return;
+ if (fwrite(&start_usec, 4, 1, file) == 0)
+ return;
+ if (fwrite(&source, 4, 1, file) == 0)
+ return;
+ if (fwrite(&port, 2, 1, file) == 0)
+ return;
+ if (fwrite(&padding, 2, 1, file) == 0)
+ return;
}
/* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
static void rtp_write_sample(rtpdump_info_t* rtpdump_info, FILE* file)
{
- guint16 length; /* length of packet, including this header (may
- be smaller than plen if not whole packet recorded) */
- guint16 plen; /* actual header+payload length for RTP, 0 for RTCP */
- guint32 offset; /* milliseconds since the start of recording */
-
- length = g_htons(rtpdump_info->num_samples + 8);
- plen = g_htons(rtpdump_info->num_samples);
- offset = g_htonl(rtpdump_info->rec_time);
-
- if (fwrite(&length, 2, 1, file) == 0)
- return;
- if (fwrite(&plen, 2, 1, file) == 0)
- return;
- if (fwrite(&offset, 4, 1, file) == 0)
- return;
- if (fwrite(rtpdump_info->samples, rtpdump_info->num_samples, 1, file) == 0)
- return;
+ guint16 length; /* length of packet, including this header (may
+ be smaller than plen if not whole packet recorded) */
+ guint16 plen; /* actual header+payload length for RTP, 0 for RTCP */
+ guint32 offset; /* milliseconds since the start of recording */
+
+ length = g_htons(rtpdump_info->num_samples + 8);
+ plen = g_htons(rtpdump_info->num_samples);
+ offset = g_htonl(rtpdump_info->rec_time);
+
+ if (fwrite(&length, 2, 1, file) == 0)
+ return;
+ if (fwrite(&plen, 2, 1, file) == 0)
+ return;
+ if (fwrite(&offset, 4, 1, file) == 0)
+ return;
+ if (fwrite(rtpdump_info->samples, rtpdump_info->num_samples, 1, file) == 0)
+ return;
}
/****************************************************************************/
/* whenever a RTP packet is seen by the tap listener */
-int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
-{
- rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)arg;
- const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2;
- rtp_stream_info_t new_stream_info;
- rtp_stream_info_t *stream_info = NULL;
- GList* list;
- rtpdump_info_t rtpdump_info;
-
- struct _rtp_conversation_info *p_conv_data = NULL;
-
- /* gather infos on the stream this packet is part of */
- memset(&new_stream_info, 0, sizeof(rtp_stream_info_t));
- copy_address(&(new_stream_info.src_addr), &(pinfo->src));
- new_stream_info.src_port = pinfo->srcport;
- copy_address(&(new_stream_info.dest_addr), &(pinfo->dst));
- new_stream_info.dest_port = pinfo->destport;
- new_stream_info.ssrc = rtpinfo->info_sync_src;
- new_stream_info.payload_type = rtpinfo->info_payload_type;
- new_stream_info.payload_type_name = g_strdup(rtpinfo->info_payload_type_str);
-
- if (tapinfo->mode == TAP_ANALYSE) {
- /* check whether we already have a stream with these parameters in the list */
- list = g_list_first(tapinfo->strinfo_list);
- while (list)
- {
- if (rtp_stream_info_cmp(&new_stream_info, (rtp_stream_info_t*)(list->data))==0)
- {
- stream_info = (rtp_stream_info_t*)(list->data); /*found!*/
- break;
- }
- list = g_list_next(list);
- }
-
- /* not in the list? then create a new entry */
- if (!stream_info) {
- new_stream_info.start_fd = pinfo->fd;
- new_stream_info.start_rel_time = pinfo->rel_ts;
-
- /* reset RTP stats */
- new_stream_info.rtp_stats.first_packet = TRUE;
- new_stream_info.rtp_stats.reg_pt = PT_UNDEFINED;
-
- /* Get the Setup frame number who set this RTP stream */
- p_conv_data = (struct _rtp_conversation_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_get_id_by_filter_name("rtp"), 0);
- if (p_conv_data)
- new_stream_info.setup_frame_number = p_conv_data->frame_number;
- else
- new_stream_info.setup_frame_number = 0xFFFFFFFF;
-
- stream_info = g_new(rtp_stream_info_t,1);
- *stream_info = new_stream_info; /* memberwise copy of struct */
- tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, stream_info);
- }
-
- /* get RTP stats for the packet */
- rtp_packet_analyse(&(stream_info->rtp_stats), pinfo, rtpinfo);
- if (stream_info->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
- || stream_info->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
- stream_info->problem = TRUE;
-
-
- /* increment the packets counter for this stream */
- ++(stream_info->packet_count);
- stream_info->stop_rel_time = pinfo->rel_ts;
-
- /* increment the packets counter of all streams */
- ++(tapinfo->npackets);
-
- return 1; /* refresh output */
- }
- else if (tapinfo->mode == TAP_SAVE) {
- if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0) {
- /* XXX - what if rtpinfo->info_all_data_present is
- FALSE, so that we don't *have* all the data? */
- rtpdump_info.rec_time = nstime_to_msec(&pinfo->fd->abs_ts) -
- nstime_to_msec(&tapinfo->filter_stream_fwd->start_fd->abs_ts);
- rtpdump_info.num_samples = rtpinfo->info_data_len;
- rtpdump_info.samples = rtpinfo->info_data;
- rtp_write_sample(&rtpdump_info, tapinfo->save_file);
- }
- }
- else if (tapinfo->mode == TAP_MARK && tapinfo->tap_mark_packet) {
- if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0
- || rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_rev)==0)
- {
- tapinfo->tap_mark_packet(tapinfo, pinfo->fd);
- }
- }
- return 0;
-}
-
-
-typedef struct _key_value {
- guint32 key;
- guint32 value;
-} key_value;
-
-
-/* RTP sampling clock rates for fixed payload types as defined in
- http://www.iana.org/assignments/rtp-parameters */
-static const key_value clock_map[] = {
- {PT_PCMU, 8000},
- {PT_1016, 8000},
- {PT_G721, 8000},
- {PT_GSM, 8000},
- {PT_G723, 8000},
- {PT_DVI4_8000, 8000},
- {PT_DVI4_16000, 16000},
- {PT_LPC, 8000},
- {PT_PCMA, 8000},
- {PT_G722, 8000},
- {PT_L16_STEREO, 44100},
- {PT_L16_MONO, 44100},
- {PT_QCELP, 8000},
- {PT_CN, 8000},
- {PT_MPA, 90000},
- {PT_G728, 8000},
- {PT_G728, 8000},
- {PT_DVI4_11025, 11025},
- {PT_DVI4_22050, 22050},
- {PT_G729, 8000},
- {PT_CN_OLD, 8000},
- {PT_CELB, 90000},
- {PT_JPEG, 90000},
- {PT_NV, 90000},
- {PT_H261, 90000},
- {PT_MPV, 90000},
- {PT_MP2T, 90000},
- {PT_H263, 90000},
-};
-
-#define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0])
-
-static guint32
-get_clock_rate(guint32 key)
+int rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
{
- size_t i;
-
- for (i = 0; i < NUM_CLOCK_VALUES; i++) {
- if (clock_map[i].key == key)
- return clock_map[i].value;
- }
- return 0;
+ rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)arg;
+ const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2;
+ rtpstream_info_t new_stream_info;
+ rtpstream_info_t *stream_info = NULL;
+ GList* list;
+ rtpdump_info_t rtpdump_info;
+
+ struct _rtp_conversation_info *p_conv_data = NULL;
+
+ /* gather infos on the stream this packet is part of.
+ * Addresses and strings are read-only and must be duplicated if copied. */
+ rtpstream_info_init(&new_stream_info);
+ rtpstream_id_copy_pinfo(pinfo,&(new_stream_info.id),FALSE);
+ new_stream_info.id.ssrc = rtpinfo->info_sync_src;
+ new_stream_info.first_payload_type = rtpinfo->info_payload_type;
+ new_stream_info.first_payload_type_name = rtpinfo->info_payload_type_str;
+
+ if (tapinfo->mode == TAP_ANALYSE) {
+ /* check whether we already have a stream with these parameters in the list */
+ list = g_list_first(tapinfo->strinfo_list);
+ while (list)
+ {
+ if (rtpstream_info_cmp(&new_stream_info, (rtpstream_info_t *)(list->data))==0)
+ {
+ stream_info = (rtpstream_info_t *)(list->data); /*found!*/
+ break;
+ }
+ list = g_list_next(list);
+ }
+
+ /* not in the list? then create a new entry */
+ if (!stream_info) {
+ new_stream_info.start_fd = pinfo->fd;
+ new_stream_info.start_rel_time = pinfo->rel_ts;
+ new_stream_info.first_payload_type = rtpinfo->info_payload_type;
+ new_stream_info.first_payload_type_name = rtpinfo->info_payload_type_str;
+
+ /* reset RTP stats */
+ new_stream_info.rtp_stats.first_packet = TRUE;
+ new_stream_info.rtp_stats.reg_pt = PT_UNDEFINED;
+
+ /* Get the Setup frame number who set this RTP stream */
+ p_conv_data = (struct _rtp_conversation_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_get_id_by_filter_name("rtp"), 0);
+ if (p_conv_data)
+ new_stream_info.setup_frame_number = p_conv_data->frame_number;
+ else
+ new_stream_info.setup_frame_number = 0xFFFFFFFF;
+
+ stream_info = rtpstream_info_malloc_and_init();
+ rtpstream_info_copy_deep(stream_info, &new_stream_info);
+ tapinfo->strinfo_list = g_list_prepend(tapinfo->strinfo_list, stream_info);
+ }
+
+ /* get RTP stats for the packet */
+ rtppacket_analyse(&(stream_info->rtp_stats), pinfo, rtpinfo);
+ if (stream_info->payload_type_names[rtpinfo->info_payload_type] == NULL ) {
+ update_payload_names(stream_info, rtpinfo);
+ }
+
+ if (stream_info->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
+ || stream_info->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
+ stream_info->problem = TRUE;
+
+
+ /* increment the packets counter for this stream */
+ ++(stream_info->packet_count);
+ stream_info->stop_rel_time = pinfo->rel_ts;
+
+ /* increment the packets counter of all streams */
+ ++(tapinfo->npackets);
+
+ return 1; /* refresh output */
+ }
+ else if (tapinfo->mode == TAP_SAVE) {
+ if (rtpstream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0) {
+ /* XXX - what if rtpinfo->info_all_data_present is
+ FALSE, so that we don't *have* all the data? */
+ rtpdump_info.rec_time = nstime_to_msec(&pinfo->abs_ts) -
+ nstime_to_msec(&tapinfo->filter_stream_fwd->start_fd->abs_ts);
+ rtpdump_info.num_samples = rtpinfo->info_data_len;
+ rtpdump_info.samples = rtpinfo->info_data;
+ rtp_write_sample(&rtpdump_info, tapinfo->save_file);
+ }
+ }
+ else if (tapinfo->mode == TAP_MARK && tapinfo->tap_mark_packet) {
+ if (rtpstream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0
+ || rtpstream_info_cmp(&new_stream_info, tapinfo->filter_stream_rev)==0)
+ {
+ tapinfo->tap_mark_packet(tapinfo, pinfo->fd);
+ }
+ }
+ return 0;
}
-typedef struct _mimetype_and_clock {
- const gchar *pt_mime_name_str;
- guint32 value;
-} mimetype_and_clock;
-/* RTP sampling clock rates for
- "In addition to the RTP payload formats (encodings) listed in the RTP
- Payload Types table, there are additional payload formats that do not
- have static RTP payload types assigned but instead use dynamic payload
- type number assignment. Each payload format is named by a registered
- MIME subtype"
- http://www.iana.org/assignments/rtp-parameters.
-
- NOTE: Please keep the mimetypes in case insensitive alphabetical order.
-*/
-static const mimetype_and_clock mimetype_and_clock_map[] = {
- {"AMR", 8000}, /* [RFC4867][RFC3267] */
- {"AMR-WB", 16000}, /* [RFC4867][RFC3267] */
- {"BMPEG", 90000}, /* [RFC2343],[RFC3555] */
- {"BT656", 90000}, /* [RFC2431],[RFC3555] */
- {"DV", 90000}, /* [RFC3189] */
- {"EVRC", 8000}, /* [RFC3558] */
- {"EVRC0", 8000}, /* [RFC4788] */
- {"EVRC1", 8000}, /* [RFC4788] */
- {"EVRCB", 8000}, /* [RFC4788] */
- {"EVRCB0", 8000}, /* [RFC4788] */
- {"EVRCB1", 8000}, /* [RFC4788] */
- {"EVRCWB", 16000}, /* [RFC5188] */
- {"EVRCWB0", 16000}, /* [RFC5188] */
- {"EVRCWB1", 16000}, /* [RFC5188] */
- {"G7221", 16000}, /* [RFC3047] */
- {"G726-16", 8000}, /* [RFC3551][RFC4856] */
- {"G726-24", 8000}, /* [RFC3551][RFC4856] */
- {"G726-32", 8000}, /* [RFC3551][RFC4856] */
- {"G726-40", 8000}, /* [RFC3551][RFC4856] */
- {"G729D", 8000}, /* [RFC3551][RFC4856] */
- {"G729E", 8000}, /* [RFC3551][RFC4856] */
- {"GSM-EFR", 8000}, /* [RFC3551] */
- {"H263-1998", 90000}, /* [RFC2429],[RFC3555] */
- {"H263-2000", 90000}, /* [RFC2429],[RFC3555] */
- {"H264", 90000}, /* [RFC3984] */
- {"MP1S", 90000}, /* [RFC2250],[RFC3555] */
- {"MP2P", 90000}, /* [RFC2250],[RFC3555] */
- {"MP4V-ES", 90000}, /* [RFC3016] */
- {"mpa-robust", 90000}, /* [RFC3119] */
- {"pointer", 90000}, /* [RFC2862] */
- {"raw", 90000}, /* [RFC4175] */
- {"red", 1000}, /* [RFC4102] */
- {"SMV", 8000}, /* [RFC3558] */
- {"SMV0", 8000}, /* [RFC3558] */
- {"t140", 1000}, /* [RFC4103] */
- {"telephone-event", 8000}, /* [RFC4733] */
-};
-
-#define NUM_DYN_CLOCK_VALUES (sizeof mimetype_and_clock_map / sizeof mimetype_and_clock_map[0])
-
-static guint32
-get_dyn_pt_clock_rate(const gchar *payload_type_str)
+/****************************************************************************/
+/* evaluate rtpstream_info_t calculations */
+/* - code is gathered from existing GTK/Qt/tui sources related to RTP statistics calculation
+ * - one place for calculations ensures that all wireshark tools shows same output for same input and avoids code duplication
+ */
+void rtpstream_info_calculate(const rtpstream_info_t *strinfo, rtpstream_info_calc_t *calc)
{
- int i;
-
- /* Search for matching mimetype in reverse order to avoid false matches
- * when pt_mime_name_str is the prefix of payload_type_str */
- for (i = NUM_DYN_CLOCK_VALUES - 1; i > -1 ; i--) {
- if (g_ascii_strncasecmp(mimetype_and_clock_map[i].pt_mime_name_str,payload_type_str,(strlen(mimetype_and_clock_map[i].pt_mime_name_str))) == 0)
- return mimetype_and_clock_map[i].value;
- }
-
- return 0;
+ double sumt;
+ double sumTS;
+ double sumt2;
+ double sumtTS;
+ double clock_drift_x;
+ guint32 clock_rate_x;
+ double duration_x;
+
+ calc->src_addr_str = address_to_display(NULL, &(strinfo->id.src_addr));
+ calc->src_port = strinfo->id.src_port;
+ calc->dst_addr_str = address_to_display(NULL, &(strinfo->id.dst_addr));
+ calc->dst_port = strinfo->id.dst_port;
+ calc->ssrc = strinfo->id.ssrc;
+
+ calc->all_payload_type_names = wmem_strdup(NULL, strinfo->all_payload_type_names);
+
+ calc->packet_count = strinfo->packet_count;
+ /* packet count, lost packets */
+ calc->packet_expected = (strinfo->rtp_stats.stop_seq_nr + strinfo->rtp_stats.cycles*65536)
+ - strinfo->rtp_stats.start_seq_nr + 1;
+ calc->total_nr = strinfo->rtp_stats.total_nr;
+ calc->lost_num = calc->packet_expected - strinfo->rtp_stats.total_nr;
+ if (calc->packet_expected) {
+ calc->lost_perc = (double)(calc->lost_num*100)/(double)calc->packet_expected;
+ } else {
+ calc->lost_perc = 0;
+ }
+
+ calc->max_delta = strinfo->rtp_stats.max_delta;
+ calc->max_jitter = strinfo->rtp_stats.max_jitter;
+ calc->mean_jitter = strinfo->rtp_stats.mean_jitter;
+ calc->max_skew = strinfo->rtp_stats.max_skew;
+ calc->problem = strinfo->problem;
+ sumt = strinfo->rtp_stats.sumt;
+ sumTS = strinfo->rtp_stats.sumTS;
+ sumt2 = strinfo->rtp_stats.sumt2;
+ sumtTS = strinfo->rtp_stats.sumtTS;
+ duration_x = strinfo->rtp_stats.time - strinfo->rtp_stats.start_time;
+
+ if ((calc->packet_count >0) && (sumt2 > 0)) {
+ clock_drift_x = (calc->packet_count * sumtTS - sumt * sumTS) / (calc->packet_count * sumt2 - sumt * sumt);
+ calc->clock_drift_ms = duration_x * (clock_drift_x - 1.0);
+ clock_rate_x = (guint32)(strinfo->rtp_stats.clock_rate * clock_drift_x);
+ calc->freq_drift_hz = clock_drift_x * clock_rate_x;
+ calc->freq_drift_perc = 100.0 * (clock_drift_x - 1.0);
+ } else {
+ calc->clock_drift_ms = 0.0;
+ calc->freq_drift_hz = 0.0;
+ calc->freq_drift_perc = 0.0;
+ }
+ calc->duration_ms = duration_x / 1000.0;
+ calc->sequence_err = strinfo->rtp_stats.sequence;
+ calc->start_time_ms = strinfo->rtp_stats.start_time / 1000.0;
+ calc->first_packet_num = strinfo->rtp_stats.first_packet_num;
+ calc->last_packet_num = strinfo->rtp_stats.max_nr;
}
/****************************************************************************/
-void
-rtp_packet_analyse(tap_rtp_stat_t *statinfo,
- packet_info *pinfo,
- const struct _rtp_info *rtpinfo)
+/* free rtpstream_info_calc_t structure (internal items) */
+void rtpstream_info_calc_free(rtpstream_info_calc_t *calc)
{
- double current_time;
- double current_jitter;
- double current_diff = 0;
- double nominaltime;
- double arrivaltime; /* Time relative to start_time */
- double expected_time;
- double absskew;
- guint32 clock_rate;
-
- /* Store the current time */
- current_time = nstime_to_msec(&pinfo->rel_ts);
-
- /* Is this the first packet we got in this direction? */
- if (statinfo->first_packet) {
- /* Save the MAC address of the first RTP frame */
- if( pinfo->dl_src.type == AT_ETHER){
- copy_address(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src));
- }
- statinfo->start_seq_nr = rtpinfo->info_seq_num;
- statinfo->stop_seq_nr = rtpinfo->info_seq_num;
- statinfo->seq_num = rtpinfo->info_seq_num;
- statinfo->start_time = current_time;
- statinfo->timestamp = rtpinfo->info_timestamp;
- statinfo->first_timestamp = rtpinfo->info_timestamp;
- statinfo->time = current_time;
- statinfo->lastnominaltime = 0;
- statinfo->pt = rtpinfo->info_payload_type;
- statinfo->reg_pt = rtpinfo->info_payload_type;
- statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
- statinfo->bw_history[statinfo->bw_index].time = current_time;
- statinfo->bw_index++;
- statinfo->total_bytes += rtpinfo->info_data_len + 28;
- statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
- /* Not needed ? initialised to zero? */
- statinfo->delta = 0;
- statinfo->jitter = 0;
- statinfo->diff = 0;
-
- statinfo->total_nr++;
- statinfo->flags |= STAT_FLAG_FIRST;
- if (rtpinfo->info_marker_set) {
- statinfo->flags |= STAT_FLAG_MARKER;
- }
- statinfo->first_packet = FALSE;
- return;
- }
-
- /* Reset flags */
- statinfo->flags = 0;
-
- /* Chek for duplicates (src mac differs from first_packet_mac_addr) */
- if( pinfo->dl_src.type == AT_ETHER){
- if(!addresses_equal(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src))){
- statinfo->flags |= STAT_FLAG_DUP_PKT;
- statinfo->delta = current_time-(statinfo->time);
- return;
- }
- }
-
- /* When calculating expected rtp packets the seq number can wrap around
- * so we have to count the number of cycles
- * Variable cycles counts the wraps around in forwarding connection and
- * under is flag that indicates where we are
- *
- * XXX How to determine number of cycles with all possible lost, late
- * and duplicated packets without any doubt? It seems to me, that
- * because of all possible combination of late, duplicated or lost
- * packets, this can only be more or less good approximation
- *
- * There are some combinations (rare but theoretically possible),
- * where below code won't work correctly - statistic may be wrong then.
- */
-
- /* So if the current sequence number is less than the start one
- * we assume, that there is another cycle running
- */
- if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
- statinfo->cycles++;
- statinfo->under = TRUE;
- }
- /* what if the start seq nr was 0? Then the above condition will never
- * be true, so we add another condition. XXX The problem would arise
- * if one of the packets with seq nr 0 or 65535 would be lost or late
- */
- else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
- (statinfo->under == FALSE)){
- statinfo->cycles++;
- statinfo->under = TRUE;
- }
- /* the whole round is over, so reset the flag */
- else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
- statinfo->under = FALSE;
- }
-
- /* Since it is difficult to count lost, duplicate or late packets separately,
- * we would like to know at least how many times the sequence number was not ok
- */
-
- /* If the current seq number equals the last one or if we are here for
- * the first time, then it is ok, we just store the current one as the last one
- */
- if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
- statinfo->seq_num = rtpinfo->info_seq_num;
- /* If the first one is 65535 we wrap */
- else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
- statinfo->seq_num = rtpinfo->info_seq_num;
- /* Lost packets. If the prev seq is enormously larger than the cur seq
- * we assume that instead of being massively late we lost the packet(s)
- * that would have indicated the sequence number wrapping. An imprecise
- * heuristic at best, but it seems to work well enough.
- * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5958 */
- else if (statinfo->seq_num+1 < rtpinfo->info_seq_num || statinfo->seq_num - rtpinfo->info_seq_num > 0xFF00) {
- statinfo->seq_num = rtpinfo->info_seq_num;
- statinfo->sequence++;
- statinfo->flags |= STAT_FLAG_WRONG_SEQ;
- }
- /* Late or duplicated */
- else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
- statinfo->sequence++;
- statinfo->flags |= STAT_FLAG_WRONG_SEQ;
- }
-
- /* Check payload type */
- if (rtpinfo->info_payload_type == PT_CN
- || rtpinfo->info_payload_type == PT_CN_OLD)
- statinfo->flags |= STAT_FLAG_PT_CN;
- if (statinfo->pt == PT_CN
- || statinfo->pt == PT_CN_OLD)
- statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
- if (rtpinfo->info_payload_type != statinfo->pt)
- statinfo->flags |= STAT_FLAG_PT_CHANGE;
- statinfo->pt = rtpinfo->info_payload_type;
-
- /*
- * Return for unknown payload types
- * Ignore jitter calculation for clockrate = 0
- */
- if (statinfo->pt < 96 ){
- clock_rate = get_clock_rate(statinfo->pt);
- }else{ /* Dynamic PT */
- if ( rtpinfo->info_payload_type_str != NULL ){
- /* Is it a "telephone-event" ?
- * Timestamp is not increased for telepone-event packets impacting
- * calculation of Jitter Skew and clock drift.
- * see 2.2.1 of RFC 4733
- */
- if (g_ascii_strncasecmp("telephone-event",rtpinfo->info_payload_type_str,(strlen("telephone-event")))==0){
- clock_rate = 0;
- statinfo->flags |= STAT_FLAG_PT_T_EVENT;
- }else{
- if(rtpinfo->info_payload_rate !=0){
- clock_rate = rtpinfo->info_payload_rate;
- }else{
- clock_rate = get_dyn_pt_clock_rate(rtpinfo->info_payload_type_str);
- }
- }
- }else{
- clock_rate = 0;
- }
- }
-
- /* Handle wraparound ? */
- arrivaltime = current_time - statinfo->start_time;
-
- if (statinfo->first_timestamp > rtpinfo->info_timestamp){
- /* Handle wraparound */
- nominaltime = (double)(rtpinfo->info_timestamp + 0xffffffff - statinfo->first_timestamp + 1);
- }else{
- nominaltime = (double)(rtpinfo->info_timestamp - statinfo->first_timestamp);
- }
-
- /* Can only analyze defined sampling rates */
- if (clock_rate != 0) {
- statinfo->clock_rate = clock_rate;
- /* Convert from sampling clock to ms */
- nominaltime = nominaltime /(clock_rate/1000);
-
- /* Calculate the current jitter(in ms) */
- if (!statinfo->first_packet) {
- expected_time = statinfo->time + (nominaltime - statinfo->lastnominaltime);
- current_diff = fabs(current_time - expected_time);
- current_jitter = (15 * statinfo->jitter + current_diff) / 16;
-
- statinfo->delta = current_time-(statinfo->time);
- statinfo->jitter = current_jitter;
- statinfo->diff = current_diff;
- }
- statinfo->lastnominaltime = nominaltime;
- /* Calculate skew, i.e. absolute jitter that also catches clock drift
- * Skew is positive if TS (nominal) is too fast
- */
- statinfo->skew = nominaltime - arrivaltime;
- absskew = fabs(statinfo->skew);
- if(absskew > fabs(statinfo->max_skew)){
- statinfo->max_skew = statinfo->skew;
- }
- /* Gather data for calculation of average, minimum and maximum framerate based on timestamp */
-#if 0
- if (numPackets > 0 && (!hardPayloadType || !alternatePayloadType)) {
- /* Skip first packet and possibly alternate payload type packets */
- double dt;
- dt = nominaltime - statinfo->lastnominaltime;
- sumdt += 1.0 * dt;
- numdt += (dt != 0 ? 1 : 0);
- mindt = (dt < mindt ? dt : mindt);
- maxdt = (dt > maxdt ? dt : maxdt);
- }
-#endif
- /* Gather data for calculation of skew least square */
- statinfo->sumt += 1.0 * current_time;
- statinfo->sumTS += 1.0 * nominaltime;
- statinfo->sumt2 += 1.0 * current_time * current_time;
- statinfo->sumtTS += 1.0 * current_time * nominaltime;
- }
-
- /* Calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP) + 8bytes(UDP) */
- statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
- statinfo->bw_history[statinfo->bw_index].time = current_time;
-
- /* Check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
- while ((statinfo->bw_history[statinfo->bw_start_index].time+1000/* ms */)<current_time){
- statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
- statinfo->bw_start_index++;
- if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
- };
- /* IP hdr + UDP + RTP */
- statinfo->total_bytes += rtpinfo->info_data_len + 28;
- statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
- statinfo->bw_index++;
- if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
-
-
- /* Is it a packet with the mark bit set? */
- if (rtpinfo->info_marker_set) {
- statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
- if (rtpinfo->info_timestamp > statinfo->timestamp){
- statinfo->flags |= STAT_FLAG_MARKER;
- }
- else{
- statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
- }
- }
- /* Is it a regular packet? */
- if (!(statinfo->flags & STAT_FLAG_FIRST)
- && !(statinfo->flags & STAT_FLAG_MARKER)
- && !(statinfo->flags & STAT_FLAG_PT_CN)
- && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
- && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
- /* Include it in maximum delta calculation */
- if (statinfo->delta > statinfo->max_delta) {
- statinfo->max_delta = statinfo->delta;
- statinfo->max_nr = pinfo->fd->num;
- }
- if (clock_rate != 0) {
- /* Maximum and mean jitter calculation */
- if (statinfo->jitter > statinfo->max_jitter) {
- statinfo->max_jitter = statinfo->jitter;
- }
- statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
- }
- }
- /* Regular payload change? (CN ignored) */
- if (!(statinfo->flags & STAT_FLAG_FIRST)
- && !(statinfo->flags & STAT_FLAG_PT_CN)) {
- if ((statinfo->pt != statinfo->reg_pt)
- && (statinfo->reg_pt != PT_UNDEFINED)) {
- statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
- }
- }
-
- /* Set regular payload*/
- if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
- statinfo->reg_pt = statinfo->pt;
- }
-
- statinfo->time = current_time;
- statinfo->timestamp = rtpinfo->info_timestamp;
- statinfo->stop_seq_nr = rtpinfo->info_seq_num;
- statinfo->total_nr++;
-
- return;
+ wmem_free(NULL, calc->src_addr_str);
+ wmem_free(NULL, calc->dst_addr_str);
+ wmem_free(NULL, calc->all_payload_type_names);
}
/*
- * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
*
- * Local variables:
- * c-basic-offset: 8
+ * Local Variables:
+ * c-basic-offset: 4
* tab-width: 8
- * indent-tabs-mode: t
+ * indent-tabs-mode: nil
* End:
*
- * vi: set shiftwidth=8 tabstop=8 noexpandtab:
- * :indentSize=8:tabSize=8:noTabs=false:
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
*/