aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2006-04-24 19:10:36 +0000
committerAnders Broman <anders.broman@ericsson.com>2006-04-24 19:10:36 +0000
commit6b33d50f9054c02be28a846190d279b0a101f744 (patch)
tree2463fd66c1668bd359aca93a73e2cbaf51e36d0a
parentde3b8195c53392f0158951540054e47cc832ee89 (diff)
From Miha Jemec:
With the new feature we can: 1. Measure how big the bursts are for a video streams (it uses sliding window algorithm) 2. Measure how big the output buffer should be that no packet drop will occur (it uses Leaky bucket algorithm) 3. Detect if we have loses inside the MPEG2 video stream (if there are already MPEG2 packets missing) - this part of code is not added yet, see Limitations The addition is called Multicast streams and works as follows: - it uses the TAP system - the main "stream" logic is taken from rtp_strems.* files - the TAP system checks for UDP packets where the destination MAC address starts with "01:00:5E" (ethernet multicast address) - it creates an entry for every new multicast stream - based on sliding window and leaky bucket algorithm it calculates for every stream average BW, max BW, burst size, max buffer needed, some alarms if the limits are exceeded,... - the same calculation is done for all streams together - inside the window dialog you can specify the burst interval, the alarm limits and output speeds To do & limitations: - Currently the analysis can be done only for multicast streams, it means that VoD (Video on demand) or PayTV streams, which are normally unicast can not be analysed. - since the MPEG2 is patended I don't know if decoding of MPEG2 packets is allowed? Can we look inside this packets and calculate packets drops based on some counter information inside the payload? Can someone please answer this question? If we can do this, I will post this part of code too. - some more flexibility will be added svn path=/trunk/; revision=17980
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/Makefile.common2
-rw-r--r--gtk/mcast_stream.c450
-rw-r--r--gtk/mcast_stream.h146
-rw-r--r--gtk/mcast_stream_dlg.c689
-rw-r--r--gtk/mcast_stream_dlg.h53
6 files changed, 1342 insertions, 0 deletions
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 <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * based on rtp_stream.c
+ * 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 "mcast_stream.h"
+#include "mcast_stream_dlg.h"
+
+#include "globals.h"
+
+#include <epan/tap.h>
+#include "register.h"
+
+#include "alert_box.h"
+#include "simple_dialog.h"
+#include "file_util.h"
+#include <time.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <epan/addr_resolv.h>
+
+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 <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * based on rtp_stream.h
+ * 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.
+ */
+
+#ifndef Mcast_STREAM_H_INCLUDED
+#define Mcast_STREAM_H_INCLUDED
+
+#include <glib.h>
+#include <stdio.h>
+#include <epan/address.h>
+
+/** @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 <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * based on rtp_stream_dlg.c
+ * 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 "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 <epan/address.h>
+
+#include <string.h>
+#include <locale.h>
+#include <epan/addr_resolv.h>
+
+/* 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; i<NUM_COLS; i++) {
+ gtk_widget_hide(col_arrows[i].ascend_pm);
+ gtk_widget_hide(col_arrows[i].descend_pm);
+ }
+
+ if (column == clist->sort_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; i<NUM_COLS; i++) {
+ col_arrows[i].table = gtk_table_new(2, 2, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
+ column_lb = gtk_label_new(titles[i]);
+ gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show(column_lb);
+
+ col_arrows[i].ascend_pm = xpm_to_widget(clist_ascend_xpm);
+ gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+ col_arrows[i].descend_pm = xpm_to_widget(clist_descend_xpm);
+ gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+ /* make src-ip be the default sort order */
+ if (i == 0) {
+ gtk_widget_show(col_arrows[i].ascend_pm);
+ }
+ gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
+ gtk_widget_show(col_arrows[i].table);
+ }
+
+ SIGNAL_CONNECT(clist, "click-column", mcaststream_click_column_cb, col_arrows);
+
+ label_fwd = gtk_label_new (FWD_LABEL_TEXT);
+ //gtk_box_pack_start (GTK_BOX (main_vb), label_fwd, FALSE, FALSE, 0);
+
+ label_par = gtk_label_new (PAR_LABEL_TEXT);
+ gtk_box_pack_start (GTK_BOX (main_vb), label_par, FALSE, FALSE, 0);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
+ gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 0);
+
+ /*bt_unselect = gtk_button_new_with_label ("Unselect");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_unselect);
+ gtk_tooltips_set_tip (tooltips, bt_unselect, "Undo stream selection", NULL);*/
+
+ bt_params = gtk_button_new_with_label ("Set parameters");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_params);
+ gtk_tooltips_set_tip (tooltips, bt_params, "Set buffer, limit and speed parameters", NULL);
+
+ bt_filter = gtk_button_new_with_label ("Prepare Filter");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_filter);
+ gtk_tooltips_set_tip (tooltips, bt_filter, "Prepare a display filter of the selected stream", NULL);
+
+ bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
+ gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+
+ SIGNAL_CONNECT(clist, "select_row", mcaststream_on_select_row, NULL);
+ //SIGNAL_CONNECT(bt_unselect, "clicked", mcaststream_on_unselect, NULL);
+ SIGNAL_CONNECT(bt_params, "clicked", mcast_on_params, NULL);
+ SIGNAL_CONNECT(bt_filter, "clicked", mcaststream_on_filter, NULL);
+ window_set_cancel_button(mcaststream_dlg_w, bt_close, window_cancel_button_cb);
+
+ SIGNAL_CONNECT(mcaststream_dlg_w, "delete_event", window_delete_event_cb, NULL);
+ SIGNAL_CONNECT(mcaststream_dlg_w, "destroy", mcaststream_on_destroy, NULL);
+
+ gtk_widget_show_all(mcaststream_dlg_w);
+ window_present(mcaststream_dlg_w);
+
+ mcaststream_on_unselect(NULL, NULL);
+
+ mcast_stream_dlg = mcaststream_dlg_w;
+}
+
+
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+/****************************************************************************/
+/* update the contents of the dialog box clist */
+/* list: pointer to list of mcast_stream_info_t* */
+void mcaststream_dlg_update(GList *list)
+{
+ if (mcast_stream_dlg != NULL) {
+ gtk_clist_clear(GTK_CLIST(clist));
+ streams_nb = 0;
+
+ list = g_list_first(list);
+ while (list)
+ {
+ add_to_clist((mcast_stream_info_t*)(list->data));
+ 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 <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * based on rtp_stream_dlg.h
+ * 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.
+ */
+
+#ifndef Mcast_STREAM_DLG_H_INCLUDED
+#define Mcast_STREAM_DLG_H_INCLUDED
+
+#include <gtk/gtk.h>
+
+/** @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*/