diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 8e26b978b0..4da0b7ba31 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -89,6 +89,8 @@ noinst_HEADERS = \ rtp_analysis.h \ rtp_stream.h \ rtp_stream_dlg.h \ + mcast_stream.h \ + mcast_stream_dlg.h \ sat.h \ sctp_stat.h \ service_response_time_table.h \ diff --git a/gtk/Makefile.common b/gtk/Makefile.common index 2279a33503..59a58009e6 100644 --- a/gtk/Makefile.common +++ b/gtk/Makefile.common @@ -89,6 +89,7 @@ ETHEREAL_GTK_SRC = \ range_utils.c \ recent.c \ rtp_stream.c \ + mcast_stream.c \ sctp_stat.c \ sctp_graph_dlg.c \ sctp_byte_graph_dlg.c \ @@ -157,6 +158,7 @@ ETHEREAL_TAP_SRC = \ rpc_stat.c \ rtp_analysis.c \ rtp_stream_dlg.c \ + mcast_stream_dlg.c \ stats_tree_stat.c \ sctp_assoc_analyse.c \ sctp_chunk_stat_dlg.c \ diff --git a/gtk/mcast_stream.c b/gtk/mcast_stream.c new file mode 100644 index 0000000000..5ccd2cca01 --- /dev/null +++ b/gtk/mcast_stream.c @@ -0,0 +1,450 @@ +/* mcast_stream.c + * + * Copyright 2006, Iskratel , Slovenia + * By Jakob Bratkovic and + * Miha Jemec + * + * based on rtp_stream.c + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 "mcast_stream.h" +#include "mcast_stream_dlg.h" + +#include "globals.h" + +#include +#include "register.h" + +#include "alert_box.h" +#include "simple_dialog.h" +#include "file_util.h" +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#include +#include + +gint32 trigger=50; /* limit for triggering the burst alarm (in packets per second) */ +gint32 bufferalarm = 10000; /* limit for triggernig the buffer alarm (in bytes) */ +guint16 burstint = 100; /* burts interval in ms */ +gint32 emptyspeed = 5000; /* outgoing speed for single stream (kbps)*/ +gint32 cumulemptyspeed = 100000; /* outgoiong speed for all streams (kbps)*/ + +t_buffer **bufflist; + +/* sliding window and buffer usage */ +gint32 buffsize = (int)((double)MAX_SPEED * 100 / 1000) * 2;; +guint16 comparetimes(struct timeval *t1, struct timeval *t2, guint16 burstint); +static void buffusagecalc(mcast_stream_info_t *strinfo, packet_info *pinfo, double emptyspeed); +static void slidingwindow(mcast_stream_info_t *strinfo, packet_info *pinfo); + + +/****************************************************************************/ +/* the one and only global mcaststream_tapinfo_t structure */ +static mcaststream_tapinfo_t the_tapinfo_struct = + {0, NULL, 0, NULL, 0, FALSE}; + + +/****************************************************************************/ +/* GCompareFunc style comparison function for _mcast_stream_info */ +static gint mcast_stream_info_cmp(gconstpointer aa, gconstpointer bb) +{ + const struct _mcast_stream_info* a = aa; + const struct _mcast_stream_info* b = 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)) + return 0; + else + return 1; + +} + + +/****************************************************************************/ +/* when there is a [re]reading of packet's */ +void mcaststream_reset(mcaststream_tapinfo_t *tapinfo) +{ + GList* list; + + /* free the data items first */ + list = g_list_first(tapinfo->strinfo_list); + while (list) + { + /* XYZ I don't know how to clean this */ + /*g_free(list->element.buff); */ + g_free(list->data); + list = g_list_next(list); + } + g_list_free(tapinfo->strinfo_list); + tapinfo->strinfo_list = NULL; + + /* XYZ and why does the line below causes a crach? */ + /*g_free(tapinfo->allstreams->element.buff);*/ + g_free(tapinfo->allstreams); + tapinfo->allstreams = NULL; + + tapinfo->nstreams = 0; + tapinfo->npackets = 0; + + ++(tapinfo->launch_count); + + return; +} + +static void mcaststream_reset_cb(void *arg) +{ + mcaststream_reset(arg); +} + +/****************************************************************************/ +/* redraw the output */ +static void mcaststream_draw(void *arg _U_) +{ +/* XXX: see mcaststream_on_update in mcast_streams_dlg.c for comments + gtk_signal_emit_by_name(top_level, "signal_mcaststream_update"); +*/ + mcaststream_dlg_update(the_tapinfo_struct.strinfo_list); + return; +} + + + +/****************************************************************************/ +/* whenever a udp packet is seen by the tap listener */ +static int mcaststream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2 _U_) +{ + mcaststream_tapinfo_t *tapinfo = arg; + mcast_stream_info_t tmp_strinfo; + mcast_stream_info_t *strinfo = NULL; + GList* list; + float deltatime; + + /* gather infos on the stream this packet is part of */ + COPY_ADDRESS(&(tmp_strinfo.src_addr), &(pinfo->src)); + tmp_strinfo.src_port = pinfo->srcport; + COPY_ADDRESS(&(tmp_strinfo.dest_addr), &(pinfo->dst)); + tmp_strinfo.dest_port = pinfo->destport; + + /* first we ignore non multicast packets; we filter out only those ethernet packets + * which start with the 01:00:5E multicast address */ + if (strncmp("01:00:5e", g_strdup(get_addr_name(&(pinfo->dl_dst))), 8) != 0) + return 0; + + /* check wether we already have a stream with these parameters in the list */ + list = g_list_first(tapinfo->strinfo_list); + while (list) + { + if (mcast_stream_info_cmp(&tmp_strinfo, (mcast_stream_info_t*)(list->data))==0) + { + strinfo = (mcast_stream_info_t*)(list->data); /*found!*/ + break; + } + list = g_list_next(list); + } + + /* not in the list? then create a new entry */ + if (!strinfo) { + /*printf("nov sip %s sp %d dip %s dp %d\n", g_strdup(get_addr_name(&(pinfo->src))), + pinfo->srcport, g_strdup(get_addr_name(&(pinfo->dst))), pinfo->destport);*/ + tmp_strinfo.npackets = 0; + tmp_strinfo.apackets = 0; + tmp_strinfo.first_frame_num = pinfo->fd->num; + tmp_strinfo.start_sec = pinfo->fd->abs_ts.secs; + tmp_strinfo.start_usec = pinfo->fd->abs_ts.nsecs/1000; + tmp_strinfo.start_rel_sec = pinfo->fd->rel_ts.secs; + tmp_strinfo.start_rel_usec = pinfo->fd->rel_ts.nsecs/1000; + tmp_strinfo.vlan_id = 0; + + /* reset Mcast stats */ + tmp_strinfo.average_bw = 0; + tmp_strinfo.total_bytes = 0; + + /* reset slidingwindow and buffer parameters */ + tmp_strinfo.element.buff = (struct timeval *)g_malloc(buffsize * sizeof(struct timeval)); + tmp_strinfo.element.first=0; + tmp_strinfo.element.last=0; + tmp_strinfo.element.burstsize=1; + tmp_strinfo.element.topburstsize=1; + tmp_strinfo.element.numbursts=0; + tmp_strinfo.element.burststatus=0; + tmp_strinfo.element.count=1; + tmp_strinfo.element.buffusage=pinfo->fd->pkt_len; + tmp_strinfo.element.topbuffusage=pinfo->fd->pkt_len; + tmp_strinfo.element.numbuffalarms=0; + tmp_strinfo.element.buffstatus=0; + tmp_strinfo.element.maxbw=0; + + strinfo = g_malloc(sizeof(mcast_stream_info_t)); + *strinfo = tmp_strinfo; /* memberwise copy of struct */ + tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo); + strinfo->element.buff = (struct timeval *)g_malloc(buffsize * sizeof(struct timeval)); + + /* set time with the first packet */ + if (tapinfo->npackets == 0) { + tapinfo->allstreams = g_malloc(sizeof(mcast_stream_info_t)); + tapinfo->allstreams->element.buff = + (struct timeval *)g_malloc(buffsize * sizeof(struct timeval)); + tapinfo->allstreams->start_rel_sec = pinfo->fd->rel_ts.secs; + tapinfo->allstreams->start_rel_usec = pinfo->fd->rel_ts.nsecs/1000; + tapinfo->allstreams->total_bytes = 0; + tapinfo->allstreams->element.first=0; + tapinfo->allstreams->element.last=0; + tapinfo->allstreams->element.burstsize=1; + tapinfo->allstreams->element.topburstsize=1; + tapinfo->allstreams->element.numbursts=0; + tapinfo->allstreams->element.burststatus=0; + tapinfo->allstreams->element.count=1; + tapinfo->allstreams->element.buffusage=pinfo->fd->pkt_len; + tapinfo->allstreams->element.topbuffusage=pinfo->fd->pkt_len; + tapinfo->allstreams->element.numbuffalarms=0; + tapinfo->allstreams->element.buffstatus=0; + tapinfo->allstreams->element.maxbw=0; + } + } + + /* time between first and last packet in the group */ + strinfo->stop_rel_sec = pinfo->fd->rel_ts.secs; + strinfo->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000; + deltatime = ((float)((strinfo->stop_rel_sec * 1000000 + strinfo->stop_rel_usec) + - (strinfo->start_rel_sec*1000000 + strinfo->start_rel_usec)))/1000000; + + /* calculate average bandwidth for this stream */ + strinfo->total_bytes = strinfo->total_bytes + pinfo->fd->pkt_len; + if (deltatime > 0) + strinfo->average_bw = (((float)(strinfo->total_bytes*8) / deltatime) / 1000000); + + /* increment the packets counter for this stream and calculate average pps */ + ++(strinfo->npackets); + strinfo->apackets = strinfo->npackets / deltatime; + + /* time between first and last packet in any group */ + tapinfo->allstreams->stop_rel_sec = pinfo->fd->rel_ts.secs; + tapinfo->allstreams->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000; + deltatime = ((float)((tapinfo->allstreams->stop_rel_sec * 1000000 + tapinfo->allstreams->stop_rel_usec) + - (tapinfo->allstreams->start_rel_sec*1000000 + tapinfo->allstreams->start_rel_usec)))/1000000; + + /* increment the packets counter of all streams */ + ++(tapinfo->npackets); + + /* calculate average bandwidth for all streams */ + tapinfo->allstreams->total_bytes = tapinfo->allstreams->total_bytes + pinfo->fd->pkt_len; + if (deltatime > 0) + tapinfo->allstreams->average_bw = (((float)(tapinfo->allstreams->total_bytes *8) / deltatime) / 1000000); + + /* sliding window and buffercalc for this group*/ + slidingwindow(strinfo, pinfo); + buffusagecalc(strinfo, pinfo, emptyspeed*1000); + /* sliding window and buffercalc for all groups */ + slidingwindow(tapinfo->allstreams, pinfo); + buffusagecalc(tapinfo->allstreams, pinfo, cumulemptyspeed*1000); + /* end of sliding window */ + + return 1; /* refresh output */ + +} + +/****************************************************************************/ +/* scan for Mcast streams */ +void mcaststream_scan(void) +{ + gboolean was_registered = the_tapinfo_struct.is_registered; + if (!the_tapinfo_struct.is_registered) + register_tap_listener_mcast_stream(); + + cf_retap_packets(&cfile, FALSE); + + if (!was_registered) + remove_tap_listener_mcast_stream(); +} + + +/****************************************************************************/ +const mcaststream_tapinfo_t* mcaststream_get_info(void) +{ + return &the_tapinfo_struct; +} + + +/****************************************************************************/ +/* TAP INTERFACE */ +/****************************************************************************/ + +/* XXX just copied from gtk/rpc_stat.c */ +void protect_thread_critical_region(void); +void unprotect_thread_critical_region(void); + +/****************************************************************************/ +void +remove_tap_listener_mcast_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_mcast_stream(void) +{ + GString *error_string; + if (!the_tapinfo_struct.is_registered) { + error_string = register_tap_listener("udp", &the_tapinfo_struct, + NULL, mcaststream_reset_cb, mcaststream_packet, + mcaststream_draw); + + if (error_string != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + the_tapinfo_struct.is_registered = TRUE; + } +} + +/*******************************************************************************/ +/* sliding window and buffer calculations */ + +/* compare two times */ +guint16 comparetimes(struct timeval *t1, struct timeval *t2, guint16 burstint){ + if(((t2->tv_sec - t1->tv_sec)*1000 + (t2->tv_usec - t1->tv_usec)/1000) > burstint){ + return 1; + } else{ + return 0; + } +} + +/* calculate buffer usage */ +void buffusagecalc(mcast_stream_info_t *strinfo, packet_info *pinfo, double emptyspeed) +{ + gint32 sec=0, usec=0, cur, prev; + struct timeval *buffer; + double timeelapsed; + + buffer = strinfo->element.buff; + cur = strinfo->element.last; + if(cur == 0){ + cur = buffsize - 1; + prev = cur - 1; + } else if(cur == 1){ + prev = buffsize - 1; + cur = 0; + } else{ + cur=cur-1; + prev=cur-1; + } + + sec = buffer[cur].tv_sec - buffer[prev].tv_sec; + usec = buffer[cur].tv_usec - buffer[prev].tv_usec; + timeelapsed = (double)usec/1000000 + (double)sec; + + /* bytes added to buffer */ + strinfo->element.buffusage+=pinfo->fd->pkt_len; + + /* bytes cleared from buffer */ + strinfo->element.buffusage-=(timeelapsed * emptyspeed / 8); + + if(strinfo->element.buffusage < 0) strinfo->element.buffusage=0; + if(strinfo->element.buffusage > strinfo->element.topbuffusage) + strinfo->element.topbuffusage = strinfo->element.buffusage; + /* check for buffer losses */ + if((strinfo->element.buffusage >= bufferalarm) && (strinfo->element.buffstatus == 0)){ + strinfo->element.buffstatus = 1; + strinfo->element.numbuffalarms++; + } else if(strinfo->element.buffusage < bufferalarm){ + strinfo->element.buffstatus = 0; + } + + return; +} + +/* sliding window calculation */ +void slidingwindow(mcast_stream_info_t *strinfo, packet_info *pinfo) +{ + struct timeval *buffer; + gint32 diff; + + buffer = strinfo->element.buff; + + diff = strinfo->element.last - strinfo->element.first; + if(diff < 0) diff+=buffsize; + + /* check if buffer is full */ + if(diff >= (buffsize - 2)){ + fprintf(stderr, "Warning: capture buffer full\n"); + strinfo->element.first++; + if(strinfo->element.first >= buffsize) strinfo->element.first = strinfo->element.first % buffsize; + } + + /* burst count */ + buffer[strinfo->element.last].tv_sec = pinfo->fd->rel_ts.secs; + buffer[strinfo->element.last].tv_usec = pinfo->fd->rel_ts.nsecs/1000; + while(comparetimes((struct timeval *)&(buffer[strinfo->element.first]), + (struct timeval *)&(buffer[strinfo->element.last]), burstint)){ + strinfo->element.first++; + if(strinfo->element.first >= buffsize) strinfo->element.first = strinfo->element.first % buffsize; + diff--; + } + strinfo->element.burstsize = diff; + if(strinfo->element.burstsize > strinfo->element.topburstsize) { + strinfo->element.topburstsize = strinfo->element.burstsize; + strinfo->element.maxbw = (float)(strinfo->element.topburstsize) * 1000 / burstint * pinfo->fd->pkt_len * 8 / 1000000; + } + + strinfo->element.last++; + if(strinfo->element.last >= buffsize) strinfo->element.last = strinfo->element.last % buffsize; + /* trigger check */ + if((strinfo->element.burstsize >= trigger) && (strinfo->element.burststatus == 0)){ + strinfo->element.burststatus = 1; + strinfo->element.numbursts++; + } else if(strinfo->element.burstsize < trigger){ + strinfo->element.burststatus = 0; + } + + strinfo->element.count++; +} + diff --git a/gtk/mcast_stream.h b/gtk/mcast_stream.h new file mode 100644 index 0000000000..a79d165db7 --- /dev/null +++ b/gtk/mcast_stream.h @@ -0,0 +1,146 @@ +/* mcast_stream.h + * + * Copyright 2006, Iskratel , Slovenia + * By Jakob Bratkovic and + * Miha Jemec + * + * based on rtp_stream.h + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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. + */ + +#ifndef Mcast_STREAM_H_INCLUDED +#define Mcast_STREAM_H_INCLUDED + +#include +#include +#include + +/** @file + * ??? + * @ingroup dialog_group + * @todo what's this? + */ + +#define INTERFACE 2 +#define FILTER 3 +#define TRIGGER 4 +#define TIMER 5 +#define REFRESHTIMER 6 +#define EMPTYSPEED 7 +#define BUFFERALARM 8 +#define CUMULEMPTYSPEED 9 + +#define MAX_SPEED 200000 + +/* typedefs for sliding window and buffer size */ +typedef struct buffer{ + struct timeval *buff; /* packet times */ + gint32 first; /* pointer to the first element */ + gint32 last; /* pointer to the last element */ + gint32 burstsize; /* current burst */ + gint32 topburstsize; /* maximum burst in the refresh interval*/ + gint32 count; /* packet counter */ + gint32 burststatus; /* burst status */ + gint32 numbursts; /* number of bursts */ + gint32 buffusage; /* buffer usage */ + gint32 buffstatus; /* buffer status */ + gint32 numbuffalarms; /* number of alarms triggered by buffer underruns */ + gint32 topbuffusage; /* top buffer usage in refresh interval */ + float maxbw; /* maximum bandwidth usage */ +} t_buffer; + + +/* defines an mcast stream */ +typedef struct _mcast_stream_info { + address src_addr; + guint16 src_port; + address dest_addr; + guint16 dest_port; + guint32 npackets; + guint32 apackets; + guint32 total_bytes; + float average_bw; + + guint32 first_frame_num; /* frame number of first frame */ + /* start of recording (GMT) of this stream */ + guint32 start_sec; /* seconds */ + guint32 start_usec; /* microseconds */ + guint32 start_rel_sec; /* start stream rel seconds */ + guint32 start_rel_usec; /* start stream rel microseconds */ + guint32 stop_rel_sec; /* stop stream rel seconds */ + guint32 stop_rel_usec; /* stop stream rel microseconds */ + guint16 vlan_id; + + /*for the sliding window */ + t_buffer element; + +} mcast_stream_info_t; + + +/* structure that holds the information about all detected streams */ +/* struct holding all information of the tap */ +typedef struct _mcaststream_tapinfo { + int nstreams; /* number of streams in the list */ + GList* strinfo_list; /* list with all streams */ + guint32 npackets; /* total number of mcast packets of all streams */ + mcast_stream_info_t* allstreams; /* structure holding information common for all streams */ + + guint32 launch_count; /* number of times the tap has been run */ + gboolean is_registered; /* if the tap listener is currently registered or not */ +} mcaststream_tapinfo_t; + +/****************************************************************************/ +/* INTERFACE */ + +/* +* Registers the mcast_streams tap listener (if not already done). +* From that point on, the Mcast streams list will be updated with every redissection. +* This function is also the entry point for the initialization routine of the tap system. +* So whenever mcast_stream.c is added to the list of ETHEREAL_TAP_SRCs, the tap will be registered on startup. +* If not, it will be registered on demand by the mcast_streams and mcast_analysis functions that need it. +*/ +void register_tap_listener_mcast_stream(void); + +/* +* Removes the mcast_streams tap listener (if not already done) +* From that point on, the Mcast streams list won't be updated any more. +*/ +void remove_tap_listener_mcast_stream(void); + +/* +* Retrieves a constant reference to the unique info structure of the mcast_streams tap listener. +* The user should not modify the data pointed to. +*/ +const mcaststream_tapinfo_t* mcaststream_get_info(void); + +/* +* Cleans up memory of mcast streams tap. +*/ +void mcaststream_reset(mcaststream_tapinfo_t *tapinfo); + +/* +* Scans all packets for Mcast streams and updates the Mcast streams list. +* (redissects all packets) +*/ +void mcaststream_scan(void); + +#endif /*Mcast_STREAM_H_INCLUDED*/ diff --git a/gtk/mcast_stream_dlg.c b/gtk/mcast_stream_dlg.c new file mode 100644 index 0000000000..5e96b4231f --- /dev/null +++ b/gtk/mcast_stream_dlg.c @@ -0,0 +1,689 @@ +/* mcast_stream_dlg.c + * + * Copyright 2006, Iskratel , Slovenia + * By Jakob Bratkovic and + * Miha Jemec + * + * based on rtp_stream_dlg.c + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 +#endif + +#include "mcast_stream_dlg.h" +#include "mcast_stream.h" + +#include "globals.h" +#include "epan/filesystem.h" + +#include "../stat_menu.h" +#include "gui_stat_menu.h" +#include "dlg_utils.h" +#include "gui_utils.h" +#include "compat_macros.h" +#include "gtkglobals.h" +#include "simple_dialog.h" + +#include "image/clist_ascend.xpm" +#include "image/clist_descend.xpm" + +#include + +#include +#include +#include + +/* Capture callback data keys */ +#define E_MCAST_ENTRY_1 "burst_interval" +#define E_MCAST_ENTRY_2 "burst_alarm" +#define E_MCAST_ENTRY_3 "buffer_alarm" +#define E_MCAST_ENTRY_4 "stream_speed" +#define E_MCAST_ENTRY_5 "total_speed" + +extern guint16 burstint; +extern guint32 trigger; +extern guint32 bufferalarm; +extern gint32 emptyspeed; +extern gint32 cumulemptyspeed; + +static const gchar FWD_LABEL_TEXT[] = "Select a stream with left mouse button"; +static const gchar PAR_LABEL_TEXT[] = "\nBurst int: ms Burst alarm: pps Buffer alarm: KB Stream empty speed: Mbps Total empty speed: Mbps\n"; + +/****************************************************************************/ +static GtkWidget *mcast_stream_dlg = NULL; +static GtkWidget *mcast_params_dlg = NULL; + +static GtkWidget *clist = NULL; +static GtkWidget *top_label = NULL; +static GtkWidget *label_fwd = NULL; +static GtkWidget *label_par = NULL; + +static mcast_stream_info_t* selected_stream_fwd = NULL; /* current selection */ +static GList *last_list = NULL; + +static guint32 streams_nb = 0; /* number of displayed streams */ + +#define NUM_COLS 12 +static const gchar *titles[NUM_COLS] = {"Src IP addr", "Src port", "Dst IP addr", "Dst port", "Packets", "Packets/s", "Awg Bw", "Max Bw", "Max burst", "Burst Alarms", "Max buffer", "Buff Alarms"}; + +/****************************************************************************/ +/* append a line to clist */ +static void add_to_clist(mcast_stream_info_t* strinfo) +{ + gchar label_text[256]; + gint added_row; + gchar *data[NUM_COLS]; + int i; + char *savelocale; + + /* save the current locale */ + savelocale = setlocale(LC_NUMERIC, NULL); + /* switch to "C" locale to avoid problems with localized decimal separators + in g_snprintf("%f") functions */ + setlocale(LC_NUMERIC, "C"); + data[0] = g_strdup(get_addr_name(&(strinfo->src_addr))); + data[1] = g_strdup_printf("%u", strinfo->src_port); + data[2] = g_strdup(get_addr_name(&(strinfo->dest_addr))); + data[3] = g_strdup_printf("%u", strinfo->dest_port); + data[4] = g_strdup_printf("%u", strinfo->npackets); + data[5] = g_strdup_printf("%u /s", strinfo->apackets); + data[6] = g_strdup_printf("%2.1f Mbps", strinfo->average_bw); + data[7] = g_strdup_printf("%2.1f Mbps", strinfo->element.maxbw); + data[8] = g_strdup_printf("%u / %dms", strinfo->element.topburstsize, burstint); + data[9] = g_strdup_printf("%u", strinfo->element.numbursts); + data[10] = g_strdup_printf("%.1f KB", (float)strinfo->element.topbuffusage/1000); + data[11] = g_strdup_printf("%u", strinfo->element.numbuffalarms); + + /* restore previous locale setting */ + setlocale(LC_NUMERIC, savelocale); + + added_row = gtk_clist_append(GTK_CLIST(clist), data); + for (i = 0; i < NUM_COLS; i++) + g_free(data[i]); + + /* set data pointer of last row to point to user data for that row */ + gtk_clist_set_row_data(GTK_CLIST(clist), added_row, strinfo); + + /* Update the top label with the number of detected streams */ + sprintf(label_text, + "Detected %d Multicast streams, Average Bw: %.1f Mbps Max Bw: %.1f Mbps Max burst: %d / %dms Max buffer: %.1f KB", + ++streams_nb, + mcaststream_get_info()->allstreams->average_bw, mcaststream_get_info()->allstreams->element.maxbw, + mcaststream_get_info()->allstreams->element.topburstsize, burstint, + (float)(mcaststream_get_info()->allstreams->element.topbuffusage)/1000); + gtk_label_set(GTK_LABEL(top_label), label_text); + + g_snprintf(label_text, 200, "\nBurst int: %u ms Burst alarm: %u pps Buffer alarm: %u Bytes Stream empty speed: %u Kbps Total empty speed: %u Kbps\n", + burstint, trigger, bufferalarm, emptyspeed, cumulemptyspeed); + gtk_label_set_text(GTK_LABEL(label_par), label_text); +} + +/****************************************************************************/ +/* CALLBACKS */ +/****************************************************************************/ +static void +mcaststream_on_destroy (GtkObject *object _U_, + gpointer user_data _U_) +{ + /* Remove the stream tap listener */ + remove_tap_listener_mcast_stream(); + + /* Is there a params window open? */ + if (mcast_params_dlg != NULL) + window_destroy(mcast_params_dlg); + + /* Clean up memory used by stream tap */ + mcaststream_reset((mcaststream_tapinfo_t*) mcaststream_get_info()); + + /* Note that we no longer have a "Mcast Streams" dialog box. */ + mcast_stream_dlg = NULL; +} + + +/****************************************************************************/ +static void +mcaststream_on_unselect (GtkButton *button _U_, + gpointer user_data _U_) +{ + selected_stream_fwd = NULL; + gtk_clist_unselect_all(GTK_CLIST(clist)); + gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT); +} + + +/****************************************************************************/ +static void +mcaststream_on_filter (GtkButton *button _U_, + gpointer user_data _U_) +{ + gchar *filter_string = NULL; + gchar *filter_string_fwd = NULL; + gchar ip_version[3]; + + if (selected_stream_fwd==NULL) + return; + + if (selected_stream_fwd) + { + if (selected_stream_fwd->src_addr.type==AT_IPv6){ + strcpy(ip_version,"v6"); + } + else{ + strcpy(ip_version,""); + } + filter_string_fwd = g_strdup_printf( + "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u)", + ip_version, + address_to_str(&(selected_stream_fwd->src_addr)), + selected_stream_fwd->src_port, + ip_version, + address_to_str(&(selected_stream_fwd->dest_addr)), + selected_stream_fwd->dest_port); + filter_string = filter_string_fwd; + } + + gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string); + g_free(filter_string); + +/* + main_filter_packets(&cfile, filter_string, FALSE); + mcaststream_dlg_update(mcaststream_get_info()->strinfo_list); +*/ +} + + +/****************************************************************************/ +/* when the user selects a row in the stream list */ +static void +mcaststream_on_select_row(GtkCList *clist, + gint row _U_, + gint column _U_, + GdkEventButton *event _U_, + gpointer user_data _U_) +{ + gchar label_text[80]; + + selected_stream_fwd = gtk_clist_get_row_data(GTK_CLIST(clist), row); + g_snprintf(label_text, 80, "Selected: %s:%u -> %s:%u", + get_addr_name(&(selected_stream_fwd->src_addr)), + selected_stream_fwd->src_port, + get_addr_name(&(selected_stream_fwd->dest_addr)), + selected_stream_fwd->dest_port + ); + gtk_label_set_text(GTK_LABEL(label_fwd), label_text); + +/* + gtk_widget_set_sensitive(filter_bt, TRUE); +*/ + /* TODO: activate other buttons when implemented */ +} + + +/****************************************************************************/ +typedef struct column_arrows { + GtkWidget *table; + GtkWidget *ascend_pm; + GtkWidget *descend_pm; +} column_arrows; + + +/****************************************************************************/ +static void +mcaststream_click_column_cb(GtkCList *clist, gint column, gpointer data) +{ + column_arrows *col_arrows = (column_arrows *) data; + int i; + + gtk_clist_freeze(clist); + + for (i=0; isort_column) { + if (clist->sort_type == GTK_SORT_ASCENDING) { + clist->sort_type = GTK_SORT_DESCENDING; + gtk_widget_show(col_arrows[column].descend_pm); + } else { + clist->sort_type = GTK_SORT_ASCENDING; + gtk_widget_show(col_arrows[column].ascend_pm); + } + } else { + clist->sort_type = GTK_SORT_ASCENDING; + gtk_widget_show(col_arrows[column].ascend_pm); + gtk_clist_set_sort_column(clist, column); + } + gtk_clist_thaw(clist); + + gtk_clist_sort(clist); +} + + +/****************************************************************************/ +static gint +mcaststream_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) +{ + char *text1 = NULL; + char *text2 = NULL; + int i1, i2; + + const GtkCListRow *row1 = (const GtkCListRow *) ptr1; + const GtkCListRow *row2 = (const GtkCListRow *) ptr2; + + text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text; + text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text; + + switch(clist->sort_column){ + case 0: + case 2: + return strcmp (text1, text2); + case 1: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + i1=atoi(text1); + i2=atoi(text2); + return i1-i2; + } + g_assert_not_reached(); + return 0; +} + + +/****************************************************************************/ +/* INTERFACE */ +/****************************************************************************/ +static void mcast_params_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_) +{ + /* Note that we no longer have a mcast params dialog box. */ + mcast_params_dlg = NULL; +} + + +static void +mcast_params_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w) +{ + GtkWidget *fnumber_te; + const gchar *fnumber_text; + gint32 fnumber; + char *p; + + fnumber_te = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_MCAST_ENTRY_1); + fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te)); + fnumber = strtoul(fnumber_text, &p, 10); + if ( (p == fnumber_text || *p != '\0') || (fnumber <=0) || (fnumber > 1000) ){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The burst interval should be between 1 and 1000 ms "); + return; } + burstint = fnumber; + + fnumber_te = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_MCAST_ENTRY_2); + fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te)); + fnumber = strtoul(fnumber_text, &p, 10); + if ( (p == fnumber_text || *p != '\0') || (fnumber <=0) ){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The burst alarm treshold you entered isn't valid."); + return; } + trigger = fnumber; + + fnumber_te = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_MCAST_ENTRY_3); + fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te)); + fnumber = strtoul(fnumber_text, &p, 10); + if ( (p == fnumber_text || *p != '\0') || (fnumber <=0) ){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The buffer alarm treshold you entered isn't valid."); + return; } + bufferalarm = fnumber; + + fnumber_te = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_MCAST_ENTRY_4); + fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te)); + fnumber = strtoul(fnumber_text, &p, 10); + if ( (p == fnumber_text || *p != '\0') || (fnumber <=0) || (fnumber > 10000000) ){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The stream empty speed should be between 1 and 10000000"); + return; } + emptyspeed = fnumber; + + fnumber_te = (GtkWidget *)OBJECT_GET_DATA(parent_w, E_MCAST_ENTRY_5); + fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te)); + fnumber = strtoul(fnumber_text, &p, 10); + if ( (p == fnumber_text || *p != '\0') || (fnumber <=0) || (fnumber > 10000000) ){ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The total empty speed should be between 1 and 10000000"); + return; } + cumulemptyspeed = fnumber; + + window_destroy(GTK_WIDGET(parent_w)); + + /* Clean up memory used by stream tap */ + mcaststream_reset((mcaststream_tapinfo_t*) mcaststream_get_info()); + /* retap all packets */ + cf_retap_packets(&cfile, FALSE); + +} + + + +static void +mcast_on_params (GtkButton *button _U_, + gpointer data _U_) +{ + GtkWidget *main_vb; + GtkWidget *label, *hbuttonbox, *table; + GtkWidget *ok_bt, *cancel_bt; + GtkWidget *entry1, *entry2, *entry3, *entry4, *entry5; + gchar label_text[51]; + + if (mcast_params_dlg != NULL) { + /* There's already a Params dialog box; reactivate it. */ + reactivate_window(mcast_params_dlg); + return; + } + + mcast_params_dlg = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Set parameters for Multicast Stream Analysis"); + gtk_window_set_default_size(GTK_WINDOW(mcast_params_dlg), 210, 210); + + gtk_widget_show(mcast_params_dlg); + + /* Container for each row of widgets */ + main_vb = gtk_vbox_new(FALSE, 3); + gtk_container_border_width(GTK_CONTAINER(main_vb), 2); + gtk_container_add(GTK_CONTAINER(mcast_params_dlg), main_vb); + gtk_widget_show(main_vb); + + table = gtk_table_new (6, 2, FALSE); + gtk_container_add (GTK_CONTAINER (main_vb), table); + + label = gtk_label_new(" Burst measurement interval (ms) "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); + entry1 = gtk_entry_new(); + g_snprintf(label_text, 50, "%u", burstint); + gtk_entry_set_text(GTK_ENTRY(entry1), label_text); + gtk_table_attach_defaults(GTK_TABLE(table), entry1, 1, 2, 0, 1); + label = gtk_label_new(" Burst alarm treshold (packets) "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); + entry2 = gtk_entry_new(); + g_snprintf(label_text, 50, "%u", trigger); + gtk_entry_set_text(GTK_ENTRY(entry2), label_text); + gtk_table_attach_defaults(GTK_TABLE(table), entry2, 1, 2, 1, 2); + label = gtk_label_new(" Buffer alarm treshold (bytes) "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); + entry3 = gtk_entry_new(); + g_snprintf(label_text, 50, "%u", bufferalarm); + gtk_entry_set_text(GTK_ENTRY(entry3), label_text); + gtk_table_attach_defaults(GTK_TABLE(table), entry3, 1, 2, 2, 3); + label = gtk_label_new(" Stream empty speed (kbit/s) "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); + entry4 = gtk_entry_new(); + g_snprintf(label_text, 50, "%u", emptyspeed); + gtk_entry_set_text(GTK_ENTRY(entry4), label_text); + gtk_table_attach_defaults(GTK_TABLE(table), entry4, 1, 2, 3, 4); + label = gtk_label_new(" Total empty speed (kbit/s) "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5); + entry5 = gtk_entry_new(); + g_snprintf(label_text, 50, "%u", cumulemptyspeed); + gtk_entry_set_text(GTK_ENTRY(entry5), label_text); + gtk_table_attach_defaults(GTK_TABLE(table), entry5, 1, 2, 4, 5); + + gtk_widget_show (table); + + /* button row */ + hbuttonbox = gtk_hbutton_box_new (); + gtk_table_attach_defaults(GTK_TABLE(table), hbuttonbox, 0, 2, 5, 6); + ok_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_OK); + gtk_container_add (GTK_CONTAINER (hbuttonbox), ok_bt); + cancel_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CANCEL); + gtk_container_add (GTK_CONTAINER (hbuttonbox), cancel_bt); + GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 0); + + SIGNAL_CONNECT(mcast_params_dlg, "delete_event", window_delete_event_cb, NULL); + SIGNAL_CONNECT(mcast_params_dlg, "destroy", mcast_params_destroy_cb, NULL); + SIGNAL_CONNECT(ok_bt, "clicked", mcast_params_ok_cb, mcast_params_dlg); + window_set_cancel_button(mcast_params_dlg, cancel_bt, window_cancel_button_cb); + + /* Attach pointers to needed widgets */ + OBJECT_SET_DATA(mcast_params_dlg, E_MCAST_ENTRY_1, entry1); + OBJECT_SET_DATA(mcast_params_dlg, E_MCAST_ENTRY_2, entry2); + OBJECT_SET_DATA(mcast_params_dlg, E_MCAST_ENTRY_3, entry3); + OBJECT_SET_DATA(mcast_params_dlg, E_MCAST_ENTRY_4, entry4); + OBJECT_SET_DATA(mcast_params_dlg, E_MCAST_ENTRY_5, entry5); + + gtk_widget_show_all(mcast_params_dlg); + window_present(mcast_params_dlg); +} + + + +static void mcaststream_dlg_create (void) +{ + GtkWidget *mcaststream_dlg_w; + GtkWidget *main_vb; + GtkWidget *scrolledwindow; + GtkWidget *hbuttonbox; + /*GtkWidget *bt_unselect;*/ + GtkWidget *bt_filter; + GtkWidget *bt_params; + GtkWidget *bt_close; + GtkTooltips *tooltips = gtk_tooltips_new(); + + column_arrows *col_arrows; + GtkWidget *column_lb; + int i; + + mcaststream_dlg_w = dlg_window_new("Ethereal: Multicast Streams"); + gtk_window_set_default_size(GTK_WINDOW(mcaststream_dlg_w), 620, 400); + + main_vb = gtk_vbox_new (FALSE, 0); + gtk_container_add(GTK_CONTAINER(mcaststream_dlg_w), main_vb); + gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12); + + top_label = gtk_label_new ("Detected 0 Multicast streams"); + gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8); + + scrolledwindow = scrolled_window_new (NULL, NULL); + gtk_box_pack_start (GTK_BOX (main_vb), scrolledwindow, TRUE, TRUE, 0); + + clist = gtk_clist_new (NUM_COLS); + gtk_container_add (GTK_CONTAINER (scrolledwindow), clist); + + gtk_clist_set_column_width (GTK_CLIST (clist), 0, 95); + gtk_clist_set_column_width (GTK_CLIST (clist), 1, 55); + gtk_clist_set_column_width (GTK_CLIST (clist), 2, 95); + gtk_clist_set_column_width (GTK_CLIST (clist), 3, 55); + gtk_clist_set_column_width (GTK_CLIST (clist), 4, 70); + gtk_clist_set_column_width (GTK_CLIST (clist), 5, 70); + gtk_clist_set_column_width (GTK_CLIST (clist), 6, 60); + gtk_clist_set_column_width (GTK_CLIST (clist), 7, 60); + gtk_clist_set_column_width (GTK_CLIST (clist), 8, 80); + gtk_clist_set_column_width (GTK_CLIST (clist), 9, 85); + gtk_clist_set_column_width (GTK_CLIST (clist), 10, 80); + gtk_clist_set_column_width (GTK_CLIST (clist), 11, 80); + + gtk_clist_set_column_justification(GTK_CLIST(clist), 0, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 1, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 2, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 3, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 4, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 5, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 6, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 7, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 8, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 9, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 10, GTK_JUSTIFY_CENTER); + gtk_clist_set_column_justification(GTK_CLIST(clist), 11, GTK_JUSTIFY_CENTER); + + gtk_clist_column_titles_show (GTK_CLIST (clist)); + + gtk_clist_set_compare_func(GTK_CLIST(clist), mcaststream_sort_column); + gtk_clist_set_sort_column(GTK_CLIST(clist), 0); + gtk_clist_set_sort_type(GTK_CLIST(clist), GTK_SORT_ASCENDING); + + gtk_widget_show(mcaststream_dlg_w); + + /* sort by column feature */ + col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS); + + for (i=0; idata)); + list = g_list_next(list); + } + + mcaststream_on_unselect(NULL, NULL); + } + + last_list = list; +} + + +/****************************************************************************/ +/* update the contents of the dialog box clist */ +/* list: pointer to list of mcast_stream_info_t* */ +void mcaststream_dlg_show(GList *list) +{ + if (mcast_stream_dlg != NULL) { + /* There's already a dialog box; reactivate it. */ + reactivate_window(mcast_stream_dlg); + /* Another list since last call? */ + if (list != last_list) { + mcaststream_dlg_update(list); + } + } + else { + /* Create and show the dialog box */ + mcaststream_dlg_create(); + mcaststream_dlg_update(list); + } +} + + +/****************************************************************************/ +/* entry point when called via the GTK menu */ +static void mcaststream_launch(GtkWidget *w _U_, gpointer data _U_) +{ + /* Register the tap listener */ + register_tap_listener_mcast_stream(); + + /* Scan for Mcast streams (redissect all packets) */ + mcaststream_scan(); + + /* Show the dialog box with the list of streams */ + mcaststream_dlg_show(mcaststream_get_info()->strinfo_list); + + /* Tap listener will be removed and cleaned up in mcaststream_on_destroy */ +} + +/****************************************************************************/ +void +register_tap_listener_mcast_stream_dlg(void) +{ + register_stat_menu_item("Multicat Streams", REGISTER_STAT_GROUP_NONE, + mcaststream_launch, NULL, NULL, NULL); +} diff --git a/gtk/mcast_stream_dlg.h b/gtk/mcast_stream_dlg.h new file mode 100644 index 0000000000..a36aaa624f --- /dev/null +++ b/gtk/mcast_stream_dlg.h @@ -0,0 +1,53 @@ +/* mcast_stream_dlg.h + * + * Copyright 2006, Iskratel , Slovenia + * By Jakob Bratkovic and + * Miha Jemec + * + * based on rtp_stream_dlg.h + * Copyright 2003, Alcatel Business Systems + * By Lars Ruoff + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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. + */ + +#ifndef Mcast_STREAM_DLG_H_INCLUDED +#define Mcast_STREAM_DLG_H_INCLUDED + +#include + +/** @file + * "Mcast Stream Analysis" dialog box. + */ + +/** + * Create or reactivate the mcast streams dialog box. + * + * @param list pointer to list of mcast_stream_info_t* + */ +void mcaststream_dlg_show(GList *list); + +/** + * Update the contents of the dialog box clist with that of list. + * + * @param list pointer to list of mcast_stream_info_t* + */ +void mcaststream_dlg_update(GList *list); + +#endif /*Mcast_STREAM_DLG_H_INCLUDED*/