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>
34 #include <epan/dissectors/packet-udp.h>
36 #include "ui/mcast_stream.h"
38 int32_t mcast_stream_trigger
= 50; /* limit for triggering the burst alarm (in packets per second) */
39 int32_t mcast_stream_bufferalarm
= 10000; /* limit for triggering the buffer alarm (in bytes) */
40 uint16_t mcast_stream_burstint
= 100; /* burst interval in ms */
41 int32_t mcast_stream_emptyspeed
= 5000; /* outgoing speed for single stream (kbps)*/
42 int32_t mcast_stream_cumulemptyspeed
= 100000; /* outgoing speed for all streams (kbps)*/
44 /* sliding window and buffer usage */
45 static int32_t buffsize
= (int)((double)MAX_SPEED
* 100 / 1000) * 2;
46 static uint16_t comparetimes(nstime_t
*t1
, nstime_t
*t2
, uint16_t burstint_lcl
);
47 static void buffusagecalc(mcast_stream_info_t
*strinfo
, uint32_t nbytes
, double emptyspeed_lcl
);
48 static void slidingwindow(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
, uint32_t nbytes
);
50 /****************************************************************************/
51 /* GCompareFunc style comparison function for _mcast_stream_info */
53 mcast_stream_info_cmp(const void *aa
, const void *bb
)
55 const struct _mcast_stream_info
* a
= (const struct _mcast_stream_info
*)aa
;
56 const struct _mcast_stream_info
* b
= (const struct _mcast_stream_info
*)bb
;
60 if (a
==NULL
|| b
==NULL
)
62 if (addresses_equal(&(a
->src_addr
), &(b
->src_addr
))
63 && (a
->src_port
== b
->src_port
)
64 && addresses_equal(&(a
->dest_addr
), &(b
->dest_addr
))
65 && (a
->dest_port
== b
->dest_port
))
73 /****************************************************************************/
74 /* when there is a [re]reading of packet's */
76 mcaststream_reset(mcaststream_tapinfo_t
*tapinfo
)
79 mcast_stream_info_t
*strinfo
;
81 /* free the data items first */
82 list
= g_list_first(tapinfo
->strinfo_list
);
85 strinfo
= (mcast_stream_info_t
*)list
->data
;
86 g_free(strinfo
->element
.buff
);
87 free_address_wmem(NULL
, &strinfo
->src_addr
);
88 free_address_wmem(NULL
, &strinfo
->dest_addr
);
90 list
= g_list_next(list
);
92 g_list_free(tapinfo
->strinfo_list
);
93 tapinfo
->strinfo_list
= NULL
;
95 if (tapinfo
->allstreams
!= NULL
) {
96 g_free(tapinfo
->allstreams
->element
.buff
);
97 g_free(tapinfo
->allstreams
);
98 tapinfo
->allstreams
= NULL
;
101 tapinfo
->npackets
= 0;
107 mcaststream_reset_cb(void *ti_ptr
)
109 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)ti_ptr
;
111 if (tapinfo
->tap_reset
) {
112 tapinfo
->tap_reset(tapinfo
);
114 mcaststream_reset(tapinfo
);
118 /****************************************************************************/
119 /* redraw the output */
121 mcaststream_draw(void *ti_ptr
)
123 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)ti_ptr
;
124 /* XXX: see mcaststream_on_update in mcast_streams_dlg.c for comments
125 g_signal_emit_by_name(top_level, "signal_mcaststream_update");
127 if (tapinfo
&& tapinfo
->tap_draw
) {
128 tapinfo
->tap_draw(tapinfo
);
135 /****************************************************************************/
136 /* whenever a udp packet is seen by the tap listener */
138 mcaststream_packet(void *arg
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *arg2 _U_
, tap_flags_t flags _U_
)
140 mcaststream_tapinfo_t
*tapinfo
= (mcaststream_tapinfo_t
*)arg
;
141 const e_udphdr
*udphdr
= (const e_udphdr
*)arg2
;
142 mcast_stream_info_t tmp_strinfo
;
143 mcast_stream_info_t
*strinfo
= NULL
;
150 * Restrict statistics to standard multicast IPv4 and IPv6 addresses.
151 * We might want to check for and allow ethernet addresses starting
152 * with 01:00:05 and 33:33 as well.
154 switch (pinfo
->net_dst
.type
) {
157 if (pinfo
->net_dst
.len
== 0 || (((const uint8_t*)pinfo
->net_dst
.data
)[0] & 0xf0) != 0xe0)
158 return TAP_PACKET_DONT_REDRAW
;
162 /* XXX This includes DHCPv6. */
163 if (pinfo
->net_dst
.len
== 0 || ((const uint8_t*)pinfo
->net_dst
.data
)[0] != 0xff)
164 return TAP_PACKET_DONT_REDRAW
;
167 return TAP_PACKET_DONT_REDRAW
;
170 /* New payload bytes - UDP datagram length (including UDP header but not
171 * IP header nor link-layer frame header). This is a uint32_t because
172 * of UDP jumbograms (RFC 2675) but overflow probably causes unexpected
173 * results if jumbograms over INT32_MAX are used. (They aren't.)
175 nbytes
= udphdr
->uh_ulen
;
177 /* shallow copy information to temporary key for lookup */
178 copy_address_shallow(&(tmp_strinfo
.src_addr
), &(pinfo
->net_src
));
179 tmp_strinfo
.src_port
= pinfo
->srcport
;
180 copy_address_shallow(&(tmp_strinfo
.dest_addr
), &(pinfo
->net_dst
));
181 tmp_strinfo
.dest_port
= pinfo
->destport
;
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", address_to_display(NULL, &(pinfo->src)),
198 pinfo->srcport, address_to_display(NULL, &(pinfo->dst)), pinfo->destport);*/
200 strinfo
= g_new0(mcast_stream_info_t
, 1);
202 strinfo
->src_port
= pinfo
->srcport
;
203 strinfo
->dest_port
= pinfo
->destport
;
204 copy_address_wmem(NULL
, &(strinfo
->src_addr
), &(pinfo
->net_src
));
205 copy_address_wmem(NULL
, &(strinfo
->dest_addr
), &(pinfo
->net_dst
));
206 strinfo
->first_frame_num
= pinfo
->num
;
207 nstime_copy(&strinfo
->start_abs
, &pinfo
->abs_ts
);
208 nstime_copy(&strinfo
->start_rel
, &pinfo
->rel_ts
);
210 /* slidingwindow and buffer parameters */
211 strinfo
->element
.buff
= g_new(nstime_t
, buffsize
);
212 strinfo
->element
.burstsize
=1;
213 strinfo
->element
.topburstsize
=1;
214 strinfo
->element
.count
=1;
215 strinfo
->element
.buffusage
=nbytes
;
216 strinfo
->element
.topbuffusage
=nbytes
;
218 tapinfo
->strinfo_list
= g_list_append(tapinfo
->strinfo_list
, strinfo
);
220 /* set time with the first packet */
221 if (tapinfo
->npackets
== 0) {
222 tapinfo
->allstreams
= g_new0(mcast_stream_info_t
, 1);
223 tapinfo
->allstreams
->element
.buff
=
224 g_new(nstime_t
, buffsize
);
225 nstime_copy(&tapinfo
->allstreams
->start_rel
, &pinfo
->rel_ts
);
226 tapinfo
->allstreams
->element
.burstsize
=1;
227 tapinfo
->allstreams
->element
.topburstsize
=1;
228 tapinfo
->allstreams
->element
.count
=1;
229 tapinfo
->allstreams
->element
.buffusage
=nbytes
;
230 tapinfo
->allstreams
->element
.topbuffusage
=nbytes
;
234 /* time between first and last packet in the group */
235 strinfo
->stop_rel
= pinfo
->rel_ts
;
236 nstime_delta(&delta
, &strinfo
->stop_rel
, &strinfo
->start_rel
);
237 deltatime
= nstime_to_sec(&delta
);
239 /* calculate average bandwidth for this stream */
240 strinfo
->total_bytes
= strinfo
->total_bytes
+ nbytes
;
242 /* increment the packets counter for this stream and calculate average pps */
243 ++(strinfo
->npackets
);
246 strinfo
->apackets
= strinfo
->npackets
/ deltatime
;
247 strinfo
->average_bw
= ((double)(strinfo
->total_bytes
*8) / deltatime
);
249 strinfo
->apackets
= strinfo
->average_bw
= 0.0;
252 /* time between first and last packet in any group */
253 tapinfo
->allstreams
->stop_rel
= pinfo
->rel_ts
;
254 nstime_delta(&delta
, &tapinfo
->allstreams
->stop_rel
, &tapinfo
->allstreams
->start_rel
);
255 deltatime
= nstime_to_sec(&delta
);
257 /* increment the packets counter of all streams */
258 ++(tapinfo
->npackets
);
260 /* calculate average bandwidth for all streams */
261 tapinfo
->allstreams
->total_bytes
= tapinfo
->allstreams
->total_bytes
+ nbytes
;
263 tapinfo
->allstreams
->average_bw
= ((double)(tapinfo
->allstreams
->total_bytes
*8) / deltatime
);
265 /* sliding window and buffercalc for this group*/
266 slidingwindow(strinfo
, pinfo
, nbytes
);
267 buffusagecalc(strinfo
, nbytes
, mcast_stream_emptyspeed
*1000);
268 /* sliding window and buffercalc for all groups */
269 slidingwindow(tapinfo
->allstreams
, pinfo
, nbytes
);
270 buffusagecalc(tapinfo
->allstreams
, nbytes
, mcast_stream_cumulemptyspeed
*1000);
271 /* end of sliding window */
273 return TAP_PACKET_REDRAW
; /* refresh output */
277 /****************************************************************************/
279 /****************************************************************************/
281 /****************************************************************************/
283 remove_tap_listener_mcast_stream(mcaststream_tapinfo_t
*tapinfo
)
285 if (tapinfo
&& tapinfo
->is_registered
) {
286 remove_tap_listener(tapinfo
);
287 tapinfo
->is_registered
= false;
292 /****************************************************************************/
294 register_tap_listener_mcast_stream(mcaststream_tapinfo_t
*tapinfo
)
296 GString
*error_string
;
301 if (tapinfo
->is_registered
) {
305 error_string
= register_tap_listener("udp", tapinfo
,
306 NULL
, 0, mcaststream_reset_cb
, mcaststream_packet
,
307 mcaststream_draw
, NULL
);
309 if (NULL
== error_string
) {
310 tapinfo
->is_registered
= true;
315 /*******************************************************************************/
316 /* sliding window and buffer calculations */
318 /* compare two times */
320 comparetimes(nstime_t
*t1
, nstime_t
*t2
, uint16_t burstint_lcl
)
322 if(((t2
->secs
- t1
->secs
)*1000 + (t2
->nsecs
- t1
->nsecs
)/1000000) > burstint_lcl
){
329 /* calculate buffer usage */
331 buffusagecalc(mcast_stream_info_t
*strinfo
, uint32_t nbytes
, double emptyspeed_lcl
)
338 buffer
= strinfo
->element
.buff
;
339 cur
= strinfo
->element
.last
;
351 nstime_delta(&delta
, &buffer
[cur
], &buffer
[prev
]);
352 timeelapsed
= nstime_to_sec(&delta
);
354 /* bytes added to buffer */
355 strinfo
->element
.buffusage
+=nbytes
;
357 /* bytes cleared from buffer */
358 strinfo
->element
.buffusage
-= (uint32_t) (timeelapsed
* emptyspeed_lcl
/ 8);
360 if(strinfo
->element
.buffusage
< 0) strinfo
->element
.buffusage
=0;
361 if(strinfo
->element
.buffusage
> strinfo
->element
.topbuffusage
)
362 strinfo
->element
.topbuffusage
= strinfo
->element
.buffusage
;
363 /* check for buffer losses */
364 if((strinfo
->element
.buffusage
>= mcast_stream_bufferalarm
) && (strinfo
->element
.buffstatus
== 0)){
365 strinfo
->element
.buffstatus
= 1;
366 strinfo
->element
.numbuffalarms
++;
367 } else if(strinfo
->element
.buffusage
< mcast_stream_bufferalarm
){
368 strinfo
->element
.buffstatus
= 0;
374 /* sliding window calculation */
376 slidingwindow(mcast_stream_info_t
*strinfo
, packet_info
*pinfo
, uint32_t nbytes
)
381 buffer
= strinfo
->element
.buff
;
383 diff
= strinfo
->element
.last
- strinfo
->element
.first
;
384 if(diff
< 0) diff
+=buffsize
;
386 /* check if buffer is full */
387 if(diff
>= (buffsize
- 2)){
388 fprintf(stderr
, "Warning: capture buffer full\n");
389 strinfo
->element
.first
++;
390 if(strinfo
->element
.first
>= buffsize
) strinfo
->element
.first
= strinfo
->element
.first
% buffsize
;
394 buffer
[strinfo
->element
.last
] = pinfo
->rel_ts
;
395 while(comparetimes(&buffer
[strinfo
->element
.first
],
396 &buffer
[strinfo
->element
.last
], mcast_stream_burstint
)){
397 strinfo
->element
.first
++;
398 if(strinfo
->element
.first
>= buffsize
) strinfo
->element
.first
= strinfo
->element
.first
% buffsize
;
401 strinfo
->element
.burstsize
= diff
;
402 if(strinfo
->element
.burstsize
> strinfo
->element
.topburstsize
) {
403 strinfo
->element
.topburstsize
= strinfo
->element
.burstsize
;
404 strinfo
->element
.maxbw
= (double)(strinfo
->element
.topburstsize
) * 1000 / mcast_stream_burstint
* nbytes
* 8;
407 strinfo
->element
.last
++;
408 if(strinfo
->element
.last
>= buffsize
) strinfo
->element
.last
= strinfo
->element
.last
% buffsize
;
410 if((strinfo
->element
.burstsize
>= mcast_stream_trigger
) && (strinfo
->element
.burststatus
== 0)){
411 strinfo
->element
.burststatus
= 1;
412 strinfo
->element
.numbursts
++;
413 } else if(strinfo
->element
.burstsize
< mcast_stream_trigger
){
414 strinfo
->element
.burststatus
= 0;
417 strinfo
->element
.count
++;