1 /* multicast_statistics_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "multicast_statistics_dialog.h"
12 #include <QFormLayout>
14 #include <QPushButton>
15 #include <QTreeWidget>
17 #include <ui/qt/utils/qt_ui_utils.h>
18 #include <ui/qt/widgets/syntax_line_edit.h>
19 #include "ui/simple_dialog.h"
22 #include "main_application.h"
40 mcast_table_type_
= 1000
43 class MulticastStatTreeWidgetItem
: public QTreeWidgetItem
46 MulticastStatTreeWidgetItem(QTreeWidget
*parent
) :
47 QTreeWidgetItem (parent
, mcast_table_type_
)
49 clear_address(&src_addr_
);
50 clear_address(&dst_addr_
);
63 void updateStreamInfo(const mcast_stream_info_t
*stream_info
) {
64 copy_address(&src_addr_
, &stream_info
->src_addr
);
65 src_port_
= stream_info
->src_port
;
66 copy_address(&dst_addr_
, &stream_info
->dest_addr
);
67 dst_port_
= stream_info
->dest_port
;
68 num_packets_
= stream_info
->npackets
;
69 avg_pps_
= stream_info
->apackets
;
70 avg_bw_
= stream_info
->average_bw
;
71 max_bw_
= stream_info
->element
.maxbw
;
72 top_burst_size_
= stream_info
->element
.topburstsize
;
73 num_bursts_
= stream_info
->element
.numbursts
;
74 top_buff_usage_
= stream_info
->element
.topbuffusage
;
75 num_buff_alarms_
= stream_info
->element
.numbuffalarms
;
81 setText(col_src_addr_
, address_to_qstring(&src_addr_
));
82 setText(col_src_port_
, QString::number(src_port_
));
83 setText(col_dst_addr_
, address_to_qstring(&dst_addr_
));
84 setText(col_dst_port_
, QString::number(dst_port_
));
85 setText(col_packets_
, QString::number(num_packets_
));
86 setText(col_packets_s_
, QString::number(avg_pps_
, 'f', 2));
87 setText(col_avg_bw_
, bits_s_to_qstring(avg_bw_
));
88 setText(col_max_bw_
, bits_s_to_qstring(max_bw_
));
89 setText(col_max_burst_
, QStringLiteral("%1 / %2ms").arg(top_burst_size_
).arg(mcast_stream_burstint
));
90 setText(col_burst_alarms_
, QString::number(num_bursts_
));
91 setText(col_max_buffers_
, bits_s_to_qstring(top_buff_usage_
));
92 setText(col_buffer_alarms_
, QString::number(num_buff_alarms_
));
95 bool operator< (const QTreeWidgetItem
&other
) const
97 if (other
.type() != mcast_table_type_
) return QTreeWidgetItem::operator< (other
);
98 const MulticastStatTreeWidgetItem
*other_row
= static_cast<const MulticastStatTreeWidgetItem
*>(&other
);
100 switch (treeWidget()->sortColumn()) {
102 return cmp_address(&src_addr_
, &other_row
->src_addr_
) < 0;
104 return src_port_
< other_row
->src_port_
;
106 return cmp_address(&dst_addr_
, &other_row
->dst_addr_
) < 0;
108 return dst_port_
< other_row
->dst_port_
;
110 return num_packets_
< other_row
->num_packets_
;
112 return avg_pps_
< other_row
->avg_pps_
;
114 return avg_bw_
< other_row
->avg_bw_
;
116 return max_bw_
< other_row
->max_bw_
;
118 return top_burst_size_
< other_row
->top_burst_size_
;
119 case col_burst_alarms_
:
120 return num_bursts_
< other_row
->num_bursts_
;
121 case col_max_buffers_
:
122 return top_buff_usage_
< other_row
->top_buff_usage_
;
123 case col_buffer_alarms_
:
124 return num_buff_alarms_
< other_row
->num_buff_alarms_
;
129 return QTreeWidgetItem::operator< (other
);
131 QList
<QVariant
> rowData() {
132 return QList
<QVariant
>()
133 << address_to_qstring(&src_addr_
) << src_port_
134 << address_to_qstring(&dst_addr_
) << dst_port_
135 << num_packets_
<< avg_pps_
136 << avg_bw_
<< max_bw_
137 << top_burst_size_
<< num_bursts_
138 << top_buff_usage_
<< num_buff_alarms_
;
140 const QString
filterExpression() {
143 if (src_addr_
.type
== AT_IPv6
) ip_version
= "v6";
145 const QString filter_expr
= QStringLiteral("(ip%1.src==%2 && udp.srcport==%3 && ip%1.dst==%4 && udp.dstport==%5)")
147 .arg(address_to_qstring(&src_addr_
))
149 .arg(address_to_qstring(&dst_addr_
))
159 unsigned num_packets_
;
166 int num_buff_alarms_
;
169 MulticastStatisticsDialog::MulticastStatisticsDialog(QWidget
&parent
, CaptureFile
&cf
, const char *filter
) :
170 TapParameterDialog(parent
, cf
)
172 setWindowSubtitle(tr("UDP Multicast Streams"));
173 loadGeometry(parent
.width() * 4 / 5, parent
.height() * 3 / 4, "MulticastStatisticsDialog");
175 tapinfo_
= new mcaststream_tapinfo_t();
176 tapinfo_
->user_data
= this;
177 tapinfo_
->tap_reset
= tapReset
;
178 tapinfo_
->tap_draw
= tapDraw
;
180 QStringList header_names
= QStringList()
181 << tr("Source Address") << tr("Source Port")
182 << tr("Destination Address") << tr("Destination Port")
183 << tr("Packets") << tr("Packets/s")
184 << tr("Avg BW (bps)") << tr("Max BW (bps)")
185 << tr("Max Burst") << tr("Burst Alarms")
186 << tr("Max Buffers (B)") << tr("Buffer Alarms");
188 statsTreeWidget()->setHeaderLabels(header_names
);
190 for (int col
= 0; col
< statsTreeWidget()->columnCount(); col
++) {
191 if (col
== col_src_addr_
|| col
== col_dst_addr_
) continue;
192 statsTreeWidget()->headerItem()->setTextAlignment(col
, Qt::AlignRight
);
195 burst_measurement_interval_le_
= new SyntaxLineEdit(this);
196 burst_alarm_threshold_le_
= new SyntaxLineEdit(this);
197 buffer_alarm_threshold_le_
= new SyntaxLineEdit(this);
198 stream_empty_speed_le_
= new SyntaxLineEdit(this);
199 total_empty_speed_le_
= new SyntaxLineEdit(this);
201 int filter_layout_idx
= verticalLayout()->indexOf(filterLayout()->widget());
202 QGridLayout
*param_grid
= new QGridLayout();
203 int one_em
= fontMetrics().height();
204 verticalLayout()->insertLayout(filter_layout_idx
, param_grid
);
206 // Label | LineEdit | | Label | LineEdit | | Label | LineEdit
208 param_grid
->setColumnMinimumWidth(2, one_em
* 2);
209 param_grid
->setColumnStretch(2, 1);
210 param_grid
->setColumnMinimumWidth(5, one_em
* 2);
211 param_grid
->setColumnStretch(5, 1);
212 param_grid
->addWidget(new QLabel(tr("Burst measurement interval (ms):")), 0, 0, Qt::AlignRight
);
213 param_grid
->addWidget(burst_measurement_interval_le_
, 0, 1);
214 param_grid
->addWidget(new QLabel(tr("Burst alarm threshold (packets):")), 0, 3, Qt::AlignRight
);
215 param_grid
->addWidget(burst_alarm_threshold_le_
, 0, 4);
216 param_grid
->addWidget(new QLabel(tr("Buffer alarm threshold (B):")), 0, 6, Qt::AlignRight
);
217 param_grid
->addWidget(buffer_alarm_threshold_le_
, 0, 7);
219 param_grid
->addWidget(new QLabel(tr("Stream empty speed (Kb/s):")), 1, 0, Qt::AlignRight
);
220 param_grid
->addWidget(stream_empty_speed_le_
, 1, 1);
221 param_grid
->addWidget(new QLabel(tr("Total empty speed (Kb/s):")), 1, 3, Qt::AlignRight
);
222 param_grid
->addWidget(total_empty_speed_le_
, 1, 4);
224 burst_measurement_interval_le_
->setText(QString::number(mcast_stream_burstint
));
225 burst_alarm_threshold_le_
->setText(QString::number(mcast_stream_trigger
));
226 buffer_alarm_threshold_le_
->setText(QString::number(mcast_stream_bufferalarm
));
227 stream_empty_speed_le_
->setText(QString::number(mcast_stream_emptyspeed
));
228 total_empty_speed_le_
->setText(QString::number(mcast_stream_cumulemptyspeed
));
230 line_edits_
= QList
<QWidget
*>()
231 << burst_measurement_interval_le_
<< burst_alarm_threshold_le_
232 << buffer_alarm_threshold_le_
<< stream_empty_speed_le_
233 << total_empty_speed_le_
;
235 foreach (QWidget
*w
, line_edits_
) {
236 QLineEdit
*line_edit
= qobject_cast
<QLineEdit
*>(w
);
237 line_edit
->setMinimumWidth(one_em
* 5);
238 connect(line_edit
, &QLineEdit::textEdited
, this, &MulticastStatisticsDialog::updateWidgets
);
244 setDisplayFilter(filter
);
247 connect(this, &MulticastStatisticsDialog::updateFilter
,
248 this, &MulticastStatisticsDialog::updateMulticastParameters
);
250 /* Register the tap listener */
251 GString
* error_string
= register_tap_listener_mcast_stream(tapinfo_
);
252 if (error_string
!= NULL
) {
253 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
254 "%s", error_string
->str
);
255 g_string_free(error_string
, TRUE
);
262 MulticastStatisticsDialog::~MulticastStatisticsDialog()
264 /* Remove the stream tap listener */
265 remove_tap_listener_mcast_stream(tapinfo_
);
267 /* Clean up memory used by stream tap */
268 mcaststream_reset(tapinfo_
);
273 void MulticastStatisticsDialog::tapReset(mcaststream_tapinfo_t
*tapinfo
)
275 MulticastStatisticsDialog
*ms_dlg
= dynamic_cast<MulticastStatisticsDialog
*>((MulticastStatisticsDialog
*)tapinfo
->user_data
);
276 if (!ms_dlg
|| !ms_dlg
->statsTreeWidget()) return;
278 ms_dlg
->statsTreeWidget()->clear();
281 void MulticastStatisticsDialog::tapDraw(mcaststream_tapinfo_t
*tapinfo
)
283 MulticastStatisticsDialog
*ms_dlg
= dynamic_cast<MulticastStatisticsDialog
*>((MulticastStatisticsDialog
*)tapinfo
->user_data
);
284 if (!ms_dlg
|| !ms_dlg
->statsTreeWidget()) return;
286 //Clear the tree because the list always starts from the beginning
287 ms_dlg
->statsTreeWidget()->clear();
289 // Add missing rows and update stats
291 for (GList
*cur
= g_list_first(tapinfo
->strinfo_list
); cur
; cur
= gxx_list_next(cur
)) {
292 mcast_stream_info_t
*stream_info
= gxx_list_data(mcast_stream_info_t
*, cur
);
293 if (!stream_info
) continue;
295 MulticastStatTreeWidgetItem
*ms_ti
;
296 QTreeWidgetItem
*ti
= ms_dlg
->statsTreeWidget()->topLevelItem(cur_row
);
298 ms_ti
= new MulticastStatTreeWidgetItem(ms_dlg
->statsTreeWidget());
299 for (int col
= 0; col
< ms_dlg
->statsTreeWidget()->columnCount(); col
++) {
300 if (col
== col_src_addr_
|| col
== col_dst_addr_
) continue;
301 ms_ti
->setTextAlignment(col
, Qt::AlignRight
);
304 ms_ti
= static_cast<MulticastStatTreeWidgetItem
*>(ti
);
307 ms_ti
->updateStreamInfo(stream_info
);
312 QList
<QVariant
> MulticastStatisticsDialog::treeItemData(QTreeWidgetItem
*ti
) const
314 MulticastStatTreeWidgetItem
*ms_ti
= dynamic_cast<MulticastStatTreeWidgetItem
*>(ti
);
316 return ms_ti
->rowData();
319 return QList
<QVariant
>();
323 const QString
MulticastStatisticsDialog::filterExpression()
326 if (statsTreeWidget()->selectedItems().count() > 0) {
327 QTreeWidgetItem
*ti
= statsTreeWidget()->selectedItems()[0];
329 MulticastStatTreeWidgetItem
*ms_ti
= static_cast<MulticastStatTreeWidgetItem
*>(ti
);
330 filter_expr
= ms_ti
->filterExpression();
335 void MulticastStatisticsDialog::updateWidgets()
338 bool enable_apply
= true;
339 bool enable_edits
= cap_file_
.isValid();
343 param
= burst_measurement_interval_le_
->text().toUInt(&ok
);
344 if (!ok
|| param
< 1 || param
> 1000) {
345 hint
+= tr("The burst interval must be between 1 and 1000. ");
346 enable_apply
= false;
347 burst_measurement_interval_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
349 burst_measurement_interval_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
352 param
= burst_alarm_threshold_le_
->text().toInt(&ok
);
353 if (!ok
|| param
< 1) {
354 hint
+= tr("The burst alarm threshold isn't valid. ");
355 enable_apply
= false;
356 burst_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
358 burst_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
361 param
= buffer_alarm_threshold_le_
->text().toInt(&ok
);
362 if (!ok
|| param
< 1) {
363 hint
+= tr("The buffer alarm threshold isn't valid. ");
364 enable_apply
= false;
365 buffer_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
367 buffer_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
370 param
= stream_empty_speed_le_
->text().toInt(&ok
);
371 if (!ok
|| param
< 1 || param
> 10000000) {
372 hint
+= tr("The stream empty speed should be between 1 and 10000000. ");
373 enable_apply
= false;
374 stream_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
376 stream_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
379 param
= total_empty_speed_le_
->text().toInt(&ok
);
380 if (!ok
|| param
< 1 || param
> 10000000) {
381 hint
+= tr("The total empty speed should be between 1 and 10000000. ");
382 enable_apply
= false;
383 total_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
385 total_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
388 foreach (QWidget
*line_edit
, line_edits_
) {
389 line_edit
->setEnabled(enable_edits
);
392 applyFilterButton()->setEnabled(enable_apply
);
394 if (hint
.isEmpty() && tapinfo_
->allstreams
) {
395 const QString stats
= tr("%1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B")
396 .arg(statsTreeWidget()->topLevelItemCount())
397 .arg(bits_s_to_qstring(tapinfo_
->allstreams
->average_bw
))
398 .arg(bits_s_to_qstring(tapinfo_
->allstreams
->element
.maxbw
))
399 .arg(tapinfo_
->allstreams
->element
.topburstsize
)
400 .arg(mcast_stream_burstint
)
401 .arg(bits_s_to_qstring(tapinfo_
->allstreams
->element
.topbuffusage
));
404 hint
.prepend("<small><i>");
405 hint
.append("</i></small>");
407 TapParameterDialog::updateWidgets();
410 void MulticastStatisticsDialog::updateMulticastParameters()
415 param
= burst_measurement_interval_le_
->text().toUInt(&ok
);
416 if (ok
&& param
> 0 && param
<= 1000) {
417 mcast_stream_burstint
= (uint16_t) param
;
420 param
= burst_alarm_threshold_le_
->text().toInt(&ok
);
422 mcast_stream_trigger
= param
;
425 param
= buffer_alarm_threshold_le_
->text().toInt(&ok
);
426 if (ok
&& param
> 0) {
427 mcast_stream_bufferalarm
= param
;
430 param
= stream_empty_speed_le_
->text().toInt(&ok
);
431 if (ok
&& param
> 0 && param
<= 10000000) {
432 mcast_stream_emptyspeed
= param
;
435 param
= total_empty_speed_le_
->text().toInt(&ok
);
436 if (ok
&& param
> 0 && param
<= 10000000) {
437 mcast_stream_cumulemptyspeed
= param
;
441 void MulticastStatisticsDialog::fillTree()
443 QList
<QWidget
*> disable_widgets
= QList
<QWidget
*>()
444 << line_edits_
<< displayFilterLineEdit() << applyFilterButton();
446 foreach (QWidget
*w
, disable_widgets
) w
->setEnabled(false);
450 foreach (QWidget
*w
, disable_widgets
) w
->setEnabled(true);
451 for (int col
= 0; col
< statsTreeWidget()->columnCount() - 1; col
++) {
452 statsTreeWidget()->resizeColumnToContents(col
);
457 void MulticastStatisticsDialog::rescan()
459 bool was_registered
= tapinfo_
->is_registered
;
460 if (!tapinfo_
->is_registered
)
461 register_tap_listener_mcast_stream(tapinfo_
);
463 cf_retap_packets(cap_file_
.capFile());
466 remove_tap_listener_mcast_stream(tapinfo_
);
471 void MulticastStatisticsDialog::captureFileClosing()
473 /* Remove the stream tap listener */
474 remove_tap_listener_mcast_stream(tapinfo_
);
476 WiresharkDialog::captureFileClosing();
479 // Stat command + args
482 multicast_statistics_init(const char *args
, void*) {
483 QStringList args_l
= QString(args
).split(',');
485 if (args_l
.length() > 2) {
486 filter
= QStringList(args_l
.mid(2)).join(",").toUtf8();
488 mainApp
->emitStatCommandSignal("MulticastStatistics", filter
.constData(), NULL
);
491 static stat_tap_ui multicast_statistics_ui
= {
492 REGISTER_STAT_GROUP_GENERIC
,
495 multicast_statistics_init
,
502 void register_tap_listener_qt_multicast_statistics(void);
505 register_tap_listener_qt_multicast_statistics(void)
507 register_stat_tap_ui(&multicast_statistics_ui
, NULL
);