diff options
Diffstat (limited to 'ui/qt/multicast_statistics_dialog.cpp')
-rw-r--r-- | ui/qt/multicast_statistics_dialog.cpp | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/ui/qt/multicast_statistics_dialog.cpp b/ui/qt/multicast_statistics_dialog.cpp new file mode 100644 index 0000000..cca7478 --- /dev/null +++ b/ui/qt/multicast_statistics_dialog.cpp @@ -0,0 +1,510 @@ +/* multicast_statistics_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "multicast_statistics_dialog.h" + +#include <QFormLayout> +#include <QLabel> +#include <QPushButton> +#include <QTreeWidget> + +#include <ui/qt/utils/qt_ui_utils.h> +#include <ui/qt/widgets/syntax_line_edit.h> +#include "ui/simple_dialog.h" +#include <file.h> + +#include "main_application.h" + +enum { + col_src_addr_, + col_src_port_, + col_dst_addr_, + col_dst_port_, + col_packets_, + col_packets_s_, + col_avg_bw_, + col_max_bw_, + col_max_burst_, + col_burst_alarms_, + col_max_buffers_, + col_buffer_alarms_ +}; + +enum { + mcast_table_type_ = 1000 +}; + +class MulticastStatTreeWidgetItem : public QTreeWidgetItem +{ +public: + MulticastStatTreeWidgetItem(QTreeWidget *parent) : + QTreeWidgetItem (parent, mcast_table_type_) + { + clear_address(&src_addr_); + clear_address(&dst_addr_); + src_port_ = 0; + dst_port_ = 0; + num_packets_ = 0; + avg_pps_ = 0; + avg_bw_ = 0; + max_bw_ = 0; + top_burst_size_ = 0; + num_bursts_ = 0; + top_buff_usage_ = 0; + num_buff_alarms_ = 0; + } + + void updateStreamInfo(const mcast_stream_info_t *stream_info) { + copy_address(&src_addr_, &stream_info->src_addr); + src_port_ = stream_info->src_port; + copy_address(&dst_addr_, &stream_info->dest_addr); + dst_port_ = stream_info->dest_port; + num_packets_ = stream_info->npackets; + avg_pps_ = stream_info->apackets; + avg_bw_ = stream_info->average_bw; + max_bw_ = stream_info->element.maxbw; + top_burst_size_ = stream_info->element.topburstsize; + num_bursts_ = stream_info->element.numbursts; + top_buff_usage_ = stream_info->element.topbuffusage; + num_buff_alarms_ = stream_info->element.numbuffalarms; + + draw(); + } + + void draw() { + setText(col_src_addr_, address_to_qstring(&src_addr_)); + setText(col_src_port_, QString::number(src_port_)); + setText(col_dst_addr_, address_to_qstring(&dst_addr_)); + setText(col_dst_port_, QString::number(dst_port_)); + setText(col_packets_, QString::number(num_packets_)); + setText(col_packets_s_, QString::number(avg_pps_, 'f', 2)); + setText(col_avg_bw_, bits_s_to_qstring(avg_bw_)); + setText(col_max_bw_, bits_s_to_qstring(max_bw_)); + setText(col_max_burst_, QString("%1 / %2ms").arg(top_burst_size_).arg(mcast_stream_burstint)); + setText(col_burst_alarms_, QString::number(num_bursts_)); + setText(col_max_buffers_, bits_s_to_qstring(top_buff_usage_)); + setText(col_buffer_alarms_, QString::number(num_buff_alarms_)); + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != mcast_table_type_) return QTreeWidgetItem::operator< (other); + const MulticastStatTreeWidgetItem *other_row = static_cast<const MulticastStatTreeWidgetItem *>(&other); + + switch (treeWidget()->sortColumn()) { + case col_src_addr_: + return cmp_address(&src_addr_, &other_row->src_addr_) < 0; + case col_src_port_: + return src_port_ < other_row->src_port_; + case col_dst_addr_: + return cmp_address(&dst_addr_, &other_row->dst_addr_) < 0; + case col_dst_port_: + return dst_port_ < other_row->dst_port_; + case col_packets_: + return num_packets_ < other_row->num_packets_; + case col_packets_s_: + return avg_pps_ < other_row->avg_pps_; + case col_avg_bw_: + return avg_bw_ < other_row->avg_bw_; + case col_max_bw_: + return max_bw_ < other_row->max_bw_; + case col_max_burst_: + return top_burst_size_ < other_row->top_burst_size_; + case col_burst_alarms_: + return num_bursts_ < other_row->num_bursts_; + case col_max_buffers_: + return top_buff_usage_ < other_row->top_buff_usage_; + case col_buffer_alarms_: + return num_buff_alarms_ < other_row->num_buff_alarms_; + default: + break; + } + + return QTreeWidgetItem::operator< (other); + } + QList<QVariant> rowData() { + return QList<QVariant>() + << address_to_qstring(&src_addr_) << src_port_ + << address_to_qstring(&dst_addr_) << dst_port_ + << num_packets_ << avg_pps_ + << avg_bw_ << max_bw_ + << top_burst_size_ << num_bursts_ + << top_buff_usage_ << num_buff_alarms_; + } + const QString filterExpression() { + QString ip_version; + + if (src_addr_.type == AT_IPv6) ip_version = "v6"; + + const QString filter_expr = QString("(ip%1.src==%2 && udp.srcport==%3 && ip%1.dst==%4 && udp.dstport==%5)") + .arg(ip_version) + .arg(address_to_qstring(&src_addr_)) + .arg(src_port_) + .arg(address_to_qstring(&dst_addr_)) + .arg(dst_port_); + return filter_expr; + } + +private: + address src_addr_; + guint16 src_port_; + address dst_addr_; + guint16 dst_port_; + unsigned num_packets_; + double avg_pps_; + double avg_bw_; + double max_bw_; + int top_burst_size_; + int num_bursts_; + int top_buff_usage_; + int num_buff_alarms_; +}; + +MulticastStatisticsDialog::MulticastStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) : + TapParameterDialog(parent, cf) +{ + setWindowSubtitle(tr("UDP Multicast Streams")); + loadGeometry(parent.width() * 4 / 5, parent.height() * 3 / 4, "MulticastStatisticsDialog"); + + tapinfo_ = new mcaststream_tapinfo_t(); + tapinfo_->user_data = this; + tapinfo_->tap_reset = tapReset; + tapinfo_->tap_draw = tapDraw; + + QStringList header_names = QStringList() + << tr("Source Address") << tr("Source Port") + << tr("Destination Address") << tr("Destination Port") + << tr("Packets") << tr("Packets/s") + << tr("Avg BW (bps)") << tr("Max BW (bps)") + << tr("Max Burst") << tr("Burst Alarms") + << tr("Max Buffers (B)") << tr("Buffer Alarms"); + + statsTreeWidget()->setHeaderLabels(header_names); + + for (int col = 0; col < statsTreeWidget()->columnCount(); col++) { + if (col == col_src_addr_ || col == col_dst_addr_) continue; + statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); + } + + burst_measurement_interval_le_ = new SyntaxLineEdit(this); + burst_alarm_threshold_le_ = new SyntaxLineEdit(this); + buffer_alarm_threshold_le_ = new SyntaxLineEdit(this); + stream_empty_speed_le_ = new SyntaxLineEdit(this); + total_empty_speed_le_ = new SyntaxLineEdit(this); + + int filter_layout_idx = verticalLayout()->indexOf(filterLayout()->widget()); + QGridLayout *param_grid = new QGridLayout(); + int one_em = fontMetrics().height(); + verticalLayout()->insertLayout(filter_layout_idx, param_grid); + + // Label | LineEdit | | Label | LineEdit | | Label | LineEdit + // 0 1 2 3 4 5 6 7 + param_grid->setColumnMinimumWidth(2, one_em * 2); + param_grid->setColumnStretch(2, 1); + param_grid->setColumnMinimumWidth(5, one_em * 2); + param_grid->setColumnStretch(5, 1); + param_grid->addWidget(new QLabel(tr("Burst measurement interval (ms):")), 0, 0, Qt::AlignRight); + param_grid->addWidget(burst_measurement_interval_le_, 0, 1); + param_grid->addWidget(new QLabel(tr("Burst alarm threshold (packets):")), 0, 3, Qt::AlignRight); + param_grid->addWidget(burst_alarm_threshold_le_, 0, 4); + param_grid->addWidget(new QLabel(tr("Buffer alarm threshold (B):")), 0, 6, Qt::AlignRight); + param_grid->addWidget(buffer_alarm_threshold_le_, 0, 7); + + param_grid->addWidget(new QLabel(tr("Stream empty speed (Kb/s):")), 1, 0, Qt::AlignRight); + param_grid->addWidget(stream_empty_speed_le_, 1, 1); + param_grid->addWidget(new QLabel(tr("Total empty speed (Kb/s):")), 1, 3, Qt::AlignRight); + param_grid->addWidget(total_empty_speed_le_, 1, 4); + + burst_measurement_interval_le_->setText(QString::number(mcast_stream_burstint)); + burst_alarm_threshold_le_->setText(QString::number(mcast_stream_trigger)); + buffer_alarm_threshold_le_->setText(QString::number(mcast_stream_bufferalarm)); + stream_empty_speed_le_->setText(QString::number(mcast_stream_emptyspeed)); + total_empty_speed_le_->setText(QString::number(mcast_stream_cumulemptyspeed)); + + line_edits_ = QList<QWidget *>() + << burst_measurement_interval_le_ << burst_alarm_threshold_le_ + << buffer_alarm_threshold_le_ << stream_empty_speed_le_ + << total_empty_speed_le_; + + foreach (QWidget *w, line_edits_) { + QLineEdit *line_edit = qobject_cast<QLineEdit *>(w); + line_edit->setMinimumWidth(one_em * 5); + connect(line_edit, &QLineEdit::textEdited, this, &MulticastStatisticsDialog::updateWidgets); + } + + addFilterActions(); + + if (filter) { + setDisplayFilter(filter); + } + + connect(this, &MulticastStatisticsDialog::updateFilter, + this, &MulticastStatisticsDialog::updateMulticastParameters); + + /* Register the tap listener */ + GString * error_string = register_tap_listener_mcast_stream(tapinfo_); + if (error_string != NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "%s", error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + updateWidgets(); +} + +MulticastStatisticsDialog::~MulticastStatisticsDialog() +{ + /* Remove the stream tap listener */ + remove_tap_listener_mcast_stream(tapinfo_); + + /* Clean up memory used by stream tap */ + mcaststream_reset(tapinfo_); + + delete tapinfo_; +} + +void MulticastStatisticsDialog::tapReset(mcaststream_tapinfo_t *tapinfo) +{ + MulticastStatisticsDialog *ms_dlg = dynamic_cast<MulticastStatisticsDialog *>((MulticastStatisticsDialog*)tapinfo->user_data); + if (!ms_dlg || !ms_dlg->statsTreeWidget()) return; + + ms_dlg->statsTreeWidget()->clear(); +} + +void MulticastStatisticsDialog::tapDraw(mcaststream_tapinfo_t *tapinfo) +{ + MulticastStatisticsDialog *ms_dlg = dynamic_cast<MulticastStatisticsDialog *>((MulticastStatisticsDialog*)tapinfo->user_data); + if (!ms_dlg || !ms_dlg->statsTreeWidget()) return; + + //Clear the tree because the list always starts from the beginning + ms_dlg->statsTreeWidget()->clear(); + + // Add missing rows and update stats + int cur_row = 0; + for (GList *cur = g_list_first(tapinfo->strinfo_list); cur; cur = gxx_list_next(cur)) { + mcast_stream_info_t *stream_info = gxx_list_data(mcast_stream_info_t *, cur); + if (!stream_info) continue; + + MulticastStatTreeWidgetItem *ms_ti; + QTreeWidgetItem *ti = ms_dlg->statsTreeWidget()->topLevelItem(cur_row); + if (!ti) { + ms_ti = new MulticastStatTreeWidgetItem(ms_dlg->statsTreeWidget()); + for (int col = 0; col < ms_dlg->statsTreeWidget()->columnCount(); col++) { + if (col == col_src_addr_ || col == col_dst_addr_) continue; + ms_ti->setTextAlignment(col, Qt::AlignRight); + } + } else { + ms_ti = static_cast<MulticastStatTreeWidgetItem *>(ti); + } + + ms_ti->updateStreamInfo(stream_info); + cur_row++; + } +} + +QList<QVariant> MulticastStatisticsDialog::treeItemData(QTreeWidgetItem *ti) const +{ + MulticastStatTreeWidgetItem *ms_ti = dynamic_cast<MulticastStatTreeWidgetItem*>(ti); + if (ms_ti) { + return ms_ti->rowData(); + } + else { + return QList<QVariant>(); + } +} + +const QString MulticastStatisticsDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + + MulticastStatTreeWidgetItem *ms_ti = static_cast<MulticastStatTreeWidgetItem *>(ti); + filter_expr = ms_ti->filterExpression(); + } + return filter_expr; +} + +void MulticastStatisticsDialog::updateWidgets() +{ + QString hint; + bool enable_apply = true; + bool enable_edits = cap_file_.isValid(); + bool ok = false; + int param; + + param = burst_measurement_interval_le_->text().toUInt(&ok); + if (!ok || param < 1 || param > 1000) { + hint += tr("The burst interval must be between 1 and 1000. "); + enable_apply = false; + burst_measurement_interval_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + burst_measurement_interval_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = burst_alarm_threshold_le_->text().toInt(&ok); + if (!ok || param < 1) { + hint += tr("The burst alarm threshold isn't valid. "); + enable_apply = false; + burst_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + burst_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = buffer_alarm_threshold_le_->text().toInt(&ok); + if (!ok || param < 1) { + hint += tr("The buffer alarm threshold isn't valid. "); + enable_apply = false; + buffer_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + buffer_alarm_threshold_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = stream_empty_speed_le_->text().toInt(&ok); + if (!ok || param < 1 || param > 10000000) { + hint += tr("The stream empty speed should be between 1 and 10000000. "); + enable_apply = false; + stream_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + stream_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + param = total_empty_speed_le_->text().toInt(&ok); + if (!ok || param < 1 || param > 10000000) { + hint += tr("The total empty speed should be between 1 and 10000000. "); + enable_apply = false; + total_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Invalid); + } else { + total_empty_speed_le_->setSyntaxState(SyntaxLineEdit::Valid); + } + + foreach (QWidget *line_edit, line_edits_) { + line_edit->setEnabled(enable_edits); + } + + applyFilterButton()->setEnabled(enable_apply); + + if (hint.isEmpty() && tapinfo_->allstreams) { + const QString stats = tr("%1 streams, avg bw: %2bps, max bw: %3bps, max burst: %4 / %5ms, max buffer: %6B") + .arg(statsTreeWidget()->topLevelItemCount()) + .arg(bits_s_to_qstring(tapinfo_->allstreams->average_bw)) + .arg(bits_s_to_qstring(tapinfo_->allstreams->element.maxbw)) + .arg(tapinfo_->allstreams->element.topburstsize) + .arg(mcast_stream_burstint) + .arg(bits_s_to_qstring(tapinfo_->allstreams->element.topbuffusage)); + hint.append(stats); + } + hint.prepend("<small><i>"); + hint.append("</i></small>"); + setHint(hint); + TapParameterDialog::updateWidgets(); +} + +void MulticastStatisticsDialog::updateMulticastParameters() +{ + bool ok = false; + int param; + + param = burst_measurement_interval_le_->text().toUInt(&ok); + if (ok && param > 0 && param <= 1000) { + mcast_stream_burstint = (guint16) param; + } + + param = burst_alarm_threshold_le_->text().toInt(&ok); + if (ok) { + mcast_stream_trigger = param; + } + + param = buffer_alarm_threshold_le_->text().toInt(&ok); + if (ok && param > 0) { + mcast_stream_bufferalarm = param; + } + + param = stream_empty_speed_le_->text().toInt(&ok); + if (ok && param > 0 && param <= 10000000) { + mcast_stream_emptyspeed = param; + } + + param = total_empty_speed_le_->text().toInt(&ok); + if (ok && param > 0 && param <= 10000000) { + mcast_stream_cumulemptyspeed = param; + } +} + +void MulticastStatisticsDialog::fillTree() +{ + QList<QWidget *> disable_widgets = QList<QWidget *>() + << line_edits_ << displayFilterLineEdit() << applyFilterButton(); + + foreach (QWidget *w, disable_widgets) w->setEnabled(false); + + rescan(); + + foreach (QWidget *w, disable_widgets) w->setEnabled(true); + for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) { + statsTreeWidget()->resizeColumnToContents(col); + } + updateWidgets(); +} + +void MulticastStatisticsDialog::rescan() +{ + gboolean was_registered = tapinfo_->is_registered; + if (!tapinfo_->is_registered) + register_tap_listener_mcast_stream(tapinfo_); + + cf_retap_packets(cap_file_.capFile()); + + if (!was_registered) + remove_tap_listener_mcast_stream(tapinfo_); + + tapDraw(tapinfo_); +} + +void MulticastStatisticsDialog::captureFileClosing() +{ + /* Remove the stream tap listener */ + remove_tap_listener_mcast_stream(tapinfo_); + + WiresharkDialog::captureFileClosing(); +} + +// Stat command + args + +static void +multicast_statistics_init(const char *args, void*) { + QStringList args_l = QString(args).split(','); + QByteArray filter; + if (args_l.length() > 2) { + filter = QStringList(args_l.mid(2)).join(",").toUtf8(); + } + mainApp->emitStatCommandSignal("MulticastStatistics", filter.constData(), NULL); +} + +static stat_tap_ui multicast_statistics_ui = { + REGISTER_STAT_GROUP_GENERIC, + NULL, + "multicast,stat", + multicast_statistics_init, + 0, + NULL +}; + +extern "C" { + +void register_tap_listener_qt_multicast_statistics(void); + +void +register_tap_listener_qt_multicast_statistics(void) +{ + register_stat_tap_ui(&multicast_statistics_ui, NULL); +} + +} |