diff options
author | Guy Harris <guy@alum.mit.edu> | 2003-09-24 07:48:12 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2003-09-24 07:48:12 +0000 |
commit | 14d13e3401e0c34c139c51f5b2c716960e3a0ab2 (patch) | |
tree | 59cbbebea2e81544b80b9cc33c7fff51a9be14a6 /gtk/rtp_stream.c | |
parent | 959850bf1626b5b511959a2001eb32adbf94d37d (diff) |
From Lars Ruoff: rewritten RTP analysis module.
svn path=/trunk/; revision=8529
Diffstat (limited to 'gtk/rtp_stream.c')
-rw-r--r-- | gtk/rtp_stream.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/gtk/rtp_stream.c b/gtk/rtp_stream.c new file mode 100644 index 0000000000..b662cb6db3 --- /dev/null +++ b/gtk/rtp_stream.c @@ -0,0 +1,407 @@ +/* rtp_stream.c + * RTP streams summary addition for ethereal + * + * $Id: rtp_stream.c,v 1.1 2003/09/24 07:48:11 guy Exp $ + * + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff <lars.ruoff@gmx.net> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "rtp_stream.h" +#include "rtp_stream_dlg.h" + +#include "globals.h" + +#include "tap.h" +#include "register.h" +#include "packet-rtp.h" + +#include "simple_dialog.h" + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <string.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + + +/****************************************************************************/ +/* the one and only global rtpstream_tapinfo_t structure */ +static rtpstream_tapinfo_t the_tapinfo_struct = + {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE}; + + +/****************************************************************************/ +/* GCompareFunc style comparison function for _rtp_stream_info */ +gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb) +{ + const struct _rtp_stream_info* a = aa; + const struct _rtp_stream_info* b = bb; + + if (a==b) + return 0; + if (a==NULL || b==NULL) + return 1; + if ((a->src_addr == b->src_addr) + && (a->src_port == b->src_port) + && (a->dest_addr == b->dest_addr) + && (a->dest_port == b->dest_port) + && (a->ssrc == b->ssrc) + && (a->pt == b->pt)) + return 0; + else + return 1; +} + + +/****************************************************************************/ +/* when there is a [re]reading of packet's */ +void rtpstream_reset(rtpstream_tapinfo_t *tapinfo _U_) +{ + 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; + } + + ++(tapinfo->launch_count); + + return; +} + +/****************************************************************************/ +/* redraw the output */ +void rtpstream_draw(rtpstream_tapinfo_t *tapinfo _U_) +{ +/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments + gtk_signal_emit_by_name(top_level, "signal_rtpstream_update"); +*/ + rtpstream_dlg_update(the_tapinfo_struct.strinfo_list); + return; +} + + +/* +* rtpdump file format +* +* The file starts with the tool to be used for playing this file, +* the multicast/unicast receive address and the port. +* +* #!rtpplay1.0 224.2.0.1/3456\n +* +* This is followed by one binary header (RD_hdr_t) and one RD_packet_t +* structure for each received packet. All fields are in network byte +* order. We don't need the source IP address since we can do mapping +* based on SSRC. This saves (a little) space, avoids non-IPv4 +* problems and privacy/security concerns. The header is followed by +* the RTP/RTCP header and (optionally) the actual payload. +*/ + +#define RTPFILE_VERSION "1.0" + +/* +* Write a header to the current output file. +* The header consists of an identifying string, followed +* by a binary structure. +*/ +static void rtp_write_header(rtp_stream_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) */ + guint16 port; /* UDP port */ + guint16 padding; /* 2 padding bytes */ + + fprintf(file, "#!rtpplay%s %s/%d\n", RTPFILE_VERSION, + ip_to_str((guint8*) &strinfo->dest_addr), + strinfo->dest_port); + + start_sec = g_htonl(strinfo->start_sec); + start_usec = g_htonl(strinfo->start_usec); + source = strinfo->src_addr; /* already is in network order */ + port = g_htons(strinfo->src_port); + padding = 0; + + fwrite(&start_sec, 4, 1, file); + fwrite(&start_usec, 4, 1, file); + fwrite(&source, 4, 1, file); + fwrite(&port, 2, 1, file); + fwrite(&padding, 2, 1, file); +} + +/* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/ +static void rtp_write_sample(rtp_sample_t* sample, 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(sample->header.frame_length + 8); + plen = g_htons(sample->header.frame_length); + offset = g_htonl(sample->header.rec_time); + + fwrite(&length, 2, 1, file); + fwrite(&plen, 2, 1, file); + fwrite(&offset, 4, 1, file); + fwrite(sample->frame, sample->header.frame_length, 1, file); +} + + +/* utility function for writing a sample to file in RAS format */ +static +void ras_write_sample(rtp_sample_t* sample, FILE* file) +{ + rtp_sample_header_t net_order_header; + + net_order_header.rec_time = g_htonl(sample->header.rec_time); + net_order_header.frame_length = g_htons(sample->header.frame_length); + + fwrite(&(net_order_header.rec_time), 1, 4, file); + fwrite(&(net_order_header.frame_length), 1, 2, file); + fwrite(sample->frame, 1, sample->header.frame_length, file); +} + + +/****************************************************************************/ +/* whenever a RTP packet is seen by the tap listener */ +int rtpstream_packet(rtpstream_tapinfo_t *tapinfo _U_, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo _U_) +{ + rtp_stream_info_t tmp_strinfo; + rtp_stream_info_t *strinfo = NULL; + GList* list; + + rtp_sample_t sample; + + /* we ignore packets that are not displayed */ +/* + if (pinfo->fd->flags.passed_dfilter == 0) + return 0; +*/ + + /* gather infos on the stream this packet is part of */ + g_memmove(&(tmp_strinfo.src_addr), pinfo->src.data, 4); + tmp_strinfo.src_port = pinfo->srcport; + g_memmove(&(tmp_strinfo.dest_addr), pinfo->dst.data, 4); + tmp_strinfo.dest_port = pinfo->destport; + tmp_strinfo.ssrc = rtpinfo->info_sync_src; + tmp_strinfo.pt = rtpinfo->info_payload_type; + + if (tapinfo->mode == TAP_ANALYSE) { + /* check wether 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(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0) + { + strinfo = (rtp_stream_info_t*)(list->data); /*found!*/ + break; + } + list = g_list_next(list); + } + + /* not in the list? then create a new entry */ + if (!strinfo) { + tmp_strinfo.npackets = 0; + tmp_strinfo.first_frame_num = pinfo->fd->num; + tmp_strinfo.start_sec = pinfo->fd->abs_secs; + tmp_strinfo.start_usec = pinfo->fd->abs_usecs; + tmp_strinfo.tag_vlan_error = 0; + tmp_strinfo.tag_diffserv_error = 0; + tmp_strinfo.vlan_id = 0; + strinfo = g_malloc(sizeof(rtp_stream_info_t)); + *strinfo = tmp_strinfo; /* memberwise copy of struct */ + tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo); + } + + /* increment the packets counter for this stream */ + ++(strinfo->npackets); + + /* increment the packets counter of all streams */ + ++(tapinfo->npackets); + } + else if (tapinfo->mode == TAP_SAVE) { + if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) { +/* sample.header.rec_time = pinfo->fd->abs_secs*1000 + pinfo->fd->abs_usecs;*/ + sample.header.rec_time = + (pinfo->fd->abs_usecs + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000 + + (pinfo->fd->abs_secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000; + sample.header.frame_length = rtpinfo->info_data_len; + sample.frame = cfile.pd + pinfo->fd->pkt_len - rtpinfo->info_data_len; + rtp_write_sample(&sample, tapinfo->save_file); +/* ras_write_sample(&sample, tapinfo->save_file);*/ + } + } + else if (tapinfo->mode == TAP_MARK) { + if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0 + || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0) + { + mark_frame(&cfile, pinfo->fd); + } + } + + return 0; +} + +/****************************************************************************/ +/* scan for RTP streams */ +void rtpstream_scan() +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + if (!the_tapinfo_struct.is_registered) + register_tap_listener_rtp_stream(); + + the_tapinfo_struct.mode = TAP_ANALYSE; + redissect_packets(&cfile); + + if (!was_registered) + remove_tap_listener_rtp_stream(); +} + + +/****************************************************************************/ +/* save rtp dump of stream_fwd */ +void rtpstream_save(rtp_stream_info_t* stream, const gchar *filename) +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + /* open file for saving */ + the_tapinfo_struct.save_file = fopen(filename, "wb"); + if (the_tapinfo_struct.save_file==NULL) { + return; + } + + rtp_write_header(stream, the_tapinfo_struct.save_file); + + if (!the_tapinfo_struct.is_registered) + register_tap_listener_rtp_stream(); + + the_tapinfo_struct.mode = TAP_SAVE; + the_tapinfo_struct.filter_stream_fwd = stream; + redissect_packets(&cfile); + the_tapinfo_struct.mode = TAP_ANALYSE; + + if (!was_registered) + remove_tap_listener_rtp_stream(); + + /* XXX check for error at fclose? */ + fclose(the_tapinfo_struct.save_file); +} + + +/****************************************************************************/ +/* mark packets in stream_fwd or stream_rev */ +void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev) +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + if (!the_tapinfo_struct.is_registered) + register_tap_listener_rtp_stream(); + + the_tapinfo_struct.mode = TAP_MARK; + the_tapinfo_struct.filter_stream_fwd = stream_fwd; + the_tapinfo_struct.filter_stream_rev = stream_rev; + redissect_packets(&cfile); + the_tapinfo_struct.mode = TAP_ANALYSE; + + if (!was_registered) + remove_tap_listener_rtp_stream(); +} + + +/****************************************************************************/ +const rtpstream_tapinfo_t* rtpstream_get_info() +{ + return &the_tapinfo_struct; +} + + +/****************************************************************************/ +/* TAP INTERFACE */ +/****************************************************************************/ + +/****************************************************************************/ +static void +rtpstream_init_tap(char *dummy _U_) +{ + /* XXX: never called? */ +} + + +/* XXX just copied from gtk/rpc_stat.c */ +void protect_thread_critical_region(void); +void unprotect_thread_critical_region(void); + +/****************************************************************************/ +void +remove_tap_listener_rtp_stream(void) +{ + if (the_tapinfo_struct.is_registered) { + protect_thread_critical_region(); + remove_tap_listener(&the_tapinfo_struct); + unprotect_thread_critical_region(); + + the_tapinfo_struct.is_registered = FALSE; + } +} + + +/****************************************************************************/ +void +register_tap_listener_rtp_stream(void) +{ + gchar filter_text[256]; + GString *error_string; + + if (!the_tapinfo_struct.is_registered) { + register_ethereal_tap("rtp", rtpstream_init_tap); + + sprintf(filter_text, "rtp && ip && !icmp"); + + error_string = register_tap_listener("rtp", &the_tapinfo_struct, + filter_text, + (void*)rtpstream_reset, (void*)rtpstream_packet, (void*)rtpstream_draw); + + if (error_string != NULL) { + simple_dialog(ESD_TYPE_WARN | ESD_TYPE_MODAL, NULL, error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + the_tapinfo_struct.is_registered = TRUE; + } +} |