3 * Copyright 2006, Iskratel , Slovenia
4 * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
5 * Miha Jemec <m.jemec@iskratel.si>
9 * based on rtp_stream.c
10 * Copyright 2003, Alcatel Business Systems
11 * By Lars Ruoff <lars.ruoff@gmx.net>
13 * Wireshark - Network traffic analyzer
14 * By Gerald Combs <gerald@wireshark.org>
15 * Copyright 1998 Gerald Combs
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
46 #include <epan/epan.h>
47 #include <epan/address.h>
48 #include <epan/packet.h>
50 #include <epan/to_str.h>
52 #include "ui/alert_box.h"
53 #include "ui/simple_dialog.h"
55 #include "ui/gtk/mcast_stream.h"
56 #include "ui/gtk/mcast_stream_dlg.h"
57 #include "ui/gtk/main.h"
59 #ifdef HAVE_WINSOCK2_H
63 gint32 mcast_stream_trigger
= 50; /* limit for triggering the burst alarm (in packets per second) */
64 gint32 mcast_stream_bufferalarm
= 10000; /* limit for triggernig the buffer alarm (in bytes) */
65 guint16 mcast_stream_burstint
= 100; /* burst interval in ms */
66 gint32 mcast_stream_emptyspeed
= 5000; /* outgoing speed for single stream (kbps)*/
67 gint32 mcast_stream_cumulemptyspeed
= 100000; /* outgoiong speed for all streams (kbps)*/
69 /* sliding window and buffer usage */
70 static gint32 buffsize
= (int)((double)MAX_SPEED
* 100 / 1000) * 2;
71 static guint16
comparetimes(struct timeval
*t1
, struct timeval
*t2
, guint16 burstint_lcl
);
72 static void buffusagecalc(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
, double emptyspeed_lcl
);
73 static void slidingwindow(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
);
76 /****************************************************************************/
77 /* the one and only global mcaststream_tapinfo_t structure */
78 static mcaststream_tapinfo_t the_tapinfo_struct
=
79 {0, NULL
, 0, NULL
, 0, FALSE
};
82 /****************************************************************************/
83 /* GCompareFunc style comparison function for _mcast_stream_info */
85 mcast_stream_info_cmp(gconstpointer aa
, gconstpointer bb
)
87 const struct _mcast_stream_info
* a
= (const struct _mcast_stream_info
*)aa
;
88 const struct _mcast_stream_info
* b
= (const struct _mcast_stream_info
*)bb
;
92 if (a
==NULL
|| b
==NULL
)
94 if (ADDRESSES_EQUAL(&(a
->src_addr
), &(b
->src_addr
))
95 && (a
->src_port
== b
->src_port
)
96 && ADDRESSES_EQUAL(&(a
->dest_addr
), &(b
->dest_addr
))
97 && (a
->dest_port
== b
->dest_port
))
105 /****************************************************************************/
106 /* when there is a [re]reading of packet's */
108 mcaststream_reset(mcaststream_tapinfo_t
*tapinfo
)
112 /* free the data items first */
113 list
= g_list_first(tapinfo
->strinfo_list
);
116 /* XYZ I don't know how to clean this */
117 /*g_free(list->element.buff); */
119 list
= g_list_next(list
);
121 g_list_free(tapinfo
->strinfo_list
);
122 tapinfo
->strinfo_list
= NULL
;
124 /* XYZ and why does the line below causes a crach? */
125 /*g_free(tapinfo->allstreams->element.buff);*/
126 g_free(tapinfo
->allstreams
);
127 tapinfo
->allstreams
= NULL
;
129 tapinfo
->nstreams
= 0;
130 tapinfo
->npackets
= 0;
132 ++(tapinfo
->launch_count
);
138 mcaststream_reset_cb(void *arg
)
140 mcaststream_reset((mcaststream_tapinfo_t
*)arg
);
143 /****************************************************************************/
144 /* redraw the output */
146 mcaststream_draw(void *arg _U_
)
148 /* XXX: see mcaststream_on_update in mcast_streams_dlg.c for comments
149 g_signal_emit_by_name(top_level, "signal_mcaststream_update");
151 mcaststream_dlg_update(the_tapinfo_struct
.strinfo_list
);
157 /****************************************************************************/
158 /* whenever a udp packet is seen by the tap listener */
160 mcaststream_packet(void *arg
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *arg2 _U_
)
162 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)arg
;
163 mcast_stream_info_t tmp_strinfo
;
164 mcast_stream_info_t
*strinfo
= NULL
;
168 /* gather infos on the stream this packet is part of */
169 COPY_ADDRESS(&(tmp_strinfo
.src_addr
), &(pinfo
->src
));
170 tmp_strinfo
.src_port
= pinfo
->srcport
;
171 COPY_ADDRESS(&(tmp_strinfo
.dest_addr
), &(pinfo
->dst
));
172 tmp_strinfo
.dest_port
= pinfo
->destport
;
174 /* first we ignore non multicast packets; we filter out only those ethernet packets
175 * which start with the 01:00:5E multicast address (for IPv4) and 33:33 multicast
176 * address (for IPv6).
178 if ((pinfo
->dl_dst
.type
!= AT_ETHER
) ||
179 ((g_ascii_strncasecmp("01005E", bytes_to_str((guint8
*)pinfo
->dl_dst
.data
, pinfo
->dl_dst
.len
), 6) != 0) &&
180 (g_ascii_strncasecmp("3333", bytes_to_str((guint8
*)pinfo
->dl_dst
.data
, pinfo
->dl_dst
.len
), 4) != 0)) )
183 /* check whether we already have a stream with these parameters in the list */
184 list
= g_list_first(tapinfo
->strinfo_list
);
187 if (mcast_stream_info_cmp(&tmp_strinfo
, (mcast_stream_info_t
*)(list
->data
))==0)
189 strinfo
= (mcast_stream_info_t
*)(list
->data
); /*found!*/
192 list
= g_list_next(list
);
195 /* not in the list? then create a new entry */
197 /*printf("nov sip %s sp %d dip %s dp %d\n", g_strdup(get_addr_name(&(pinfo->src))),
198 pinfo->srcport, g_strdup(get_addr_name(&(pinfo->dst))), pinfo->destport);*/
199 tmp_strinfo
.npackets
= 0;
200 tmp_strinfo
.apackets
= 0;
201 tmp_strinfo
.first_frame_num
= pinfo
->fd
->num
;
202 tmp_strinfo
.start_sec
= (guint32
) pinfo
->fd
->abs_ts
.secs
;
203 tmp_strinfo
.start_usec
= pinfo
->fd
->abs_ts
.nsecs
/1000;
204 tmp_strinfo
.start_rel_sec
= (guint32
) pinfo
->rel_ts
.secs
;
205 tmp_strinfo
.start_rel_usec
= pinfo
->rel_ts
.nsecs
/1000;
206 tmp_strinfo
.vlan_id
= 0;
208 /* reset Mcast stats */
209 tmp_strinfo
.average_bw
= 0;
210 tmp_strinfo
.total_bytes
= 0;
212 /* reset slidingwindow and buffer parameters */
213 tmp_strinfo
.element
.buff
= (struct timeval
*)g_malloc(buffsize
* sizeof(struct timeval
));
214 tmp_strinfo
.element
.first
=0;
215 tmp_strinfo
.element
.last
=0;
216 tmp_strinfo
.element
.burstsize
=1;
217 tmp_strinfo
.element
.topburstsize
=1;
218 tmp_strinfo
.element
.numbursts
=0;
219 tmp_strinfo
.element
.burststatus
=0;
220 tmp_strinfo
.element
.count
=1;
221 tmp_strinfo
.element
.buffusage
=pinfo
->fd
->pkt_len
;
222 tmp_strinfo
.element
.topbuffusage
=pinfo
->fd
->pkt_len
;
223 tmp_strinfo
.element
.numbuffalarms
=0;
224 tmp_strinfo
.element
.buffstatus
=0;
225 tmp_strinfo
.element
.maxbw
=0;
227 strinfo
= (mcast_stream_info_t
*)g_malloc(sizeof(mcast_stream_info_t
));
228 *strinfo
= tmp_strinfo
; /* memberwise copy of struct */
229 tapinfo
->strinfo_list
= g_list_append(tapinfo
->strinfo_list
, strinfo
);
230 strinfo
->element
.buff
= (struct timeval
*)g_malloc(buffsize
* sizeof(struct timeval
));
232 /* set time with the first packet */
233 if (tapinfo
->npackets
== 0) {
234 tapinfo
->allstreams
= (mcast_stream_info_t
*)g_malloc(sizeof(mcast_stream_info_t
));
235 tapinfo
->allstreams
->element
.buff
=
236 (struct timeval
*)g_malloc(buffsize
* sizeof(struct timeval
));
237 tapinfo
->allstreams
->start_rel_sec
= (guint32
) pinfo
->rel_ts
.secs
;
238 tapinfo
->allstreams
->start_rel_usec
= pinfo
->rel_ts
.nsecs
/1000;
239 tapinfo
->allstreams
->total_bytes
= 0;
240 tapinfo
->allstreams
->element
.first
=0;
241 tapinfo
->allstreams
->element
.last
=0;
242 tapinfo
->allstreams
->element
.burstsize
=1;
243 tapinfo
->allstreams
->element
.topburstsize
=1;
244 tapinfo
->allstreams
->element
.numbursts
=0;
245 tapinfo
->allstreams
->element
.burststatus
=0;
246 tapinfo
->allstreams
->element
.count
=1;
247 tapinfo
->allstreams
->element
.buffusage
=pinfo
->fd
->pkt_len
;
248 tapinfo
->allstreams
->element
.topbuffusage
=pinfo
->fd
->pkt_len
;
249 tapinfo
->allstreams
->element
.numbuffalarms
=0;
250 tapinfo
->allstreams
->element
.buffstatus
=0;
251 tapinfo
->allstreams
->element
.maxbw
=0;
255 /* time between first and last packet in the group */
256 strinfo
->stop_rel_sec
= (guint32
) pinfo
->rel_ts
.secs
;
257 strinfo
->stop_rel_usec
= pinfo
->rel_ts
.nsecs
/1000;
258 deltatime
= ((float)((strinfo
->stop_rel_sec
* 1000000 + strinfo
->stop_rel_usec
)
259 - (strinfo
->start_rel_sec
*1000000 + strinfo
->start_rel_usec
)))/1000000;
261 /* calculate average bandwidth for this stream */
262 strinfo
->total_bytes
= strinfo
->total_bytes
+ pinfo
->fd
->pkt_len
;
264 strinfo
->average_bw
= (((float)(strinfo
->total_bytes
*8) / deltatime
) / 1000000);
266 /* increment the packets counter for this stream and calculate average pps */
267 ++(strinfo
->npackets
);
268 strinfo
->apackets
= (guint32
) (strinfo
->npackets
/ deltatime
);
270 /* time between first and last packet in any group */
271 tapinfo
->allstreams
->stop_rel_sec
= (guint32
) pinfo
->rel_ts
.secs
;
272 tapinfo
->allstreams
->stop_rel_usec
= pinfo
->rel_ts
.nsecs
/1000;
273 deltatime
= ((float)((tapinfo
->allstreams
->stop_rel_sec
* 1000000 + tapinfo
->allstreams
->stop_rel_usec
)
274 - (tapinfo
->allstreams
->start_rel_sec
*1000000 + tapinfo
->allstreams
->start_rel_usec
)))/1000000;
276 /* increment the packets counter of all streams */
277 ++(tapinfo
->npackets
);
279 /* calculate average bandwidth for all streams */
280 tapinfo
->allstreams
->total_bytes
= tapinfo
->allstreams
->total_bytes
+ pinfo
->fd
->pkt_len
;
282 tapinfo
->allstreams
->average_bw
= (((float)(tapinfo
->allstreams
->total_bytes
*8) / deltatime
) / 1000000);
284 /* sliding window and buffercalc for this group*/
285 slidingwindow(strinfo
, pinfo
);
286 buffusagecalc(strinfo
, pinfo
, mcast_stream_emptyspeed
*1000);
287 /* sliding window and buffercalc for all groups */
288 slidingwindow(tapinfo
->allstreams
, pinfo
);
289 buffusagecalc(tapinfo
->allstreams
, pinfo
, mcast_stream_cumulemptyspeed
*1000);
290 /* end of sliding window */
292 return 1; /* refresh output */
296 /****************************************************************************/
297 /* scan for Mcast streams */
299 mcaststream_scan(void)
301 gboolean was_registered
= the_tapinfo_struct
.is_registered
;
302 if (!the_tapinfo_struct
.is_registered
)
303 register_tap_listener_mcast_stream();
305 cf_retap_packets(&cfile
);
308 remove_tap_listener_mcast_stream();
312 /****************************************************************************/
313 const mcaststream_tapinfo_t
*
314 mcaststream_get_info(void)
316 return &the_tapinfo_struct
;
320 /****************************************************************************/
322 /****************************************************************************/
324 /****************************************************************************/
326 remove_tap_listener_mcast_stream(void)
328 if (the_tapinfo_struct
.is_registered
) {
329 remove_tap_listener(&the_tapinfo_struct
);
331 the_tapinfo_struct
.is_registered
= FALSE
;
336 /****************************************************************************/
338 register_tap_listener_mcast_stream(void)
340 GString
*error_string
;
341 if (!the_tapinfo_struct
.is_registered
) {
342 error_string
= register_tap_listener("udp", &the_tapinfo_struct
,
343 NULL
, 0, mcaststream_reset_cb
, mcaststream_packet
,
346 if (error_string
!= NULL
) {
347 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
348 "%s", error_string
->str
);
349 g_string_free(error_string
, TRUE
);
353 the_tapinfo_struct
.is_registered
= TRUE
;
357 /*******************************************************************************/
358 /* sliding window and buffer calculations */
360 /* compare two times */
362 comparetimes(struct timeval
*t1
, struct timeval
*t2
, guint16 burstint_lcl
)
364 if(((t2
->tv_sec
- t1
->tv_sec
)*1000 + (t2
->tv_usec
- t1
->tv_usec
)/1000) > burstint_lcl
){
371 /* calculate buffer usage */
373 buffusagecalc(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
, double emptyspeed_lcl
)
376 gint32 usec
=0, cur
, prev
;
377 struct timeval
*buffer
;
380 buffer
= strinfo
->element
.buff
;
381 cur
= strinfo
->element
.last
;
393 sec
= buffer
[cur
].tv_sec
- buffer
[prev
].tv_sec
;
394 usec
= (gint32
)buffer
[cur
].tv_usec
- (gint32
)buffer
[prev
].tv_usec
;
395 timeelapsed
= (double)usec
/1000000 + (double)sec
;
397 /* bytes added to buffer */
398 strinfo
->element
.buffusage
+=pinfo
->fd
->pkt_len
;
400 /* bytes cleared from buffer */
401 strinfo
->element
.buffusage
-= (guint32
) (timeelapsed
* emptyspeed_lcl
/ 8);
403 if(strinfo
->element
.buffusage
< 0) strinfo
->element
.buffusage
=0;
404 if(strinfo
->element
.buffusage
> strinfo
->element
.topbuffusage
)
405 strinfo
->element
.topbuffusage
= strinfo
->element
.buffusage
;
406 /* check for buffer losses */
407 if((strinfo
->element
.buffusage
>= mcast_stream_bufferalarm
) && (strinfo
->element
.buffstatus
== 0)){
408 strinfo
->element
.buffstatus
= 1;
409 strinfo
->element
.numbuffalarms
++;
410 } else if(strinfo
->element
.buffusage
< mcast_stream_bufferalarm
){
411 strinfo
->element
.buffstatus
= 0;
417 /* sliding window calculation */
419 slidingwindow(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
)
421 struct timeval
*buffer
;
424 buffer
= strinfo
->element
.buff
;
426 diff
= strinfo
->element
.last
- strinfo
->element
.first
;
427 if(diff
< 0) diff
+=buffsize
;
429 /* check if buffer is full */
430 if(diff
>= (buffsize
- 2)){
431 fprintf(stderr
, "Warning: capture buffer full\n");
432 strinfo
->element
.first
++;
433 if(strinfo
->element
.first
>= buffsize
) strinfo
->element
.first
= strinfo
->element
.first
% buffsize
;
437 buffer
[strinfo
->element
.last
].tv_sec
= (guint32
) pinfo
->rel_ts
.secs
;
438 buffer
[strinfo
->element
.last
].tv_usec
= pinfo
->rel_ts
.nsecs
/1000;
439 while(comparetimes((struct timeval
*)&(buffer
[strinfo
->element
.first
]),
440 (struct timeval
*)&(buffer
[strinfo
->element
.last
]), mcast_stream_burstint
)){
441 strinfo
->element
.first
++;
442 if(strinfo
->element
.first
>= buffsize
) strinfo
->element
.first
= strinfo
->element
.first
% buffsize
;
445 strinfo
->element
.burstsize
= diff
;
446 if(strinfo
->element
.burstsize
> strinfo
->element
.topburstsize
) {
447 strinfo
->element
.topburstsize
= strinfo
->element
.burstsize
;
448 strinfo
->element
.maxbw
= (float)(strinfo
->element
.topburstsize
) * 1000 / mcast_stream_burstint
* pinfo
->fd
->pkt_len
* 8 / 1000000;
451 strinfo
->element
.last
++;
452 if(strinfo
->element
.last
>= buffsize
) strinfo
->element
.last
= strinfo
->element
.last
% buffsize
;
454 if((strinfo
->element
.burstsize
>= mcast_stream_trigger
) && (strinfo
->element
.burststatus
== 0)){
455 strinfo
->element
.burststatus
= 1;
456 strinfo
->element
.numbursts
++;
457 } else if(strinfo
->element
.burstsize
< mcast_stream_trigger
){
458 strinfo
->element
.burststatus
= 0;
461 strinfo
->element
.count
++;