2 * RTP streams summary addition for ethereal
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
9 * Ethereal - Network traffic analyzer
10 * By Gerald Combs <gerald@ethereal.com>
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"
49 #ifdef HAVE_SYS_TYPES_H
50 # include <sys/types.h>
56 gchar* address_to_str_w_none(address *addr){
58 if(addr->type==AT_NONE){
62 return(address_to_str(addr));
66 /****************************************************************************/
67 /* the one and only global rtpstream_tapinfo_t structure */
68 static rtpstream_tapinfo_t the_tapinfo_struct =
69 {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
72 /****************************************************************************/
73 /* GCompareFunc style comparison function for _rtp_stream_info */
74 gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
76 const struct _rtp_stream_info* a = aa;
77 const struct _rtp_stream_info* b = bb;
81 if (a==NULL || b==NULL)
83 if (ADDRESSES_EQUAL(&(a->src_addr), &(b->src_addr))
84 && (a->src_port == b->src_port)
85 && ADDRESSES_EQUAL(&(a->dest_addr), &(b->dest_addr))
86 && (a->dest_port == b->dest_port)
87 && (a->ssrc == b->ssrc))
94 /****************************************************************************/
95 /* when there is a [re]reading of packet's */
96 void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
100 if (tapinfo->mode == TAP_ANALYSE) {
101 /* free the data items first */
102 list = g_list_first(tapinfo->strinfo_list);
106 list = g_list_next(list);
108 g_list_free(tapinfo->strinfo_list);
109 tapinfo->strinfo_list = NULL;
110 tapinfo->nstreams = 0;
111 tapinfo->npackets = 0;
114 ++(tapinfo->launch_count);
119 /****************************************************************************/
120 /* redraw the output */
121 void rtpstream_draw(rtpstream_tapinfo_t *tapinfo _U_)
123 /* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
124 gtk_signal_emit_by_name(top_level, "signal_rtpstream_update");
126 rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
132 * rtpdump file format
134 * The file starts with the tool to be used for playing this file,
135 * the multicast/unicast receive address and the port.
137 * #!rtpplay1.0 224.2.0.1/3456\n
139 * This is followed by one binary header (RD_hdr_t) and one RD_packet_t
140 * structure for each received packet. All fields are in network byte
141 * order. We don't need the source IP address since we can do mapping
142 * based on SSRC. This saves (a little) space, avoids non-IPv4
143 * problems and privacy/security concerns. The header is followed by
144 * the RTP/RTCP header and (optionally) the actual payload.
147 #define RTPFILE_VERSION "1.0"
150 * Write a header to the current output file.
151 * The header consists of an identifying string, followed
152 * by a binary structure.
154 static void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
156 guint32 start_sec; /* start of recording (GMT) (seconds) */
157 guint32 start_usec; /* start of recording (GMT) (microseconds)*/
158 guint32 source; /* network source (multicast address) */
159 guint16 port; /* UDP port */
160 guint16 padding; /* 2 padding bytes */
162 fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
163 address_to_str_w_none(&(strinfo->dest_addr)),
166 start_sec = g_htonl(strinfo->start_sec);
167 start_usec = g_htonl(strinfo->start_usec);
168 source = *(strinfo->src_addr.data); /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
169 port = g_htons(strinfo->src_port);
172 fwrite(&start_sec, 4, 1, file);
173 fwrite(&start_usec, 4, 1, file);
174 fwrite(&source, 4, 1, file);
175 fwrite(&port, 2, 1, file);
176 fwrite(&padding, 2, 1, file);
179 /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
180 static void rtp_write_sample(rtp_sample_t* sample, FILE* file)
182 guint16 length; /* length of packet, including this header (may
183 be smaller than plen if not whole packet recorded) */
184 guint16 plen; /* actual header+payload length for RTP, 0 for RTCP */
185 guint32 offset; /* milliseconds since the start of recording */
187 length = g_htons(sample->header.frame_length + 8);
188 plen = g_htons(sample->header.frame_length);
189 offset = g_htonl(sample->header.rec_time);
191 fwrite(&length, 2, 1, file);
192 fwrite(&plen, 2, 1, file);
193 fwrite(&offset, 4, 1, file);
194 fwrite(sample->frame, sample->header.frame_length, 1, file);
198 /****************************************************************************/
199 /* whenever a RTP packet is seen by the tap listener */
200 int rtpstream_packet(rtpstream_tapinfo_t *tapinfo _U_, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo _U_)
202 rtp_stream_info_t tmp_strinfo;
203 rtp_stream_info_t *strinfo = NULL;
207 /* gather infos on the stream this packet is part of */
208 COPY_ADDRESS(&(tmp_strinfo.src_addr), &(pinfo->src));
209 tmp_strinfo.src_port = pinfo->srcport;
210 COPY_ADDRESS(&(tmp_strinfo.dest_addr), &(pinfo->dst));
211 tmp_strinfo.dest_port = pinfo->destport;
212 tmp_strinfo.ssrc = rtpinfo->info_sync_src;
213 tmp_strinfo.pt = rtpinfo->info_payload_type;
215 if (tapinfo->mode == TAP_ANALYSE) {
216 /* check wether we already have a stream with these parameters in the list */
217 list = g_list_first(tapinfo->strinfo_list);
220 if (rtp_stream_info_cmp(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0)
222 strinfo = (rtp_stream_info_t*)(list->data); /*found!*/
225 list = g_list_next(list);
228 /* not in the list? then create a new entry */
230 tmp_strinfo.npackets = 0;
231 tmp_strinfo.first_frame_num = pinfo->fd->num;
232 tmp_strinfo.start_sec = pinfo->fd->abs_secs;
233 tmp_strinfo.start_usec = pinfo->fd->abs_usecs;
234 tmp_strinfo.tag_vlan_error = 0;
235 tmp_strinfo.tag_diffserv_error = 0;
236 tmp_strinfo.vlan_id = 0;
237 strinfo = g_malloc(sizeof(rtp_stream_info_t));
238 *strinfo = tmp_strinfo; /* memberwise copy of struct */
239 tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
242 /* increment the packets counter for this stream */
243 ++(strinfo->npackets);
245 /* increment the packets counter of all streams */
246 ++(tapinfo->npackets);
248 return 1; /* refresh output */
250 else if (tapinfo->mode == TAP_SAVE) {
251 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
252 /* XXX - what if rtpinfo->info_all_data_present is
253 FALSE, so that we don't *have* all the data? */
254 sample.header.rec_time =
255 (pinfo->fd->abs_usecs + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
256 + (pinfo->fd->abs_secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
257 sample.header.frame_length = rtpinfo->info_data_len;
258 sample.frame = rtpinfo->info_data;
259 rtp_write_sample(&sample, tapinfo->save_file);
262 else if (tapinfo->mode == TAP_MARK) {
264 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
265 || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
267 mark_frame(&cfile, pinfo->fd);
274 /****************************************************************************/
275 /* scan for RTP streams */
276 void rtpstream_scan(void)
278 gboolean was_registered = the_tapinfo_struct.is_registered;
279 if (!the_tapinfo_struct.is_registered)
280 register_tap_listener_rtp_stream();
282 the_tapinfo_struct.mode = TAP_ANALYSE;
283 retap_packets(&cfile);
286 remove_tap_listener_rtp_stream();
290 /****************************************************************************/
291 /* save rtp dump of stream_fwd */
292 gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
294 gboolean was_registered = the_tapinfo_struct.is_registered;
295 /* open file for saving */
296 the_tapinfo_struct.save_file = fopen(filename, "wb");
297 if (the_tapinfo_struct.save_file==NULL) {
298 open_failure_alert_box(filename, errno, TRUE);
302 rtp_write_header(stream, the_tapinfo_struct.save_file);
303 if (ferror(the_tapinfo_struct.save_file)) {
304 write_failure_alert_box(filename, errno);
305 fclose(the_tapinfo_struct.save_file);
309 if (!the_tapinfo_struct.is_registered)
310 register_tap_listener_rtp_stream();
312 the_tapinfo_struct.mode = TAP_SAVE;
313 the_tapinfo_struct.filter_stream_fwd = stream;
314 retap_packets(&cfile);
315 the_tapinfo_struct.mode = TAP_ANALYSE;
318 remove_tap_listener_rtp_stream();
320 if (ferror(the_tapinfo_struct.save_file)) {
321 write_failure_alert_box(filename, errno);
322 fclose(the_tapinfo_struct.save_file);
326 if (fclose(the_tapinfo_struct.save_file) == EOF) {
327 write_failure_alert_box(filename, errno);
334 /****************************************************************************/
335 /* mark packets in stream_fwd or stream_rev */
336 void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
338 gboolean was_registered = the_tapinfo_struct.is_registered;
339 if (!the_tapinfo_struct.is_registered)
340 register_tap_listener_rtp_stream();
342 the_tapinfo_struct.mode = TAP_MARK;
343 the_tapinfo_struct.filter_stream_fwd = stream_fwd;
344 the_tapinfo_struct.filter_stream_rev = stream_rev;
345 retap_packets(&cfile);
346 the_tapinfo_struct.mode = TAP_ANALYSE;
349 remove_tap_listener_rtp_stream();
353 /****************************************************************************/
354 const rtpstream_tapinfo_t* rtpstream_get_info(void)
356 return &the_tapinfo_struct;
360 /****************************************************************************/
362 /****************************************************************************/
364 /****************************************************************************/
366 rtpstream_init_tap(char *dummy _U_)
368 /* XXX: never called? */
372 /* XXX just copied from gtk/rpc_stat.c */
373 void protect_thread_critical_region(void);
374 void unprotect_thread_critical_region(void);
376 /****************************************************************************/
378 remove_tap_listener_rtp_stream(void)
380 if (the_tapinfo_struct.is_registered) {
381 protect_thread_critical_region();
382 remove_tap_listener(&the_tapinfo_struct);
383 unprotect_thread_critical_region();
385 the_tapinfo_struct.is_registered = FALSE;
390 /****************************************************************************/
392 register_tap_listener_rtp_stream(void)
394 GString *error_string;
396 if (!the_tapinfo_struct.is_registered) {
397 register_ethereal_tap("rtp", rtpstream_init_tap);
399 error_string = register_tap_listener("rtp", &the_tapinfo_struct,
401 (void*)rtpstream_reset, (void*)rtpstream_packet, (void*)rtpstream_draw);
403 if (error_string != NULL) {
404 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
406 g_string_free(error_string, TRUE);
410 the_tapinfo_struct.is_registered = TRUE;