2 * RTP streams summary addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #include "rtp_stream.h"
33 #include "rtp_stream_dlg.h"
39 #include <epan/dissectors/packet-rtp.h>
42 #include "alert_box.h"
43 #include "simple_dialog.h"
44 #include "file_util.h"
50 #ifdef HAVE_SYS_TYPES_H
51 # include <sys/types.h>
55 #include <epan/addr_resolv.h>
58 /****************************************************************************/
59 /* the one and only global rtpstream_tapinfo_t structure */
60 static rtpstream_tapinfo_t the_tapinfo_struct =
61 {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
64 /****************************************************************************/
65 /* GCompareFunc style comparison function for _rtp_stream_info */
66 static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
68 const struct _rtp_stream_info* a = aa;
69 const struct _rtp_stream_info* b = bb;
73 if (a==NULL || b==NULL)
75 if (ADDRESSES_EQUAL(&(a->src_addr), &(b->src_addr))
76 && (a->src_port == b->src_port)
77 && ADDRESSES_EQUAL(&(a->dest_addr), &(b->dest_addr))
78 && (a->dest_port == b->dest_port)
79 && (a->ssrc == b->ssrc))
86 /****************************************************************************/
87 /* when there is a [re]reading of packet's */
88 void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
92 if (tapinfo->mode == TAP_ANALYSE) {
93 /* free the data items first */
94 list = g_list_first(tapinfo->strinfo_list);
98 list = g_list_next(list);
100 g_list_free(tapinfo->strinfo_list);
101 tapinfo->strinfo_list = NULL;
102 tapinfo->nstreams = 0;
103 tapinfo->npackets = 0;
106 ++(tapinfo->launch_count);
111 static void rtpstream_reset_cb(void *arg)
113 rtpstream_reset(arg);
116 /****************************************************************************/
117 /* redraw the output */
118 static void rtpstream_draw(void *arg _U_)
120 /* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
121 gtk_signal_emit_by_name(top_level, "signal_rtpstream_update");
123 rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
129 * rtpdump file format
131 * The file starts with the tool to be used for playing this file,
132 * the multicast/unicast receive address and the port.
134 * #!rtpplay1.0 224.2.0.1/3456\n
136 * This is followed by one binary header (RD_hdr_t) and one RD_packet_t
137 * structure for each received packet. All fields are in network byte
138 * order. We don't need the source IP address since we can do mapping
139 * based on SSRC. This saves (a little) space, avoids non-IPv4
140 * problems and privacy/security concerns. The header is followed by
141 * the RTP/RTCP header and (optionally) the actual payload.
144 #define RTPFILE_VERSION "1.0"
147 * Write a header to the current output file.
148 * The header consists of an identifying string, followed
149 * by a binary structure.
151 static void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
153 guint32 start_sec; /* start of recording (GMT) (seconds) */
154 guint32 start_usec; /* start of recording (GMT) (microseconds)*/
155 guint32 source; /* network source (multicast address) */
156 guint16 port; /* UDP port */
157 guint16 padding; /* 2 padding bytes */
159 fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
160 get_addr_name(&(strinfo->dest_addr)),
163 start_sec = g_htonl(strinfo->start_sec);
164 start_usec = g_htonl(strinfo->start_usec);
165 source = *(strinfo->src_addr.data); /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
166 port = g_htons(strinfo->src_port);
169 fwrite(&start_sec, 4, 1, file);
170 fwrite(&start_usec, 4, 1, file);
171 fwrite(&source, 4, 1, file);
172 fwrite(&port, 2, 1, file);
173 fwrite(&padding, 2, 1, file);
176 /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
177 static void rtp_write_sample(rtp_sample_t* sample, FILE* file)
179 guint16 length; /* length of packet, including this header (may
180 be smaller than plen if not whole packet recorded) */
181 guint16 plen; /* actual header+payload length for RTP, 0 for RTCP */
182 guint32 offset; /* milliseconds since the start of recording */
184 length = g_htons(sample->header.frame_length + 8);
185 plen = g_htons(sample->header.frame_length);
186 offset = g_htonl(sample->header.rec_time);
188 fwrite(&length, 2, 1, file);
189 fwrite(&plen, 2, 1, file);
190 fwrite(&offset, 4, 1, file);
191 fwrite(sample->frame, sample->header.frame_length, 1, file);
195 /****************************************************************************/
196 /* whenever a RTP packet is seen by the tap listener */
197 static int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
199 rtpstream_tapinfo_t *tapinfo = arg;
200 const struct _rtp_info *rtpinfo = arg2;
201 rtp_stream_info_t tmp_strinfo;
202 rtp_stream_info_t *strinfo = NULL;
206 struct _rtp_conversation_info *p_conv_data = NULL;
208 /* gather infos on the stream this packet is part of */
209 COPY_ADDRESS(&(tmp_strinfo.src_addr), &(pinfo->src));
210 tmp_strinfo.src_port = pinfo->srcport;
211 COPY_ADDRESS(&(tmp_strinfo.dest_addr), &(pinfo->dst));
212 tmp_strinfo.dest_port = pinfo->destport;
213 tmp_strinfo.ssrc = rtpinfo->info_sync_src;
214 tmp_strinfo.pt = rtpinfo->info_payload_type;
216 if (tapinfo->mode == TAP_ANALYSE) {
217 /* check wether we already have a stream with these parameters in the list */
218 list = g_list_first(tapinfo->strinfo_list);
221 if (rtp_stream_info_cmp(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0)
223 strinfo = (rtp_stream_info_t*)(list->data); /*found!*/
226 list = g_list_next(list);
229 /* not in the list? then create a new entry */
231 tmp_strinfo.npackets = 0;
232 tmp_strinfo.first_frame_num = pinfo->fd->num;
233 tmp_strinfo.start_sec = pinfo->fd->abs_ts.secs;
234 tmp_strinfo.start_usec = pinfo->fd->abs_ts.nsecs/1000;
235 tmp_strinfo.start_rel_sec = pinfo->fd->rel_ts.secs;
236 tmp_strinfo.start_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
237 tmp_strinfo.tag_vlan_error = 0;
238 tmp_strinfo.tag_diffserv_error = 0;
239 tmp_strinfo.vlan_id = 0;
240 tmp_strinfo.problem = FALSE;
242 /* reset RTP stats */
243 tmp_strinfo.rtp_stats.first_packet = TRUE;
244 tmp_strinfo.rtp_stats.max_delta = 0;
245 tmp_strinfo.rtp_stats.max_jitter = 0;
246 tmp_strinfo.rtp_stats.mean_jitter = 0;
247 tmp_strinfo.rtp_stats.delta = 0;
248 tmp_strinfo.rtp_stats.diff = 0;
249 tmp_strinfo.rtp_stats.jitter = 0;
250 tmp_strinfo.rtp_stats.bandwidth = 0;
251 tmp_strinfo.rtp_stats.total_bytes = 0;
252 tmp_strinfo.rtp_stats.bw_start_index = 0;
253 tmp_strinfo.rtp_stats.bw_index = 0;
254 tmp_strinfo.rtp_stats.timestamp = 0;
255 tmp_strinfo.rtp_stats.max_nr = 0;
256 tmp_strinfo.rtp_stats.total_nr = 0;
257 tmp_strinfo.rtp_stats.sequence = 0;
258 tmp_strinfo.rtp_stats.start_seq_nr = 0;
259 tmp_strinfo.rtp_stats.stop_seq_nr = 0;
260 tmp_strinfo.rtp_stats.cycles = 0;
261 tmp_strinfo.rtp_stats.under = FALSE;
262 tmp_strinfo.rtp_stats.start_time = 0;
263 tmp_strinfo.rtp_stats.time = 0;
264 tmp_strinfo.rtp_stats.reg_pt = PT_UNDEFINED;
266 /* Get the Setup frame number who set this RTP stream */
267 p_conv_data = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name("rtp"));
269 tmp_strinfo.setup_frame_number = p_conv_data->frame_number;
271 tmp_strinfo.setup_frame_number = 0xFFFFFFFF;
273 strinfo = g_malloc(sizeof(rtp_stream_info_t));
274 *strinfo = tmp_strinfo; /* memberwise copy of struct */
275 tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
278 /* get RTP stats for the packet */
279 rtp_packet_analyse(&(strinfo->rtp_stats), pinfo, rtpinfo);
280 if (strinfo->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
281 || strinfo->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
282 strinfo->problem = TRUE;
285 /* increment the packets counter for this stream */
286 ++(strinfo->npackets);
287 strinfo->stop_rel_sec = pinfo->fd->rel_ts.secs;
288 strinfo->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
290 /* increment the packets counter of all streams */
291 ++(tapinfo->npackets);
293 return 1; /* refresh output */
295 else if (tapinfo->mode == TAP_SAVE) {
296 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
297 /* XXX - what if rtpinfo->info_all_data_present is
298 FALSE, so that we don't *have* all the data? */
299 sample.header.rec_time =
300 (pinfo->fd->abs_ts.nsecs/1000 + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
301 + (pinfo->fd->abs_ts.secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
302 sample.header.frame_length = rtpinfo->info_data_len;
303 sample.frame = rtpinfo->info_data;
304 rtp_write_sample(&sample, tapinfo->save_file);
307 else if (tapinfo->mode == TAP_MARK) {
309 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
310 || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
312 cf_mark_frame(&cfile, pinfo->fd);
319 /****************************************************************************/
320 /* scan for RTP streams */
321 void rtpstream_scan(void)
323 gboolean was_registered = the_tapinfo_struct.is_registered;
324 if (!the_tapinfo_struct.is_registered)
325 register_tap_listener_rtp_stream();
327 the_tapinfo_struct.mode = TAP_ANALYSE;
328 cf_retap_packets(&cfile, FALSE);
331 remove_tap_listener_rtp_stream();
335 /****************************************************************************/
336 /* save rtp dump of stream_fwd */
337 gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
339 gboolean was_registered = the_tapinfo_struct.is_registered;
340 /* open file for saving */
341 the_tapinfo_struct.save_file = eth_fopen(filename, "wb");
342 if (the_tapinfo_struct.save_file==NULL) {
343 open_failure_alert_box(filename, errno, TRUE);
347 rtp_write_header(stream, the_tapinfo_struct.save_file);
348 if (ferror(the_tapinfo_struct.save_file)) {
349 write_failure_alert_box(filename, errno);
350 fclose(the_tapinfo_struct.save_file);
354 if (!the_tapinfo_struct.is_registered)
355 register_tap_listener_rtp_stream();
357 the_tapinfo_struct.mode = TAP_SAVE;
358 the_tapinfo_struct.filter_stream_fwd = stream;
359 cf_retap_packets(&cfile, FALSE);
360 the_tapinfo_struct.mode = TAP_ANALYSE;
363 remove_tap_listener_rtp_stream();
365 if (ferror(the_tapinfo_struct.save_file)) {
366 write_failure_alert_box(filename, errno);
367 fclose(the_tapinfo_struct.save_file);
371 if (fclose(the_tapinfo_struct.save_file) == EOF) {
372 write_failure_alert_box(filename, errno);
379 /****************************************************************************/
380 /* mark packets in stream_fwd or stream_rev */
381 void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
383 gboolean was_registered = the_tapinfo_struct.is_registered;
384 if (!the_tapinfo_struct.is_registered)
385 register_tap_listener_rtp_stream();
387 the_tapinfo_struct.mode = TAP_MARK;
388 the_tapinfo_struct.filter_stream_fwd = stream_fwd;
389 the_tapinfo_struct.filter_stream_rev = stream_rev;
390 cf_retap_packets(&cfile, FALSE);
391 the_tapinfo_struct.mode = TAP_ANALYSE;
394 remove_tap_listener_rtp_stream();
398 /****************************************************************************/
399 const rtpstream_tapinfo_t* rtpstream_get_info(void)
401 return &the_tapinfo_struct;
405 /****************************************************************************/
407 /****************************************************************************/
409 /* XXX just copied from gtk/rpc_stat.c */
410 void protect_thread_critical_region(void);
411 void unprotect_thread_critical_region(void);
413 /****************************************************************************/
415 remove_tap_listener_rtp_stream(void)
417 if (the_tapinfo_struct.is_registered) {
418 protect_thread_critical_region();
419 remove_tap_listener(&the_tapinfo_struct);
420 unprotect_thread_critical_region();
422 the_tapinfo_struct.is_registered = FALSE;
427 /****************************************************************************/
429 register_tap_listener_rtp_stream(void)
431 GString *error_string;
433 if (!the_tapinfo_struct.is_registered) {
434 error_string = register_tap_listener("rtp", &the_tapinfo_struct,
435 NULL, rtpstream_reset_cb, rtpstream_packet,
438 if (error_string != NULL) {
439 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
441 g_string_free(error_string, TRUE);
445 the_tapinfo_struct.is_registered = TRUE;