Note realtick's invalidity for ETH_CAPTYPE_OTHERPOD in the comment for
[obnox/wireshark/wip.git] / gtk / rtp_stream.c
1 /* rtp_stream.c
2  * RTP streams summary addition for Wireshark
3  *
4  * $Id$
5  *
6  * Copyright 2003, Alcatel Business Systems
7  * By Lars Ruoff <lars.ruoff@gmx.net>
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
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.
17  *
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.
22  *
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.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include "globals.h"
33
34 #include <epan/tap.h>
35 #include "register.h"
36 #include <epan/dissectors/packet-rtp.h>
37
38
39 #include "alert_box.h"
40 #include "simple_dialog.h"
41 #include "file_util.h"
42
43 #ifdef HAVE_FCNTL_H
44 #include <fcntl.h>
45 #endif
46
47 #ifdef HAVE_SYS_TYPES_H
48 # include <sys/types.h>
49 #endif
50
51 #include <string.h>
52 #include <epan/addr_resolv.h>
53
54 #include "rtp_stream.h"
55 #include "rtp_stream_dlg.h"
56
57
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};
62
63
64 /****************************************************************************/
65 /* GCompareFunc style comparison function for _rtp_stream_info */
66 static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
67 {
68         const struct _rtp_stream_info* a = aa;
69         const struct _rtp_stream_info* b = bb;
70
71         if (a==b)
72                 return 0;
73         if (a==NULL || b==NULL)
74                 return 1;
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))
80                 return 0;
81         else
82                 return 1;
83 }
84
85
86 /****************************************************************************/
87 /* when there is a [re]reading of packet's */
88 void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
89 {
90         GList* list;
91
92         if (tapinfo->mode == TAP_ANALYSE) {
93                 /* free the data items first */
94                 list = g_list_first(tapinfo->strinfo_list);
95                 while (list)
96                 {
97                         g_free(list->data);
98                         list = g_list_next(list);
99                 }
100                 g_list_free(tapinfo->strinfo_list);
101                 tapinfo->strinfo_list = NULL;
102                 tapinfo->nstreams = 0;
103                 tapinfo->npackets = 0;
104         }
105
106         ++(tapinfo->launch_count);
107
108         return;
109 }
110
111 static void rtpstream_reset_cb(void *arg)
112 {
113         rtpstream_reset(arg);
114 }
115
116 /****************************************************************************/
117 /* redraw the output */
118 static void rtpstream_draw(void *arg _U_)
119 {
120 /* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
121         gtk_signal_emit_by_name(top_level, "signal_rtpstream_update");
122 */
123         rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
124         return;
125 }
126
127
128 /*
129 * rtpdump file format
130 *
131 * The file starts with the tool to be used for playing this file,
132 * the multicast/unicast receive address and the port.
133 *
134 * #!rtpplay1.0 224.2.0.1/3456\n
135 *
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.
142 */
143
144 #define RTPFILE_VERSION "1.0"
145
146 /*
147 * Write a header to the current output file.
148 * The header consists of an identifying string, followed
149 * by a binary structure.
150 */
151 static void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
152 {
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         size_t sourcelen;
157         guint16 port;          /* UDP port */
158         guint16 padding;       /* 2 padding bytes */
159         
160         fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
161                 get_addr_name(&(strinfo->dest_addr)),
162                 strinfo->dest_port);
163
164         start_sec = g_htonl(strinfo->start_sec);
165         start_usec = g_htonl(strinfo->start_usec);
166         /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
167         memset(&source, 0, sizeof source);
168         sourcelen = strinfo->src_addr.len;
169         if (sourcelen > sizeof source)
170                 sourcelen = sizeof source;
171         memcpy(&source, strinfo->src_addr.data, sourcelen);
172         port = g_htons(strinfo->src_port);
173         padding = 0;
174
175         if (fwrite(&start_sec, 4, 1, file) == 0)
176                 return;
177         if (fwrite(&start_usec, 4, 1, file) == 0)
178                 return;
179         if (fwrite(&source, 4, 1, file) == 0)
180                 return;
181         if (fwrite(&port, 2, 1, file) == 0)
182                 return;
183         if (fwrite(&padding, 2, 1, file) == 0)
184                 return;
185 }
186
187 /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
188 static void rtp_write_sample(rtp_sample_t* sample, FILE* file)
189 {
190         guint16 length;    /* length of packet, including this header (may
191                              be smaller than plen if not whole packet recorded) */
192         guint16 plen;      /* actual header+payload length for RTP, 0 for RTCP */
193         guint32 offset;    /* milliseconds since the start of recording */
194
195         length = g_htons(sample->header.frame_length + 8);
196         plen = g_htons(sample->header.frame_length);
197         offset = g_htonl(sample->header.rec_time);
198
199         if (fwrite(&length, 2, 1, file) == 0)
200                 return;
201         if (fwrite(&plen, 2, 1, file) == 0)
202                 return;
203         if (fwrite(&offset, 4, 1, file) == 0)
204                 return;
205         if (fwrite(sample->frame, sample->header.frame_length, 1, file) == 0)
206                 return;
207 }
208
209
210 /****************************************************************************/
211 /* whenever a RTP packet is seen by the tap listener */
212 static int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
213 {
214         rtpstream_tapinfo_t *tapinfo = arg;
215         const struct _rtp_info *rtpinfo = arg2;
216         rtp_stream_info_t tmp_strinfo;
217         rtp_stream_info_t *strinfo = NULL;
218         GList* list;
219         rtp_sample_t sample;
220
221         struct _rtp_conversation_info *p_conv_data = NULL;
222
223         /* gather infos on the stream this packet is part of */
224         COPY_ADDRESS(&(tmp_strinfo.src_addr), &(pinfo->src));
225         tmp_strinfo.src_port = pinfo->srcport;
226         COPY_ADDRESS(&(tmp_strinfo.dest_addr), &(pinfo->dst));
227         tmp_strinfo.dest_port = pinfo->destport;
228         tmp_strinfo.ssrc = rtpinfo->info_sync_src;
229         tmp_strinfo.pt = rtpinfo->info_payload_type;
230         tmp_strinfo.info_payload_type_str = rtpinfo->info_payload_type_str;
231
232         if (tapinfo->mode == TAP_ANALYSE) {
233                 /* check wether we already have a stream with these parameters in the list */
234                 list = g_list_first(tapinfo->strinfo_list);
235                 while (list)
236                 {
237                         if (rtp_stream_info_cmp(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0)
238                         {
239                                 strinfo = (rtp_stream_info_t*)(list->data);  /*found!*/
240                                 break;
241                         }
242                         list = g_list_next(list);
243                 }
244
245                 /* not in the list? then create a new entry */
246                 if (!strinfo) {
247                         tmp_strinfo.npackets = 0;
248                         tmp_strinfo.first_frame_num = pinfo->fd->num;
249                         tmp_strinfo.start_sec = (guint32) pinfo->fd->abs_ts.secs;
250                         tmp_strinfo.start_usec = pinfo->fd->abs_ts.nsecs/1000;
251                         tmp_strinfo.start_rel_sec = (guint32) pinfo->fd->rel_ts.secs;
252                         tmp_strinfo.start_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
253                         tmp_strinfo.tag_vlan_error = 0;
254                         tmp_strinfo.tag_diffserv_error = 0;
255                         tmp_strinfo.vlan_id = 0;
256                         tmp_strinfo.problem = FALSE;
257
258                         /* reset RTP stats */
259                         tmp_strinfo.rtp_stats.first_packet = TRUE;
260                         tmp_strinfo.rtp_stats.max_delta = 0;
261                         tmp_strinfo.rtp_stats.max_jitter = 0;
262                         tmp_strinfo.rtp_stats.mean_jitter = 0;
263                         tmp_strinfo.rtp_stats.delta = 0;
264                         tmp_strinfo.rtp_stats.diff = 0;
265                         tmp_strinfo.rtp_stats.jitter = 0;
266                         tmp_strinfo.rtp_stats.bandwidth = 0;
267                         tmp_strinfo.rtp_stats.total_bytes = 0;
268                         tmp_strinfo.rtp_stats.bw_start_index = 0;
269                         tmp_strinfo.rtp_stats.bw_index = 0;
270                         tmp_strinfo.rtp_stats.timestamp = 0;
271                         tmp_strinfo.rtp_stats.max_nr = 0;
272                         tmp_strinfo.rtp_stats.total_nr = 0;
273                         tmp_strinfo.rtp_stats.sequence = 0;
274                         tmp_strinfo.rtp_stats.start_seq_nr = 0;
275                         tmp_strinfo.rtp_stats.stop_seq_nr = 0;
276                         tmp_strinfo.rtp_stats.cycles = 0;
277                         tmp_strinfo.rtp_stats.under = FALSE;
278                         tmp_strinfo.rtp_stats.start_time = 0;
279                         tmp_strinfo.rtp_stats.time = 0;
280                         tmp_strinfo.rtp_stats.reg_pt = PT_UNDEFINED;
281
282             /* Get the Setup frame number who set this RTP stream */
283             p_conv_data = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name("rtp"));
284             if (p_conv_data)
285                                 tmp_strinfo.setup_frame_number = p_conv_data->frame_number;
286             else
287                 tmp_strinfo.setup_frame_number = 0xFFFFFFFF;
288
289                         strinfo = g_malloc(sizeof(rtp_stream_info_t));
290                         *strinfo = tmp_strinfo;  /* memberwise copy of struct */
291                         tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
292                 }
293
294                 /* get RTP stats for the packet */
295                 rtp_packet_analyse(&(strinfo->rtp_stats), pinfo, rtpinfo);
296                 if (strinfo->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
297                         || strinfo->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
298                         strinfo->problem = TRUE;
299
300
301                 /* increment the packets counter for this stream */
302                 ++(strinfo->npackets);
303                 strinfo->stop_rel_sec = (guint32) pinfo->fd->rel_ts.secs;
304                 strinfo->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
305
306                 /* increment the packets counter of all streams */
307                 ++(tapinfo->npackets);
308                 
309                 return 1;  /* refresh output */
310         }
311         else if (tapinfo->mode == TAP_SAVE) {
312                 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
313                         /* XXX - what if rtpinfo->info_all_data_present is
314                            FALSE, so that we don't *have* all the data? */
315                         sample.header.rec_time = 
316                                 (pinfo->fd->abs_ts.nsecs/1000 + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
317                                 + (guint32) (pinfo->fd->abs_ts.secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
318                         sample.header.frame_length = rtpinfo->info_data_len;
319                         sample.frame = rtpinfo->info_data;
320                         rtp_write_sample(&sample, tapinfo->save_file);
321                 }
322         }
323         else if (tapinfo->mode == TAP_MARK) {
324
325                 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
326                         || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
327                 {
328                         cf_mark_frame(&cfile, pinfo->fd);
329                 }
330         }
331
332         return 0;
333 }
334
335 /****************************************************************************/
336 /* scan for RTP streams */
337 void rtpstream_scan(void)
338 {
339         gboolean was_registered = the_tapinfo_struct.is_registered;
340         if (!the_tapinfo_struct.is_registered)
341                 register_tap_listener_rtp_stream();
342
343         the_tapinfo_struct.mode = TAP_ANALYSE;
344         cf_retap_packets(&cfile, FALSE);
345
346         if (!was_registered)
347                 remove_tap_listener_rtp_stream();
348 }
349
350
351 /****************************************************************************/
352 /* save rtp dump of stream_fwd */
353 gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
354 {
355         gboolean was_registered = the_tapinfo_struct.is_registered;
356         /* open file for saving */
357         the_tapinfo_struct.save_file = eth_fopen(filename, "wb");
358         if (the_tapinfo_struct.save_file==NULL) {
359                 open_failure_alert_box(filename, errno, TRUE);
360                 return FALSE;
361         }
362
363         rtp_write_header(stream, the_tapinfo_struct.save_file);
364         if (ferror(the_tapinfo_struct.save_file)) {
365                 write_failure_alert_box(filename, errno);
366                 fclose(the_tapinfo_struct.save_file);
367                 return FALSE;
368         }
369
370         if (!the_tapinfo_struct.is_registered)
371                 register_tap_listener_rtp_stream();
372
373         the_tapinfo_struct.mode = TAP_SAVE;
374         the_tapinfo_struct.filter_stream_fwd = stream;
375         cf_retap_packets(&cfile, FALSE);
376         the_tapinfo_struct.mode = TAP_ANALYSE;
377
378         if (!was_registered)
379                 remove_tap_listener_rtp_stream();
380
381         if (ferror(the_tapinfo_struct.save_file)) {
382                 write_failure_alert_box(filename, errno);
383                 fclose(the_tapinfo_struct.save_file);
384                 return FALSE;
385         }
386
387         if (fclose(the_tapinfo_struct.save_file) == EOF) {
388                 write_failure_alert_box(filename, errno);
389                 return FALSE;
390         }
391         return TRUE;
392 }
393
394
395 /****************************************************************************/
396 /* mark packets in stream_fwd or stream_rev */
397 void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
398 {
399         gboolean was_registered = the_tapinfo_struct.is_registered;
400         if (!the_tapinfo_struct.is_registered)
401                 register_tap_listener_rtp_stream();
402
403         the_tapinfo_struct.mode = TAP_MARK;
404         the_tapinfo_struct.filter_stream_fwd = stream_fwd;
405         the_tapinfo_struct.filter_stream_rev = stream_rev;
406         cf_retap_packets(&cfile, FALSE);
407         the_tapinfo_struct.mode = TAP_ANALYSE;
408
409         if (!was_registered)
410                 remove_tap_listener_rtp_stream();
411 }
412
413
414 /****************************************************************************/
415 const rtpstream_tapinfo_t* rtpstream_get_info(void)
416 {
417         return &the_tapinfo_struct;
418 }
419
420
421 /****************************************************************************/
422 /* TAP INTERFACE */
423 /****************************************************************************/
424
425 /* XXX just copied from gtk/rpc_stat.c */
426 void protect_thread_critical_region(void);
427 void unprotect_thread_critical_region(void);
428
429 /****************************************************************************/
430 void
431 remove_tap_listener_rtp_stream(void)
432 {
433         if (the_tapinfo_struct.is_registered) {
434                 protect_thread_critical_region();
435                 remove_tap_listener(&the_tapinfo_struct);
436                 unprotect_thread_critical_region();
437
438                 the_tapinfo_struct.is_registered = FALSE;
439         }
440 }
441
442
443 /****************************************************************************/
444 void
445 register_tap_listener_rtp_stream(void)
446 {
447         GString *error_string;
448
449         if (!the_tapinfo_struct.is_registered) {
450                 error_string = register_tap_listener("rtp", &the_tapinfo_struct,
451                         NULL, rtpstream_reset_cb, rtpstream_packet,
452                         rtpstream_draw);
453
454                 if (error_string != NULL) {
455                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
456                                       error_string->str);
457                         g_string_free(error_string, TRUE);
458                         exit(1);
459                 }
460
461                 the_tapinfo_struct.is_registered = TRUE;
462         }
463 }