Ethereal-dev: [Ethereal-dev] New feature: analysing Multicast streams
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Miha Jemec <m.jemec@xxxxxxxxxxx>
Date: Mon, 24 Apr 2006 16:18:10 +0200
Hi! In my daily work I have a lot to do with the video solutions. One of the major problems we encounter are the traffic bursts which may significantly degrade the video quality. Analysing this kind of problems with the ethereal I/O graph can be difficult and thus we developed another mechanism. Background: If you imagine a real solution there are points in the network where speed reduction occurs (ethernet links, DSL lines, etc). One example can be: >---100 Mbit/s link-----|ethernet switch|----10 Mbit/s link --------> If this is video network for carrying one MPEG2 stream with average bandwidth of 5 Mbit/s, the crucial information is how big are the bursts inside this stream. Because of limited memory of the output queue inside the ethernet switch we can get packet drops if the bursts get to high. 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 Attached is picture of the two windows, the source code and make files (all go inside gtk directory) for Linux. Tested on FC3, FC4, FC5. I don't know what has to be added for Windows compatibility, maybe changes inside the Makefile.nmake are enough? Here is also a link with one Multicast stream capture file, you can play with http://www2.arnes.si/~ljmik6/pub/video_streams.cap (13MB) Below is also a detailed explanation of the columns inside the window. The major credit goes to my co-worker Jakob Bratkovic, who wrote the whole calculation and analysing part of the code. Regards, Miha Jemec Iskratel Slovenia Column explanation: Max burst - the highest number of packets inside a sliding window time interval. The time interval can be specified inside the Set parameters window Max Bw - same as the above one, only in Mbps instead of pps Burst Alarms - how many times the bursts exceeded the limit set inside the Parameters dialog Max buffer - how big the output queue should be that no packet will be dropped at specified output speed Buff alarms - how many times this was not the case (the required buffer was higher than available one) Inside the Set parameters dialog, beside the Burst Interval and Alarm Limits, you can also specify the output speed for single stream and output speed for all streams.
Attachment:
video.jpg
Description: JPEG image
/* mcast_stream.c * * Copyright 2006, Iskratel , Slovenia * By Jakob Bratkovic <j.bratkovic@xxxxxxxxxxx> and * Miha Jemec <m.jemec@xxxxxxxxxxx> * * based on rtp_stream.c * Copyright 2003, Alcatel Business Systems * By Lars Ruoff <lars.ruoff@xxxxxxx> * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@xxxxxxxxxxxx> * 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++; }
/* mcast_stream.h * * Copyright 2006, Iskratel , Slovenia * By Jakob Bratkovic <j.bratkovic@xxxxxxxxxxx> and * Miha Jemec <m.jemec@xxxxxxxxxxx> * * based on rtp_stream.h * Copyright 2003, Alcatel Business Systems * By Lars Ruoff <lars.ruoff@xxxxxxx> * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@xxxxxxxxxxxx> * 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*/
/* mcast_stream_dlg.c * * Copyright 2006, Iskratel , Slovenia * By Jakob Bratkovic <j.bratkovic@xxxxxxxxxxx> and * Miha Jemec <m.jemec@xxxxxxxxxxx> * * based on rtp_stream_dlg.c * Copyright 2003, Alcatel Business Systems * By Lars Ruoff <lars.ruoff@xxxxxxx> * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@xxxxxxxxxxxx> * 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); }
/* mcast_stream_dlg.h * * Copyright 2006, Iskratel , Slovenia * By Jakob Bratkovic <j.bratkovic@xxxxxxxxxxx> and * Miha Jemec <m.jemec@xxxxxxxxxxx> * * based on rtp_stream_dlg.h * Copyright 2003, Alcatel Business Systems * By Lars Ruoff <lars.ruoff@xxxxxxx> * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@xxxxxxxxxxxx> * 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*/
# Makefile.am # Automake file for the GTK interface routines for Ethereal # # $Id: Makefile.am 16593 2005-11-25 23:42:52Z ulfl $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@xxxxxxxxxxxx> # 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. include Makefile.common noinst_LIBRARIES = libui.a CLEANFILES = \ libui.a \ *~ MAINTAINERCLEANFILES = \ $(GENERATED_FILES) \ Makefile.in ethereal-tap-register.c: $(ETHEREAL_TAP_SRC) $(top_srcdir)/make-tapreg-dotc @echo Making ethereal-tap-register.c @$(top_srcdir)/make-tapreg-dotc ethereal-tap-register.c $(srcdir) $(ETHEREAL_TAP_SRC) noinst_HEADERS = \ about_dlg.h \ capture_dlg.h \ capture_prefs.h \ capture_if_details_dlg.h \ cfilter_combo_utils.h \ color_dlg.h \ colors.h \ column_prefs.h \ compat_macros.h \ conversations_table.h \ decode_as_dlg.h \ decode_as_dcerpc.h \ dfilter_expr_dlg.h \ dlg_utils.h \ expert_comp_table.h \ file_dlg.h \ fileset_dlg.h \ filter_dlg.h \ find_dlg.h \ follow_dlg.h \ font_utils.h \ goto_dlg.h \ graph_analysis.h \ gsm_map_stat.h \ gtkglobals.h \ gui_stat_util.h \ gui_prefs.h \ gui_utils.h \ help_dlg.h \ hostlist_table.h \ isprint.h \ keys.h \ layout_prefs.h \ main.h \ menu.h \ mtp3_stat.h \ nameres_prefs.h \ packet_history.h \ packet_list.h \ packet_win.h \ plugins_dlg.h \ prefs_dlg.h \ print_prefs.h \ proto_dlg.h \ proto_draw.h \ proto_hier_stats_dlg.h \ range_utils.h \ recent.h \ 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 \ gui_stat_menu.h \ stream_prefs.h \ summary_dlg.h \ supported_protos_dlg.h \ text_page.h \ toolbar.h \ voip_calls.h \ voip_calls_dlg.h \ webbrowser.h if USE_GTK2 libui_a_SOURCES = \ $(ETHEREAL_GTK_SRC) \ $(noinst_HEADERS) \ $(ETHEREAL_TAP_SRC) else libui_a_SOURCES = \ ethclist.c \ ethclist.h \ $(ETHEREAL_GTK_SRC) \ $(noinst_HEADERS) \ $(ETHEREAL_TAP_SRC) endif libui_a_DEPENDENCIES = EXTRA_DIST = \ capture_if_details_dlg.c \ doxygen.cfg.in \ ethclist.c \ ethclist.h \ Makefile.common \ Makefile.nmake \ print_mswin.c \ print_mswin.h # Common headers AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/wiretap doxygen: if HAVE_DOXYGEN $(DOXYGEN) doxygen.cfg endif # HAVE_DOXYGEN
# Makefile.common # Contains the stuff from Makefile.am and Makefile.nmake that is # a) common to both files and # b) portable between both files # # $Id: Makefile.common 16593 2005-11-25 23:42:52Z ulfl $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@xxxxxxxxxxxx> # 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. # Generated header files that we want in the distribution. # (None, so far.) GENERATED_HEADER_FILES = # Generated C source files that we want in the distribution. GENERATED_C_FILES = \ ethereal-tap-register.c # All the generated files we want in the distribution. GENERATED_FILES = $(GENERATED_HEADER_FILES) $(GENERATED_C_FILES) # # ethclist.obj is not in here because it is currently gtk+-1.2-only # code, while the DLL for GTK+ on Windows is gtk+-1.3 or gtk+-2.x. # ETHEREAL_GTK_SRC = \ about_dlg.c \ capture_dlg.c \ capture_if_dlg.c \ capture_if_details_dlg.c \ capture_info_dlg.c \ capture_prefs.c \ cfilter_combo_utils.c \ color_dlg.c \ color_utils.c \ colors.c \ column_prefs.c \ conversations_table.c \ decode_as_dlg.c \ decode_as_dcerpc.c \ dfilter_expr_dlg.c \ dlg_utils.c \ drag_and_drop.c \ ethereal-tap-register.c \ expert_comp_table.c \ file_dlg.c \ fileset_dlg.c \ filter_dlg.c \ find_dlg.c \ follow_dlg.c \ font_utils.c \ goto_dlg.c \ graph_analysis.c \ gui_stat_util.c \ gui_prefs.c \ gui_utils.c \ help_dlg.c \ hostlist_table.c \ layout_prefs.c \ main.c \ menu.c \ nameres_prefs.c \ packet_history.c \ packet_list.c \ packet_win.c \ plugins_dlg.c \ prefs_dlg.c \ print_dlg.c \ print_prefs.c \ progress_dlg.c \ proto_dlg.c \ proto_draw.c \ proto_hier_stats_dlg.c \ range_utils.c \ recent.c \ rtp_stream.c \ mcast_stream.c \ sctp_stat.c \ sctp_graph_dlg.c \ sctp_byte_graph_dlg.c \ sctp_error_dlg.c \ service_response_time_table.c \ simple_dialog.c \ stream_prefs.c \ summary_dlg.c \ supported_protos_dlg.c \ tap_dfilter_dlg.c \ text_page.c \ toolbar.c \ voip_calls.c \ webbrowser.c ETHEREAL_TAP_SRC = \ afp_stat.c \ ansi_a_stat.c \ ansi_map_stat.c \ bootp_stat.c \ conversations_eth.c \ conversations_fc.c \ conversations_fddi.c \ conversations_ip.c \ conversations_ipx.c \ conversations_jxta.c \ conversations_sctp.c \ conversations_tcpip.c \ conversations_tr.c \ conversations_udpip.c \ conversations_wlan.c \ conversations_ncp.c \ conversations_rsvp.c \ dcerpc_stat.c \ expert_comp_dlg.c \ expert_dlg.c \ fc_stat.c \ flow_graph.c \ gsm_a_stat.c \ gsm_map_stat.c \ gsm_map_summary.c \ h225_counter.c \ h225_ras_srt.c \ hostlist_eth.c \ hostlist_fc.c \ hostlist_fddi.c \ hostlist_ip.c \ hostlist_ipx.c \ hostlist_jxta.c \ hostlist_tcpip.c \ hostlist_tr.c \ hostlist_udpip.c \ hostlist_wlan.c \ hostlist_rsvp.c \ io_stat.c \ ldap_stat.c \ mgcp_stat.c \ mtp3_stat.c \ mtp3_summary.c \ ncp_stat.c \ rpc_progs.c \ 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 \ sctp_chunk_stat.c \ sctp_stat_dlg.c \ sip_stat.c \ smb_stat.c \ t38_analysis.c \ tcp_graph.c \ voip_calls_dlg.c \ wsp_stat.c
- Follow-Ups:
- SV: [Ethereal-dev] New feature: analysing Multicast streams
- From: Anders Broman
- [Ethereal-dev] Re: New feature: analysing Multicast streams
- From: ronnie sahlberg
- SV: [Ethereal-dev] New feature: analysing Multicast streams
- Prev by Date: [Ethereal-dev] [Patch] to wiretap/catapult_dct2000.c
- Next by Date: Re: [Ethereal-dev] Roadmap says: "Fix Bugzilla "critical" and "blocker" bugs" completed, but I still count 10 critical bugs
- Previous by thread: SV: [Ethereal-dev] [Patch] to wiretap/catapult_dct2000.c
- Next by thread: SV: [Ethereal-dev] New feature: analysing Multicast streams
- Index(es):