3 * Copyright 2006, Iskratel , Slovenia
4 * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
5 * Miha Jemec <m.jemec@iskratel.si>
7 * based on rtp_stream.c
8 * Copyright 2003, Alcatel Business Systems
9 * By Lars Ruoff <lars.ruoff@gmx.net>
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * SPDX-License-Identifier: GPL-2.0-or-later
29 #include <epan/epan.h>
30 #include <epan/address.h>
31 #include <epan/packet.h>
33 #include <epan/to_str.h>
35 #include "ui/mcast_stream.h"
37 int32_t mcast_stream_trigger
= 50; /* limit for triggering the burst alarm (in packets per second) */
38 int32_t mcast_stream_bufferalarm
= 10000; /* limit for triggering the buffer alarm (in bytes) */
39 uint16_t mcast_stream_burstint
= 100; /* burst interval in ms */
40 int32_t mcast_stream_emptyspeed
= 5000; /* outgoing speed for single stream (kbps)*/
41 int32_t mcast_stream_cumulemptyspeed
= 100000; /* outgoing speed for all streams (kbps)*/
43 /* sliding window and buffer usage */
44 static int32_t buffsize
= (int)((double)MAX_SPEED
* 100 / 1000) * 2;
45 static uint16_t comparetimes(nstime_t
*t1
, nstime_t
*t2
, uint16_t burstint_lcl
);
46 static void buffusagecalc(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
, double emptyspeed_lcl
);
47 static void slidingwindow(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
);
49 /****************************************************************************/
50 /* GCompareFunc style comparison function for _mcast_stream_info */
52 mcast_stream_info_cmp(const void *aa
, const void *bb
)
54 const struct _mcast_stream_info
* a
= (const struct _mcast_stream_info
*)aa
;
55 const struct _mcast_stream_info
* b
= (const struct _mcast_stream_info
*)bb
;
59 if (a
==NULL
|| b
==NULL
)
61 if (addresses_equal(&(a
->src_addr
), &(b
->src_addr
))
62 && (a
->src_port
== b
->src_port
)
63 && addresses_equal(&(a
->dest_addr
), &(b
->dest_addr
))
64 && (a
->dest_port
== b
->dest_port
))
72 /****************************************************************************/
73 /* when there is a [re]reading of packet's */
75 mcaststream_reset(mcaststream_tapinfo_t
*tapinfo
)
79 /* free the data items first */
80 list
= g_list_first(tapinfo
->strinfo_list
);
83 /* XYZ I don't know how to clean this */
84 /*g_free(list->element.buff); */
86 list
= g_list_next(list
);
88 g_list_free(tapinfo
->strinfo_list
);
89 tapinfo
->strinfo_list
= NULL
;
91 /* XYZ and why does the line below causes a crash? */
92 /*g_free(tapinfo->allstreams->element.buff);*/
93 g_free(tapinfo
->allstreams
);
94 tapinfo
->allstreams
= NULL
;
96 tapinfo
->npackets
= 0;
102 mcaststream_reset_cb(void *ti_ptr
)
104 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)ti_ptr
;
106 if (tapinfo
->tap_reset
) {
107 tapinfo
->tap_reset(tapinfo
);
109 mcaststream_reset(tapinfo
);
113 /****************************************************************************/
114 /* redraw the output */
116 mcaststream_draw(void *ti_ptr
)
118 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)ti_ptr
;
119 /* XXX: see mcaststream_on_update in mcast_streams_dlg.c for comments
120 g_signal_emit_by_name(top_level, "signal_mcaststream_update");
122 if (tapinfo
&& tapinfo
->tap_draw
) {
123 tapinfo
->tap_draw(tapinfo
);
130 /****************************************************************************/
131 /* whenever a udp packet is seen by the tap listener */
133 mcaststream_packet(void *arg
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *arg2 _U_
, tap_flags_t flags _U_
)
135 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)arg
;
136 mcast_stream_info_t tmp_strinfo
;
137 mcast_stream_info_t
*strinfo
= NULL
;
143 * Restrict statistics to standard multicast IPv4 and IPv6 addresses.
144 * We might want to check for and allow ethernet addresses starting
145 * with 01:00:05 and 33:33 as well.
147 switch (pinfo
->net_dst
.type
) {
150 if (pinfo
->net_dst
.len
== 0 || (((const uint8_t*)pinfo
->net_dst
.data
)[0] & 0xf0) != 0xe0)
151 return TAP_PACKET_DONT_REDRAW
;
155 /* XXX This includes DHCPv6. */
156 if (pinfo
->net_dst
.len
== 0 || ((const uint8_t*)pinfo
->net_dst
.data
)[0] != 0xff)
157 return TAP_PACKET_DONT_REDRAW
;
160 return TAP_PACKET_DONT_REDRAW
;
163 /* gather infos on the stream this packet is part of */
164 copy_address(&(tmp_strinfo
.src_addr
), &(pinfo
->net_src
));
165 tmp_strinfo
.src_port
= pinfo
->srcport
;
166 copy_address(&(tmp_strinfo
.dest_addr
), &(pinfo
->net_dst
));
167 tmp_strinfo
.dest_port
= pinfo
->destport
;
169 /* check whether we already have a stream with these parameters in the list */
170 list
= g_list_first(tapinfo
->strinfo_list
);
173 if (mcast_stream_info_cmp(&tmp_strinfo
, (mcast_stream_info_t
*)(list
->data
))==0)
175 strinfo
= (mcast_stream_info_t
*)(list
->data
); /*found!*/
178 list
= g_list_next(list
);
181 /* not in the list? then create a new entry */
183 /*printf("nov sip %s sp %d dip %s dp %d\n", address_to_display(NULL, &(pinfo->src)),
184 pinfo->srcport, address_to_display(NULL, &(pinfo->dst)), pinfo->destport);*/
185 tmp_strinfo
.npackets
= 0;
186 tmp_strinfo
.apackets
= 0;
187 tmp_strinfo
.first_frame_num
= pinfo
->num
;
188 tmp_strinfo
.start_abs
= pinfo
->abs_ts
;
189 tmp_strinfo
.start_rel
= pinfo
->rel_ts
;
190 tmp_strinfo
.vlan_id
= 0;
192 /* reset Mcast stats */
193 tmp_strinfo
.average_bw
= 0;
194 tmp_strinfo
.total_bytes
= 0;
196 /* reset slidingwindow and buffer parameters */
197 tmp_strinfo
.element
.buff
= g_new(nstime_t
, buffsize
);
198 tmp_strinfo
.element
.first
=0;
199 tmp_strinfo
.element
.last
=0;
200 tmp_strinfo
.element
.burstsize
=1;
201 tmp_strinfo
.element
.topburstsize
=1;
202 tmp_strinfo
.element
.numbursts
=0;
203 tmp_strinfo
.element
.burststatus
=0;
204 tmp_strinfo
.element
.count
=1;
205 tmp_strinfo
.element
.buffusage
=pinfo
->fd
->pkt_len
;
206 tmp_strinfo
.element
.topbuffusage
=pinfo
->fd
->pkt_len
;
207 tmp_strinfo
.element
.numbuffalarms
=0;
208 tmp_strinfo
.element
.buffstatus
=0;
209 tmp_strinfo
.element
.maxbw
=0;
211 strinfo
= g_new(mcast_stream_info_t
, 1);
212 *strinfo
= tmp_strinfo
; /* memberwise copy of struct */
213 tapinfo
->strinfo_list
= g_list_append(tapinfo
->strinfo_list
, strinfo
);
214 strinfo
->element
.buff
= g_new(nstime_t
, buffsize
);
216 /* set time with the first packet */
217 if (tapinfo
->npackets
== 0) {
218 tapinfo
->allstreams
= g_new(mcast_stream_info_t
, 1);
219 tapinfo
->allstreams
->element
.buff
=
220 g_new(nstime_t
, buffsize
);
221 tapinfo
->allstreams
->start_rel
= pinfo
->rel_ts
;
222 tapinfo
->allstreams
->total_bytes
= 0;
223 tapinfo
->allstreams
->element
.first
=0;
224 tapinfo
->allstreams
->element
.last
=0;
225 tapinfo
->allstreams
->element
.burstsize
=1;
226 tapinfo
->allstreams
->element
.topburstsize
=1;
227 tapinfo
->allstreams
->element
.numbursts
=0;
228 tapinfo
->allstreams
->element
.burststatus
=0;
229 tapinfo
->allstreams
->element
.count
=1;
230 tapinfo
->allstreams
->element
.buffusage
=pinfo
->fd
->pkt_len
;
231 tapinfo
->allstreams
->element
.topbuffusage
=pinfo
->fd
->pkt_len
;
232 tapinfo
->allstreams
->element
.numbuffalarms
=0;
233 tapinfo
->allstreams
->element
.buffstatus
=0;
234 tapinfo
->allstreams
->element
.maxbw
=0;
238 /* time between first and last packet in the group */
239 strinfo
->stop_rel
= pinfo
->rel_ts
;
240 nstime_delta(&delta
, &strinfo
->stop_rel
, &strinfo
->start_rel
);
241 deltatime
= nstime_to_sec(&delta
);
243 /* calculate average bandwidth for this stream */
244 strinfo
->total_bytes
= strinfo
->total_bytes
+ pinfo
->fd
->pkt_len
;
246 /* increment the packets counter for this stream and calculate average pps */
247 ++(strinfo
->npackets
);
250 strinfo
->apackets
= strinfo
->npackets
/ deltatime
;
251 strinfo
->average_bw
= ((double)(strinfo
->total_bytes
*8) / deltatime
);
253 strinfo
->apackets
= strinfo
->average_bw
= 0.0;
256 /* time between first and last packet in any group */
257 tapinfo
->allstreams
->stop_rel
= pinfo
->rel_ts
;
258 nstime_delta(&delta
, &tapinfo
->allstreams
->stop_rel
, &tapinfo
->allstreams
->start_rel
);
259 deltatime
= nstime_to_sec(&delta
);
261 /* increment the packets counter of all streams */
262 ++(tapinfo
->npackets
);
264 /* calculate average bandwidth for all streams */
265 tapinfo
->allstreams
->total_bytes
= tapinfo
->allstreams
->total_bytes
+ pinfo
->fd
->pkt_len
;
267 tapinfo
->allstreams
->average_bw
= ((double)(tapinfo
->allstreams
->total_bytes
*8) / deltatime
);
269 /* sliding window and buffercalc for this group*/
270 slidingwindow(strinfo
, pinfo
);
271 buffusagecalc(strinfo
, pinfo
, mcast_stream_emptyspeed
*1000);
272 /* sliding window and buffercalc for all groups */
273 slidingwindow(tapinfo
->allstreams
, pinfo
);
274 buffusagecalc(tapinfo
->allstreams
, pinfo
, mcast_stream_cumulemptyspeed
*1000);
275 /* end of sliding window */
277 return TAP_PACKET_REDRAW
; /* refresh output */
281 /****************************************************************************/
283 /****************************************************************************/
285 /****************************************************************************/
287 remove_tap_listener_mcast_stream(mcaststream_tapinfo_t
*tapinfo
)
289 if (tapinfo
&& tapinfo
->is_registered
) {
290 remove_tap_listener(tapinfo
);
291 tapinfo
->is_registered
= false;
296 /****************************************************************************/
298 register_tap_listener_mcast_stream(mcaststream_tapinfo_t
*tapinfo
)
300 GString
*error_string
;
305 if (tapinfo
->is_registered
) {
309 error_string
= register_tap_listener("udp", tapinfo
,
310 NULL
, 0, mcaststream_reset_cb
, mcaststream_packet
,
311 mcaststream_draw
, NULL
);
313 if (NULL
== error_string
) {
314 tapinfo
->is_registered
= true;
319 /*******************************************************************************/
320 /* sliding window and buffer calculations */
322 /* compare two times */
324 comparetimes(nstime_t
*t1
, nstime_t
*t2
, uint16_t burstint_lcl
)
326 if(((t2
->secs
- t1
->secs
)*1000 + (t2
->nsecs
- t1
->nsecs
)/1000000) > burstint_lcl
){
333 /* calculate buffer usage */
335 buffusagecalc(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
, double emptyspeed_lcl
)
342 buffer
= strinfo
->element
.buff
;
343 cur
= strinfo
->element
.last
;
355 nstime_delta(&delta
, &buffer
[cur
], &buffer
[prev
]);
356 timeelapsed
= nstime_to_sec(&delta
);
358 /* bytes added to buffer */
359 strinfo
->element
.buffusage
+=pinfo
->fd
->pkt_len
;
361 /* bytes cleared from buffer */
362 strinfo
->element
.buffusage
-= (uint32_t) (timeelapsed
* emptyspeed_lcl
/ 8);
364 if(strinfo
->element
.buffusage
< 0) strinfo
->element
.buffusage
=0;
365 if(strinfo
->element
.buffusage
> strinfo
->element
.topbuffusage
)
366 strinfo
->element
.topbuffusage
= strinfo
->element
.buffusage
;
367 /* check for buffer losses */
368 if((strinfo
->element
.buffusage
>= mcast_stream_bufferalarm
) && (strinfo
->element
.buffstatus
== 0)){
369 strinfo
->element
.buffstatus
= 1;
370 strinfo
->element
.numbuffalarms
++;
371 } else if(strinfo
->element
.buffusage
< mcast_stream_bufferalarm
){
372 strinfo
->element
.buffstatus
= 0;
378 /* sliding window calculation */
380 slidingwindow(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
)
385 buffer
= strinfo
->element
.buff
;
387 diff
= strinfo
->element
.last
- strinfo
->element
.first
;
388 if(diff
< 0) diff
+=buffsize
;
390 /* check if buffer is full */
391 if(diff
>= (buffsize
- 2)){
392 fprintf(stderr
, "Warning: capture buffer full\n");
393 strinfo
->element
.first
++;
394 if(strinfo
->element
.first
>= buffsize
) strinfo
->element
.first
= strinfo
->element
.first
% buffsize
;
398 buffer
[strinfo
->element
.last
] = pinfo
->rel_ts
;
399 while(comparetimes(&buffer
[strinfo
->element
.first
],
400 &buffer
[strinfo
->element
.last
], mcast_stream_burstint
)){
401 strinfo
->element
.first
++;
402 if(strinfo
->element
.first
>= buffsize
) strinfo
->element
.first
= strinfo
->element
.first
% buffsize
;
405 strinfo
->element
.burstsize
= diff
;
406 if(strinfo
->element
.burstsize
> strinfo
->element
.topburstsize
) {
407 strinfo
->element
.topburstsize
= strinfo
->element
.burstsize
;
408 strinfo
->element
.maxbw
= (double)(strinfo
->element
.topburstsize
) * 1000 / mcast_stream_burstint
* pinfo
->fd
->pkt_len
* 8;
411 strinfo
->element
.last
++;
412 if(strinfo
->element
.last
>= buffsize
) strinfo
->element
.last
= strinfo
->element
.last
% buffsize
;
414 if((strinfo
->element
.burstsize
>= mcast_stream_trigger
) && (strinfo
->element
.burststatus
== 0)){
415 strinfo
->element
.burststatus
= 1;
416 strinfo
->element
.numbursts
++;
417 } else if(strinfo
->element
.burstsize
< mcast_stream_trigger
){
418 strinfo
->element
.burststatus
= 0;
421 strinfo
->element
.count
++;