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 ~MulticastStatTreeWidgetItem()
65 // This probably doesn't outlive the stream_info, so perhaps we
66 // could shallow copy the addresses.
67 free_address_wmem(NULL
, &src_addr_
);
68 free_address_wmem(NULL
, &dst_addr_
);
71 void updateStreamInfo(const mcast_stream_info_t
*stream_info
) {
72 copy_address_wmem(NULL
, &src_addr_
, &stream_info
->src_addr
);
73 src_port_
= stream_info
->src_port
;
74 copy_address_wmem(NULL
, &dst_addr_
, &stream_info
->dest_addr
);
75 dst_port_
= stream_info
->dest_port
;
76 num_packets_
= stream_info
->npackets
;
77 avg_pps_
= stream_info
->apackets
;
78 avg_bw_
= stream_info
->average_bw
;
79 max_bw_
= stream_info
->element
.maxbw
;
80 top_burst_size_
= stream_info
->element
.topburstsize
;
81 num_bursts_
= stream_info
->element
.numbursts
;
82 top_buff_usage_
= stream_info
->element
.topbuffusage
;
83 num_buff_alarms_
= stream_info
->element
.numbuffalarms
;
89 setText(col_src_addr_
, address_to_qstring(&src_addr_
));
90 setText(col_src_port_
, QString::number(src_port_
));
91 setText(col_dst_addr_
, address_to_qstring(&dst_addr_
));
92 setText(col_dst_port_
, QString::number(dst_port_
));
93 setText(col_packets_
, QString::number(num_packets_
));
94 setText(col_packets_s_
, QString::number(avg_pps_
, 'f', 2));
95 setText(col_avg_bw_
, bits_s_to_qstring(avg_bw_
));
96 setText(col_max_bw_
, bits_s_to_qstring(max_bw_
));
97 setText(col_max_burst_
, QStringLiteral("%1 / %2ms").arg(top_burst_size_
).arg(mcast_stream_burstint
));
98 setText(col_burst_alarms_
, QString::number(num_bursts_
));
99 setText(col_max_buffers_
, bits_s_to_qstring(top_buff_usage_
));
100 setText(col_buffer_alarms_
, QString::number(num_buff_alarms_
));
103 bool operator< (const QTreeWidgetItem
&other
) const
105 if (other
.type() != mcast_table_type_
) return QTreeWidgetItem::operator< (other
);
106 const MulticastStatTreeWidgetItem
*other_row
= static_cast<const MulticastStatTreeWidgetItem
*>(&other
);
108 switch (treeWidget()->sortColumn()) {
110 return cmp_address(&src_addr_
, &other_row
->src_addr_
) < 0;
112 return src_port_
< other_row
->src_port_
;
114 return cmp_address(&dst_addr_
, &other_row
->dst_addr_
) < 0;
116 return dst_port_
< other_row
->dst_port_
;
118 return num_packets_
< other_row
->num_packets_
;
120 return avg_pps_
< other_row
->avg_pps_
;
122 return avg_bw_
< other_row
->avg_bw_
;
124 return max_bw_
< other_row
->max_bw_
;
126 return top_burst_size_
< other_row
->top_burst_size_
;
127 case col_burst_alarms_
:
128 return num_bursts_
< other_row
->num_bursts_
;
129 case col_max_buffers_
:
130 return top_buff_usage_
< other_row
->top_buff_usage_
;
131 case col_buffer_alarms_
:
132 return num_buff_alarms_
< other_row
->num_buff_alarms_
;
137 return QTreeWidgetItem::operator< (other
);
139 QList
<QVariant
> rowData() {
140 return QList
<QVariant
>()
141 << address_to_qstring(&src_addr_
) << src_port_
142 << address_to_qstring(&dst_addr_
) << dst_port_
143 << num_packets_
<< avg_pps_
144 << avg_bw_
<< max_bw_
145 << top_burst_size_
<< num_bursts_
146 << top_buff_usage_
<< num_buff_alarms_
;
148 const QString
filterExpression() {
151 if (src_addr_
.type
== AT_IPv6
) ip_version
= "v6";
153 const QString filter_expr
= QStringLiteral("(ip%1.src==%2 && udp.srcport==%3 && ip%1.dst==%4 && udp.dstport==%5)")
155 .arg(address_to_qstring(&src_addr_
))
157 .arg(address_to_qstring(&dst_addr_
))
167 unsigned num_packets_
;
174 int num_buff_alarms_
;
177 MulticastStatisticsDialog::MulticastStatisticsDialog(QWidget
&parent
, CaptureFile
&cf
, const char *filter
) :
178 TapParameterDialog(parent
, cf
)
180 setWindowSubtitle(tr("UDP Multicast Streams"));
181 loadGeometry(parent
.width() * 4 / 5, parent
.height() * 3 / 4, "MulticastStatisticsDialog");
183 tapinfo_
= new mcaststream_tapinfo_t();
184 tapinfo_
->user_data
= this;
185 tapinfo_
->tap_reset
= tapReset
;
186 tapinfo_
->tap_draw
= tapDraw
;
188 QStringList header_names
= QStringList()
189 << tr("Source Address") << tr("Source Port")
190 << tr("Destination Address") << tr("Destination Port")
191 << tr("Packets") << tr("Packets/s")
192 << tr("Avg BW (bps)") << tr("Max BW (bps)")
193 << tr("Max Burst") << tr("Burst Alarms")
194 << tr("Max Buffers (B)") << tr("Buffer Alarms");
196 statsTreeWidget()->setHeaderLabels(header_names
);
198 for (int col
= 0; col
< statsTreeWidget()->columnCount(); col
++) {
199 if (col
== col_src_addr_
|| col
== col_dst_addr_
) continue;
200 statsTreeWidget()->headerItem()->setTextAlignment(col
, Qt::AlignRight
);
203 burst_measurement_interval_le_
= new SyntaxLineEdit(this);
204 burst_alarm_threshold_le_
= new SyntaxLineEdit(this);
205 buffer_alarm_threshold_le_
= new SyntaxLineEdit(this);
206 stream_empty_speed_le_
= new SyntaxLineEdit(this);
207 total_empty_speed_le_
= new SyntaxLineEdit(this);
209 int filter_layout_idx
= verticalLayout()->indexOf(filterLayout()->widget());
210 QGridLayout
*param_grid
= new QGridLayout();
211 int one_em
= fontMetrics().height();
212 verticalLayout()->insertLayout(filter_layout_idx
, param_grid
);
214 // Label | LineEdit | | Label | LineEdit | | Label | LineEdit
216 param_grid
->setColumnMinimumWidth(2, one_em
* 2);
217 param_grid
->setColumnStretch(2, 1);
218 param_grid
->setColumnMinimumWidth(5, one_em
* 2);
219 param_grid
->setColumnStretch(5, 1);
220 param_grid
->addWidget(new QLabel(tr("Burst measurement interval (ms):")), 0, 0, Qt::AlignRight
);
221 param_grid
->addWidget(burst_measurement_interval_le_
, 0, 1);
222 param_grid
->addWidget(new QLabel(tr("Burst alarm threshold (packets):")), 0, 3, Qt::AlignRight
);
223 param_grid
->addWidget(burst_alarm_threshold_le_
, 0, 4);
224 param_grid
->addWidget(new QLabel(tr("Buffer alarm threshold (B):")), 0, 6, Qt::AlignRight
);
225 param_grid
->addWidget(buffer_alarm_threshold_le_
, 0, 7);
227 param_grid
->addWidget(new QLabel(tr("Stream empty speed (Kb/s):")), 1, 0, Qt::AlignRight
);
228 param_grid
->addWidget(stream_empty_speed_le_
, 1, 1);
229 param_grid
->addWidget(new QLabel(tr("Total empty speed (Kb/s):")), 1, 3, Qt::AlignRight
);
230 param_grid
->addWidget(total_empty_speed_le_
, 1, 4);
232 burst_measurement_interval_le_
->setText(QString::number(mcast_stream_burstint
));
233 burst_alarm_threshold_le_
->setText(QString::number(mcast_stream_trigger
));
234 buffer_alarm_threshold_le_
->setText(QString::number(mcast_stream_bufferalarm
));
235 stream_empty_speed_le_
->setText(QString::number(mcast_stream_emptyspeed
));
236 total_empty_speed_le_
->setText(QString::number(mcast_stream_cumulemptyspeed
));
238 line_edits_
= QList
<QWidget
*>()
239 << burst_measurement_interval_le_
<< burst_alarm_threshold_le_
240 << buffer_alarm_threshold_le_
<< stream_empty_speed_le_
241 << total_empty_speed_le_
;
243 foreach (QWidget
*w
, line_edits_
) {
244 QLineEdit
*line_edit
= qobject_cast
<QLineEdit
*>(w
);
245 line_edit
->setMinimumWidth(one_em
* 5);
246 connect(line_edit
, &QLineEdit::textEdited
, this, &MulticastStatisticsDialog::updateWidgets
);
252 setDisplayFilter(filter
);
255 connect(this, &MulticastStatisticsDialog::updateFilter
,
256 this, &MulticastStatisticsDialog::updateMulticastParameters
);
258 /* Register the tap listener */
259 GString
* error_string
= register_tap_listener_mcast_stream(tapinfo_
);
260 if (error_string
!= NULL
) {
261 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
262 "%s", error_string
->str
);
263 g_string_free(error_string
, TRUE
);
270 MulticastStatisticsDialog::~MulticastStatisticsDialog()
272 /* Remove the stream tap listener */
273 remove_tap_listener_mcast_stream(tapinfo_
);
275 /* Clean up memory used by stream tap */
276 mcaststream_reset(tapinfo_
);
281 void MulticastStatisticsDialog::tapReset(mcaststream_tapinfo_t
*tapinfo
)
283 MulticastStatisticsDialog
*ms_dlg
= dynamic_cast<MulticastStatisticsDialog
*>((MulticastStatisticsDialog
*)tapinfo
->user_data
);
284 if (!ms_dlg
|| !ms_dlg
->statsTreeWidget()) return;
286 ms_dlg
->statsTreeWidget()->clear();
289 void MulticastStatisticsDialog::tapDraw(mcaststream_tapinfo_t
*tapinfo
)
291 MulticastStatisticsDialog
*ms_dlg
= dynamic_cast<MulticastStatisticsDialog
*>((MulticastStatisticsDialog
*)tapinfo
->user_data
);
292 if (!ms_dlg
|| !ms_dlg
->statsTreeWidget()) return;
294 //Clear the tree because the list always starts from the beginning
295 ms_dlg
->statsTreeWidget()->clear();
297 // Add missing rows and update stats
299 for (GList
*cur
= g_list_first(tapinfo
->strinfo_list
); cur
; cur
= gxx_list_next(cur
)) {
300 mcast_stream_info_t
*stream_info
= gxx_list_data(mcast_stream_info_t
*, cur
);
301 if (!stream_info
) continue;
303 MulticastStatTreeWidgetItem
*ms_ti
;
304 QTreeWidgetItem
*ti
= ms_dlg
->statsTreeWidget()->topLevelItem(cur_row
);
306 ms_ti
= new MulticastStatTreeWidgetItem(ms_dlg
->statsTreeWidget());
307 for (int col
= 0; col
< ms_dlg
->statsTreeWidget()->columnCount(); col
++) {
308 if (col
== col_src_addr_
|| col
== col_dst_addr_
) continue;
309 ms_ti
->setTextAlignment(col
, Qt::AlignRight
);
312 ms_ti
= static_cast<MulticastStatTreeWidgetItem
*>(ti
);
315 ms_ti
->updateStreamInfo(stream_info
);
320 QList
<QVariant
> MulticastStatisticsDialog::treeItemData(QTreeWidgetItem
*ti
) const
322 MulticastStatTreeWidgetItem
*ms_ti
= dynamic_cast<MulticastStatTreeWidgetItem
*>(ti
);
324 return ms_ti
->rowData();
327 return QList
<QVariant
>();
331 const QString
MulticastStatisticsDialog::filterExpression()
334 if (statsTreeWidget()->selectedItems().count() > 0) {
335 QTreeWidgetItem
*ti
= statsTreeWidget()->selectedItems()[0];
337 MulticastStatTreeWidgetItem
*ms_ti
= static_cast<MulticastStatTreeWidgetItem
*>(ti
);
338 filter_expr
= ms_ti
->filterExpression();
343 void MulticastStatisticsDialog::updateWidgets()
346 bool enable_apply
= true;
347 bool enable_edits
= cap_file_
.isValid();
351 param
= burst_measurement_interval_le_
->text().toUInt(&ok
);
352 if (!ok
|| param
< 1 || param
> 1000) {
353 hint
+= tr("The burst interval must be between 1 and 1000. ");
354 enable_apply
= false;
355 burst_measurement_interval_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
357 burst_measurement_interval_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
360 param
= burst_alarm_threshold_le_
->text().toInt(&ok
);
361 if (!ok
|| param
< 1) {
362 hint
+= tr("The burst alarm threshold isn't valid. ");
363 enable_apply
= false;
364 burst_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
366 burst_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
369 param
= buffer_alarm_threshold_le_
->text().toInt(&ok
);
370 if (!ok
|| param
< 1) {
371 hint
+= tr("The buffer alarm threshold isn't valid. ");
372 enable_apply
= false;
373 buffer_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
375 buffer_alarm_threshold_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
378 param
= stream_empty_speed_le_
->text().toInt(&ok
);
379 if (!ok
|| param
< 1 || param
> 10000000) {
380 hint
+= tr("The stream empty speed should be between 1 and 10000000. ");
381 enable_apply
= false;
382 stream_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
384 stream_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
387 param
= total_empty_speed_le_
->text().toInt(&ok
);
388 if (!ok
|| param
< 1 || param
> 10000000) {
389 hint
+= tr("The total empty speed should be between 1 and 10000000. ");
390 enable_apply
= false;
391 total_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Invalid
);
393 total_empty_speed_le_
->setSyntaxState(SyntaxLineEdit::Valid
);
396 foreach (QWidget
*line_edit
, line_edits_
) {
397 line_edit
->setEnabled(enable_edits
);
400 applyFilterButton()->setEnabled(enable_apply
);
402 if (hint
.isEmpty() && tapinfo_
->allstreams
) {
403 const QString stats
= tr("%1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B")
404 .arg(statsTreeWidget()->topLevelItemCount())
405 .arg(bits_s_to_qstring(tapinfo_
->allstreams
->average_bw
))
406 .arg(bits_s_to_qstring(tapinfo_
->allstreams
->element
.maxbw
))
407 .arg(tapinfo_
->allstreams
->element
.topburstsize
)
408 .arg(mcast_stream_burstint
)
409 .arg(bits_s_to_qstring(tapinfo_
->allstreams
->element
.topbuffusage
));
412 hint
.prepend("<small><i>");
413 hint
.append("</i></small>");
415 TapParameterDialog::updateWidgets();
418 void MulticastStatisticsDialog::updateMulticastParameters()
423 param
= burst_measurement_interval_le_
->text().toUInt(&ok
);
424 if (ok
&& param
> 0 && param
<= 1000) {
425 mcast_stream_burstint
= (uint16_t) param
;
428 param
= burst_alarm_threshold_le_
->text().toInt(&ok
);
430 mcast_stream_trigger
= param
;
433 param
= buffer_alarm_threshold_le_
->text().toInt(&ok
);
434 if (ok
&& param
> 0) {
435 mcast_stream_bufferalarm
= param
;
438 param
= stream_empty_speed_le_
->text().toInt(&ok
);
439 if (ok
&& param
> 0 && param
<= 10000000) {
440 mcast_stream_emptyspeed
= param
;
443 param
= total_empty_speed_le_
->text().toInt(&ok
);
444 if (ok
&& param
> 0 && param
<= 10000000) {
445 mcast_stream_cumulemptyspeed
= param
;
449 void MulticastStatisticsDialog::fillTree()
451 QList
<QWidget
*> disable_widgets
= QList
<QWidget
*>()
452 << line_edits_
<< displayFilterLineEdit() << applyFilterButton();
454 foreach (QWidget
*w
, disable_widgets
) w
->setEnabled(false);
458 foreach (QWidget
*w
, disable_widgets
) w
->setEnabled(true);
459 for (int col
= 0; col
< statsTreeWidget()->columnCount() - 1; col
++) {
460 statsTreeWidget()->resizeColumnToContents(col
);
465 void MulticastStatisticsDialog::rescan()
467 bool was_registered
= tapinfo_
->is_registered
;
468 if (!tapinfo_
->is_registered
)
469 register_tap_listener_mcast_stream(tapinfo_
);
471 cf_retap_packets(cap_file_
.capFile());
474 remove_tap_listener_mcast_stream(tapinfo_
);
479 void MulticastStatisticsDialog::captureFileClosing()
481 /* Remove the stream tap listener */
482 remove_tap_listener_mcast_stream(tapinfo_
);
484 WiresharkDialog::captureFileClosing();
487 // Stat command + args
490 multicast_statistics_init(const char *args
, void*) {
491 QStringList args_l
= QString(args
).split(',');
493 if (args_l
.length() > 2) {
494 filter
= QStringList(args_l
.mid(2)).join(",").toUtf8();
496 mainApp
->emitStatCommandSignal("MulticastStatistics", filter
.constData(), NULL
);
499 static stat_tap_ui multicast_statistics_ui
= {
500 REGISTER_STAT_GROUP_GENERIC
,
503 multicast_statistics_init
,
510 void register_tap_listener_qt_multicast_statistics(void);
513 register_tap_listener_qt_multicast_statistics(void)
515 register_stat_tap_ui(&multicast_statistics_ui
, NULL
);