ui: use SPDX identifiers.
[metze/wireshark/wip.git] / ui / mcast_stream.c
1 /* mcast_stream.c
2  *
3  * Copyright 2006, Iskratel , Slovenia
4  * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
5  * Miha Jemec <m.jemec@iskratel.si>
6  *
7  * based on rtp_stream.c
8  * Copyright 2003, Alcatel Business Systems
9  * By Lars Ruoff <lars.ruoff@gmx.net>
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  * SPDX-License-Identifier: GPL-2.0+
16  */
17
18 #include "config.h"
19
20 #include <glib.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <time.h>
25 #include <string.h>
26
27 #include "file.h"
28
29 #include <epan/epan.h>
30 #include <epan/address.h>
31 #include <epan/packet.h>
32 #include <epan/tap.h>
33 #include <epan/to_str.h>
34
35 #include "ui/alert_box.h"
36 #include "ui/mcast_stream.h"
37 #include "ui/simple_dialog.h"
38
39 gint32  mcast_stream_trigger         =     50; /* limit for triggering the burst alarm (in packets per second) */
40 gint32  mcast_stream_bufferalarm     =  10000; /* limit for triggering the buffer alarm (in bytes) */
41 guint16 mcast_stream_burstint        =    100; /* burst interval in ms */
42 gint32  mcast_stream_emptyspeed      =   5000; /* outgoing speed for single stream (kbps)*/
43 gint32  mcast_stream_cumulemptyspeed = 100000; /* outgoiong speed for all streams (kbps)*/
44
45 /* sliding window and buffer usage */
46 static gint32  buffsize = (int)((double)MAX_SPEED * 100 / 1000) * 2;
47 static guint16 comparetimes(nstime_t *t1, nstime_t *t2, guint16 burstint_lcl);
48 static void    buffusagecalc(mcast_stream_info_t *strinfo, packet_info *pinfo, double emptyspeed_lcl);
49 static void    slidingwindow(mcast_stream_info_t *strinfo, packet_info *pinfo);
50
51 /****************************************************************************/
52 /* GCompareFunc style comparison function for _mcast_stream_info */
53 static gint
54 mcast_stream_info_cmp(gconstpointer aa, gconstpointer bb)
55 {
56     const struct _mcast_stream_info* a = (const struct _mcast_stream_info *)aa;
57     const struct _mcast_stream_info* b = (const struct _mcast_stream_info *)bb;
58
59     if (a==b)
60         return 0;
61     if (a==NULL || b==NULL)
62         return 1;
63     if (addresses_equal(&(a->src_addr), &(b->src_addr))
64         && (a->src_port == b->src_port)
65         && addresses_equal(&(a->dest_addr), &(b->dest_addr))
66         && (a->dest_port == b->dest_port))
67         return 0;
68     else
69         return 1;
70
71 }
72
73
74 /****************************************************************************/
75 /* when there is a [re]reading of packet's */
76 void
77 mcaststream_reset(mcaststream_tapinfo_t *tapinfo)
78 {
79     GList* list;
80
81     /* free the data items first */
82     list = g_list_first(tapinfo->strinfo_list);
83     while (list)
84     {
85         /* XYZ I don't know how to clean this */
86         /*g_free(list->element.buff); */
87         g_free(list->data);
88         list = g_list_next(list);
89     }
90     g_list_free(tapinfo->strinfo_list);
91     tapinfo->strinfo_list = NULL;
92
93     /* XYZ and why does the line below causes a crach? */
94     /*g_free(tapinfo->allstreams->element.buff);*/
95     g_free(tapinfo->allstreams);
96     tapinfo->allstreams = NULL;
97
98     tapinfo->npackets = 0;
99
100     return;
101 }
102
103 static void
104 mcaststream_reset_cb(void *ti_ptr)
105 {
106     mcaststream_tapinfo_t *tapinfo = (mcaststream_tapinfo_t *)ti_ptr;
107     if (tapinfo) {
108         if (tapinfo->tap_reset) {
109            tapinfo->tap_reset(tapinfo);
110         }
111         mcaststream_reset(tapinfo);
112     }
113 }
114
115 /****************************************************************************/
116 /* redraw the output */
117 static void
118 mcaststream_draw(void *ti_ptr)
119 {
120     mcaststream_tapinfo_t *tapinfo = (mcaststream_tapinfo_t *)ti_ptr;
121 /* XXX: see mcaststream_on_update in mcast_streams_dlg.c for comments
122     g_signal_emit_by_name(top_level, "signal_mcaststream_update");
123 */
124     if (tapinfo && tapinfo->tap_draw) {
125         tapinfo->tap_draw(tapinfo);
126     }
127     return;
128 }
129
130
131
132 /****************************************************************************/
133 /* whenever a udp packet is seen by the tap listener */
134 static gboolean
135 mcaststream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2 _U_)
136 {
137     mcaststream_tapinfo_t *tapinfo = (mcaststream_tapinfo_t *)arg;
138     mcast_stream_info_t tmp_strinfo;
139     mcast_stream_info_t *strinfo = NULL;
140     GList* list;
141     nstime_t delta;
142     double deltatime;
143
144     /*
145      * Restrict statistics to standard multicast IPv4 and IPv6 addresses.
146      * We might want to check for and allow ethernet addresses starting
147      * with 01:00:05 and 33:33 as well.
148      */
149     switch (pinfo->net_dst.type) {
150         case AT_IPv4:
151             /* 224.0.0.0/4 */
152             if (pinfo->net_dst.len == 0 || (((const guint8*)pinfo->net_dst.data)[0] & 0xf0) != 0xe0)
153                 return FALSE;
154             break;
155         case AT_IPv6:
156             /* ff00::/8 */
157             /* XXX This includes DHCPv6. */
158             if (pinfo->net_dst.len == 0 || ((const guint8*)pinfo->net_dst.data)[0] != 0xff)
159                 return FALSE;
160             break;
161         default:
162             return FALSE;
163     }
164
165     /* gather infos on the stream this packet is part of */
166     copy_address(&(tmp_strinfo.src_addr), &(pinfo->net_src));
167     tmp_strinfo.src_port = pinfo->srcport;
168     copy_address(&(tmp_strinfo.dest_addr), &(pinfo->net_dst));
169     tmp_strinfo.dest_port = pinfo->destport;
170
171     /* check whether we already have a stream with these parameters in the list */
172     list = g_list_first(tapinfo->strinfo_list);
173     while (list)
174     {
175         if (mcast_stream_info_cmp(&tmp_strinfo, (mcast_stream_info_t*)(list->data))==0)
176         {
177             strinfo = (mcast_stream_info_t*)(list->data);  /*found!*/
178             break;
179         }
180         list = g_list_next(list);
181     }
182
183     /* not in the list? then create a new entry */
184     if (!strinfo) {
185         /*printf("nov sip %s sp %d dip %s dp %d\n", address_to_display(NULL, &(pinfo->src)),
186             pinfo->srcport, address_to_display(NULL, &(pinfo->dst)), pinfo->destport);*/
187         tmp_strinfo.npackets = 0;
188         tmp_strinfo.apackets = 0;
189         tmp_strinfo.first_frame_num = pinfo->num;
190         tmp_strinfo.start_abs = pinfo->abs_ts;
191         tmp_strinfo.start_rel = pinfo->rel_ts;
192         tmp_strinfo.vlan_id = 0;
193
194         /* reset Mcast stats */
195         tmp_strinfo.average_bw = 0;
196         tmp_strinfo.total_bytes = 0;
197
198         /* reset slidingwindow and buffer parameters */
199         tmp_strinfo.element.buff = (nstime_t *)g_malloc(buffsize * sizeof(nstime_t));
200         tmp_strinfo.element.first=0;
201         tmp_strinfo.element.last=0;
202         tmp_strinfo.element.burstsize=1;
203         tmp_strinfo.element.topburstsize=1;
204         tmp_strinfo.element.numbursts=0;
205         tmp_strinfo.element.burststatus=0;
206         tmp_strinfo.element.count=1;
207         tmp_strinfo.element.buffusage=pinfo->fd->pkt_len;
208         tmp_strinfo.element.topbuffusage=pinfo->fd->pkt_len;
209         tmp_strinfo.element.numbuffalarms=0;
210         tmp_strinfo.element.buffstatus=0;
211         tmp_strinfo.element.maxbw=0;
212
213         strinfo = (mcast_stream_info_t *)g_malloc(sizeof(mcast_stream_info_t));
214         *strinfo = tmp_strinfo;  /* memberwise copy of struct */
215         tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
216         strinfo->element.buff = (nstime_t *)g_malloc(buffsize * sizeof(nstime_t));
217
218         /* set time with the first packet */
219         if (tapinfo->npackets == 0) {
220             tapinfo->allstreams = (mcast_stream_info_t *)g_malloc(sizeof(mcast_stream_info_t));
221             tapinfo->allstreams->element.buff =
222                     (nstime_t *)g_malloc(buffsize * sizeof(nstime_t));
223             tapinfo->allstreams->start_rel = pinfo->rel_ts;
224             tapinfo->allstreams->total_bytes = 0;
225             tapinfo->allstreams->element.first=0;
226             tapinfo->allstreams->element.last=0;
227             tapinfo->allstreams->element.burstsize=1;
228             tapinfo->allstreams->element.topburstsize=1;
229             tapinfo->allstreams->element.numbursts=0;
230             tapinfo->allstreams->element.burststatus=0;
231             tapinfo->allstreams->element.count=1;
232             tapinfo->allstreams->element.buffusage=pinfo->fd->pkt_len;
233             tapinfo->allstreams->element.topbuffusage=pinfo->fd->pkt_len;
234             tapinfo->allstreams->element.numbuffalarms=0;
235             tapinfo->allstreams->element.buffstatus=0;
236             tapinfo->allstreams->element.maxbw=0;
237         }
238     }
239
240     /* time between first and last packet in the group */
241     strinfo->stop_rel = pinfo->rel_ts;
242     nstime_delta(&delta, &strinfo->stop_rel, &strinfo->start_rel);
243     deltatime = nstime_to_sec(&delta);
244
245     /* calculate average bandwidth for this stream */
246     strinfo->total_bytes = strinfo->total_bytes + pinfo->fd->pkt_len;
247
248     /* increment the packets counter for this stream and calculate average pps */
249     ++(strinfo->npackets);
250     
251     if (deltatime > 0) {
252         strinfo->apackets = strinfo->npackets / deltatime;
253         strinfo->average_bw = ((double)(strinfo->total_bytes*8) / deltatime);
254     } else {
255         strinfo->apackets = strinfo->average_bw = 0.0;
256     }
257
258     /* time between first and last packet in any group */
259     tapinfo->allstreams->stop_rel = pinfo->rel_ts;
260     nstime_delta(&delta, &tapinfo->allstreams->stop_rel, &tapinfo->allstreams->start_rel);
261     deltatime = nstime_to_sec(&delta);
262
263     /* increment the packets counter of all streams */
264     ++(tapinfo->npackets);
265
266     /* calculate average bandwidth for all streams */
267     tapinfo->allstreams->total_bytes = tapinfo->allstreams->total_bytes + pinfo->fd->pkt_len;
268     if (deltatime > 0)
269         tapinfo->allstreams->average_bw = ((double)(tapinfo->allstreams->total_bytes*8) / deltatime);
270
271     /* sliding window and buffercalc for this group*/
272     slidingwindow(strinfo, pinfo);
273     buffusagecalc(strinfo, pinfo, mcast_stream_emptyspeed*1000);
274     /* sliding window and buffercalc for all groups */
275     slidingwindow(tapinfo->allstreams, pinfo);
276     buffusagecalc(tapinfo->allstreams, pinfo, mcast_stream_cumulemptyspeed*1000);
277     /* end of sliding window */
278
279     return 1;  /* refresh output */
280
281 }
282
283 /****************************************************************************/
284 /* scan for Mcast streams */
285 void
286 mcaststream_scan(mcaststream_tapinfo_t *tapinfo, capture_file *cap_file)
287 {
288     gboolean was_registered;
289
290     if (!tapinfo || !cap_file) {
291         return;
292     }
293
294     was_registered = tapinfo->is_registered;
295     if (!tapinfo->is_registered)
296         register_tap_listener_mcast_stream(tapinfo);
297
298     cf_retap_packets(cap_file);
299
300     if (!was_registered)
301         remove_tap_listener_mcast_stream(tapinfo);
302 }
303
304 /****************************************************************************/
305 /* TAP INTERFACE */
306 /****************************************************************************/
307
308 /****************************************************************************/
309 void
310 remove_tap_listener_mcast_stream(mcaststream_tapinfo_t *tapinfo)
311 {
312     if (tapinfo && tapinfo->is_registered) {
313         remove_tap_listener(tapinfo);
314         tapinfo->is_registered = FALSE;
315     }
316 }
317
318
319 /****************************************************************************/
320 void
321 register_tap_listener_mcast_stream(mcaststream_tapinfo_t *tapinfo)
322 {
323     GString *error_string;
324
325     if (!tapinfo) {
326         return;
327     }
328
329     if (!tapinfo->is_registered) {
330         error_string = register_tap_listener("udp", tapinfo,
331             NULL, 0, mcaststream_reset_cb, mcaststream_packet,
332             mcaststream_draw);
333
334         if (error_string != NULL) {
335             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
336                           "%s", error_string->str);
337             g_string_free(error_string, TRUE);
338             exit(1);
339         }
340
341         tapinfo->is_registered = TRUE;
342     }
343 }
344
345 /*******************************************************************************/
346 /* sliding window and buffer calculations */
347
348 /* compare two times */
349 static guint16
350 comparetimes(nstime_t *t1, nstime_t *t2, guint16 burstint_lcl)
351 {
352     if(((t2->secs - t1->secs)*1000 + (t2->nsecs - t1->nsecs)/1000000) > burstint_lcl){
353         return 1;
354     } else{
355         return 0;
356     }
357 }
358
359 /* calculate buffer usage */
360 static void
361 buffusagecalc(mcast_stream_info_t *strinfo, packet_info *pinfo, double emptyspeed_lcl)
362 {
363     gint32 cur, prev;
364     nstime_t *buffer;
365     nstime_t delta;
366     double timeelapsed;
367
368     buffer = strinfo->element.buff;
369     cur = strinfo->element.last;
370     if(cur == 0){
371         cur = buffsize - 1;
372         prev = cur - 1;
373     } else if(cur == 1){
374         prev = buffsize - 1;
375         cur = 0;
376     } else{
377         cur=cur-1;
378         prev=cur-1;
379     }
380
381     nstime_delta(&delta, &buffer[cur], &buffer[prev]);
382     timeelapsed = nstime_to_sec(&delta);
383
384     /* bytes added to buffer */
385     strinfo->element.buffusage+=pinfo->fd->pkt_len;
386
387     /* bytes cleared from buffer */
388     strinfo->element.buffusage-= (guint32) (timeelapsed * emptyspeed_lcl / 8);
389
390     if(strinfo->element.buffusage < 0) strinfo->element.buffusage=0;
391     if(strinfo->element.buffusage > strinfo->element.topbuffusage)
392         strinfo->element.topbuffusage = strinfo->element.buffusage;
393     /* check for buffer losses */
394     if((strinfo->element.buffusage >= mcast_stream_bufferalarm) && (strinfo->element.buffstatus == 0)){
395         strinfo->element.buffstatus = 1;
396         strinfo->element.numbuffalarms++;
397     } else if(strinfo->element.buffusage < mcast_stream_bufferalarm){
398         strinfo->element.buffstatus = 0;
399     }
400
401     return;
402 }
403
404 /* sliding window calculation */
405 static void
406 slidingwindow(mcast_stream_info_t *strinfo, packet_info *pinfo)
407 {
408     nstime_t *buffer;
409     gint32 diff;
410
411     buffer = strinfo->element.buff;
412
413     diff = strinfo->element.last - strinfo->element.first;
414     if(diff < 0) diff+=buffsize;
415
416     /* check if buffer is full */
417     if(diff >= (buffsize - 2)){
418         fprintf(stderr, "Warning: capture buffer full\n");
419         strinfo->element.first++;
420         if(strinfo->element.first >= buffsize) strinfo->element.first = strinfo->element.first % buffsize;
421     }
422
423     /* burst count */
424     buffer[strinfo->element.last] = pinfo->rel_ts;
425     while(comparetimes(&buffer[strinfo->element.first],
426                        &buffer[strinfo->element.last], mcast_stream_burstint)){
427         strinfo->element.first++;
428         if(strinfo->element.first >= buffsize) strinfo->element.first = strinfo->element.first % buffsize;
429         diff--;
430     }
431     strinfo->element.burstsize = diff;
432     if(strinfo->element.burstsize > strinfo->element.topburstsize) {
433         strinfo->element.topburstsize = strinfo->element.burstsize;
434         strinfo->element.maxbw = (double)(strinfo->element.topburstsize) * 1000 / mcast_stream_burstint * pinfo->fd->pkt_len * 8;
435     }
436
437     strinfo->element.last++;
438     if(strinfo->element.last >= buffsize) strinfo->element.last = strinfo->element.last % buffsize;
439     /* trigger check */
440     if((strinfo->element.burstsize >= mcast_stream_trigger) && (strinfo->element.burststatus == 0)){
441         strinfo->element.burststatus = 1;
442         strinfo->element.numbursts++;
443     } else if(strinfo->element.burstsize < mcast_stream_trigger){
444         strinfo->element.burststatus = 0;
445     }
446
447     strinfo->element.count++;
448 }
449
450 /*
451  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
452  *
453  * Local variables:
454  * c-basic-offset: 4
455  * tab-width: 8
456  * indent-tabs-mode: nil
457  * End:
458  *
459  * vi: set shiftwidth=4 tabstop=8 expandtab:
460  * :indentSize=4:tabSize=8:noTabs=true:
461  */