From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- ui/qt/sctp_graph_dialog.cpp | 524 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 ui/qt/sctp_graph_dialog.cpp (limited to 'ui/qt/sctp_graph_dialog.cpp') diff --git a/ui/qt/sctp_graph_dialog.cpp b/ui/qt/sctp_graph_dialog.cpp new file mode 100644 index 00000000..a26f0d11 --- /dev/null +++ b/ui/qt/sctp_graph_dialog.cpp @@ -0,0 +1,524 @@ +/* sctp_graph_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include "sctp_graph_dialog.h" +#include +#include "sctp_assoc_analyse_dialog.h" + +#include +#include +#include +#include "epan/packet.h" + +#include "ui/tap-sctp-analysis.h" + +#include + +#include +#include +#include "ui/qt/widgets/wireshark_file_dialog.h" +#include "main_application.h" + +SCTPGraphDialog::SCTPGraphDialog(QWidget *parent, const sctp_assoc_info_t *assoc, + capture_file *cf, int dir) : + QDialog(parent), + ui(new Ui::SCTPGraphDialog), + cap_file_(cf), + frame_num(0), + direction(dir), + relative(false), + type(1) +{ + Q_ASSERT(assoc); + selected_assoc_id = assoc->assoc_id; + + ui->setupUi(this); + Qt::WindowFlags flags = Qt::Window | Qt::WindowSystemMenuHint + | Qt::WindowMinimizeButtonHint + | Qt::WindowMaximizeButtonHint + | Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); + this->setWindowTitle(QString(tr("SCTP TSNs and SACKs over Time: %1 Port1 %2 Port2 %3")) + .arg(gchar_free_to_qstring(cf_get_display_name(cap_file_))).arg(assoc->port1).arg(assoc->port2)); + if ((direction == 1 && assoc->n_array_tsn1 == 0) || (direction == 2 && assoc->n_array_tsn2 == 0)) { + QMessageBox msgBox; + msgBox.setText(tr("No Data Chunks sent")); + msgBox.exec(); + return; + } else { + drawGraph(assoc); + } +} + +SCTPGraphDialog::~SCTPGraphDialog() +{ + delete ui; +} + +void SCTPGraphDialog::drawNRSACKGraph(const sctp_assoc_info_t* selected_assoc) +{ + tsn_t *sack = Q_NULLPTR; + GList *list = Q_NULLPTR, *tlist = Q_NULLPTR; + guint16 gap_start=0, gap_end=0, i, numberOf_gaps, numberOf_nr_gaps; + guint8 type; + guint32 tsnumber, j = 0, min_tsn, rel = 0; + struct nr_sack_chunk_header *nr_sack_header = Q_NULLPTR; + struct gaps *nr_gap = Q_NULLPTR; + /* This holds the sum of gap acks and nr gap acks */ + guint16 total_gaps = 0; + + if (direction == 1) { + list = g_list_last(selected_assoc->sack1); + min_tsn = selected_assoc->min_tsn1; + } else { + list = g_list_last(selected_assoc->sack2); + min_tsn = selected_assoc->min_tsn2; + } + if (relative) { + rel = min_tsn; + } + while (list) { + sack = gxx_list_data(tsn_t*, list); + tlist = g_list_first(sack->tsns); + while (tlist) { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_NR_SACK_CHUNK_ID) { + nr_sack_header = gxx_list_data(struct nr_sack_chunk_header *, tlist); + numberOf_nr_gaps=g_ntohs(nr_sack_header->nr_of_nr_gaps); + numberOf_gaps=g_ntohs(nr_sack_header->nr_of_gaps); + tsnumber = g_ntohl(nr_sack_header->cum_tsn_ack); + total_gaps = numberOf_gaps + numberOf_nr_gaps; + /* If the number of nr_gaps is greater than 0 */ + if (total_gaps > 0) { + nr_gap = &nr_sack_header->gaps[0]; + for (i = 0; i < total_gaps; i++) { + gap_start = g_ntohs(nr_gap->start); + gap_end = g_ntohs(nr_gap->end); + for (j = gap_start; j <= gap_end; j++) { + if (i >= numberOf_gaps) { + yn.append(j + tsnumber - rel); + xn.append(sack->secs + sack->usecs/1000000.0); + fn.append(sack->frame_number); + } else { + yg.append(j + tsnumber - rel); + xg.append(sack->secs + sack->usecs/1000000.0); + fg.append(sack->frame_number); + } + } + if (i < total_gaps-1) + nr_gap++; + } + + if (tsnumber>=min_tsn) { + ys.append(j + tsnumber - rel); + xs.append(sack->secs + sack->usecs/1000000.0); + fs.append(sack->frame_number); + } + } + } + tlist = gxx_list_next(tlist); + } + list = gxx_list_previous(list); + } +} + +void SCTPGraphDialog::drawSACKGraph(const sctp_assoc_info_t* selected_assoc) +{ + GList *listSACK = Q_NULLPTR, *tlist = Q_NULLPTR; + guint16 gap_start=0, gap_end=0, nr, dup_nr; + struct sack_chunk_header *sack_header = Q_NULLPTR; + struct gaps *gap = Q_NULLPTR; + tsn_t *tsn = Q_NULLPTR; + guint8 type; + guint32 tsnumber=0, rel = 0; + guint32 minTSN; + guint32 *dup_list = Q_NULLPTR; + int i, j; + + if (direction == 1) { + minTSN = selected_assoc->min_tsn1; + listSACK = g_list_last(selected_assoc->sack1); + } else { + minTSN = selected_assoc->min_tsn2; + listSACK = g_list_last(selected_assoc->sack2); + } + if (relative) { + rel = minTSN; + } + while (listSACK) { + tsn = gxx_list_data(tsn_t*, listSACK); + tlist = g_list_first(tsn->tsns); + while (tlist) { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_SACK_CHUNK_ID) { + sack_header = gxx_list_data(struct sack_chunk_header *, tlist); + nr=g_ntohs(sack_header->nr_of_gaps); + tsnumber = g_ntohl(sack_header->cum_tsn_ack); + dup_nr=g_ntohs(sack_header->nr_of_dups); + if (nr>0) { // Gap Reports green + gap = &sack_header->gaps[0]; + for (i=0;istart); + gap_end = g_ntohs(gap->end); + for (j=gap_start; j<=gap_end; j++) { + yg.append(j + tsnumber - rel); + xg.append(tsn->secs + tsn->usecs/1000000.0); + fg.append(tsn->frame_number); + } + if (i < nr-1) + gap++; + } + } + if (tsnumber>=minTSN) { // CumTSNAck red + ys.append(tsnumber - rel); + xs.append(tsn->secs + tsn->usecs/1000000.0); + fs.append(tsn->frame_number); + } + if (dup_nr > 0) { // Duplicates cyan + dup_list = &sack_header->a_rwnd + 2 + nr; + for (i = 0; i < dup_nr; i++) { + tsnumber = g_ntohl(dup_list[i]); + if (tsnumber >= minTSN) { + yd.append(tsnumber - rel); + xd.append(tsn->secs + tsn->usecs/1000000.0); + fd.append(tsn->frame_number); + } + } + } + } + tlist = gxx_list_next(tlist); + } + listSACK = gxx_list_previous(listSACK); + } + + QCPScatterStyle myScatter; + myScatter.setShape(QCPScatterStyle::ssCircle); + myScatter.setSize(3); + + int graphcount = ui->sctpPlot->graphCount(); + // create graph and assign data to it: + + // Add SACK graph + if (xs.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("SACK")); + myScatter.setPen(QPen(Qt::red)); + myScatter.setBrush(Qt::red); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xs, ys); + typeStrings.insert(graphcount, QString(tr("CumTSNAck"))); + graphcount++; + } + + // Add Gap Acks + if (xg.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("GAP")); + myScatter.setPen(QPen(Qt::green)); + myScatter.setBrush(Qt::green); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xg, yg); + typeStrings.insert(graphcount, QString(tr("Gap Ack"))); + graphcount++; + } + + // Add NR Gap Acks + if (xn.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("NR_GAP")); + myScatter.setPen(QPen(Qt::blue)); + myScatter.setBrush(Qt::blue); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xn, yn); + typeStrings.insert(graphcount, QString(tr("NR Gap Ack"))); + graphcount++; + } + + // Add Duplicates + if (xd.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("DUP")); + myScatter.setPen(QPen(Qt::cyan)); + myScatter.setBrush(Qt::cyan); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xd, yd); + typeStrings.insert(graphcount, QString(tr("Duplicate Ack"))); + } +} + +void SCTPGraphDialog::drawTSNGraph(const sctp_assoc_info_t* selected_assoc) +{ + GList *listTSN = Q_NULLPTR,*tlist = Q_NULLPTR; + tsn_t *tsn = Q_NULLPTR; + guint8 type; + guint32 tsnumber=0, rel = 0, minTSN; + + if (direction == 1) { + listTSN = g_list_last(selected_assoc->tsn1); + minTSN = selected_assoc->min_tsn1; + } else { + listTSN = g_list_last(selected_assoc->tsn2); + minTSN = selected_assoc->min_tsn2; + } + + if (relative) { + rel = minTSN; + } + + while (listTSN) { + tsn = gxx_list_data(tsn_t*, listTSN); + tlist = g_list_first(tsn->tsns); + while (tlist) + { + type = gxx_list_data(struct chunk_header *, tlist)->type; + if (type == SCTP_DATA_CHUNK_ID || type == SCTP_I_DATA_CHUNK_ID || type == SCTP_FORWARD_TSN_CHUNK_ID) { + tsnumber = g_ntohl(gxx_list_data(struct data_chunk_header *, tlist)->tsn); + yt.append(tsnumber - rel); + xt.append(tsn->secs + tsn->usecs/1000000.0); + ft.append(tsn->frame_number); + } + tlist = gxx_list_next(tlist); + } + listTSN = gxx_list_previous(listTSN); + } + + QCPScatterStyle myScatter; + myScatter.setShape(QCPScatterStyle::ssCircle); + myScatter.setSize(3); + + int graphcount = ui->sctpPlot->graphCount(); + // create graph and assign data to it: + + // Add TSN graph + if (xt.size() > 0) { + QCPGraph *gr = ui->sctpPlot->addGraph(); + gr->setName(QString("TSN")); + myScatter.setPen(QPen(Qt::black)); + myScatter.setBrush(Qt::black); + ui->sctpPlot->graph(graphcount)->setScatterStyle(myScatter); + ui->sctpPlot->graph(graphcount)->setLineStyle(QCPGraph::lsNone); + ui->sctpPlot->graph(graphcount)->setData(xt, yt); + typeStrings.insert(graphcount, QString(tr("TSN"))); + } +} + +void SCTPGraphDialog::drawGraph(const sctp_assoc_info_t* selected_assoc) +{ + if (!selected_assoc) { + selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + } + + guint32 maxTSN, minTSN; + + if (direction == 1) { + maxTSN = selected_assoc->max_tsn1; + minTSN = selected_assoc->min_tsn1; + } else { + maxTSN = selected_assoc->max_tsn2; + minTSN = selected_assoc->min_tsn2; + } + ui->sctpPlot->clearGraphs(); + xt.clear(); + yt.clear(); + xs.clear(); + ys.clear(); + xg.clear(); + yg.clear(); + xd.clear(); + yd.clear(); + xn.clear(); + yn.clear(); + ft.clear(); + fs.clear(); + fg.clear(); + fd.clear(); + fn.clear(); + typeStrings.clear(); + switch (type) { + case 1: + drawSACKGraph(selected_assoc); + drawNRSACKGraph(selected_assoc); + break; + case 2: + drawTSNGraph(selected_assoc); + break; + case 3: + drawTSNGraph(selected_assoc); + drawSACKGraph(selected_assoc); + drawNRSACKGraph(selected_assoc); + break; + default: + drawTSNGraph(selected_assoc); + drawSACKGraph(selected_assoc); + drawNRSACKGraph(selected_assoc); + break; + } + + // give the axes some labels: + ui->sctpPlot->xAxis->setLabel(tr("time [secs]")); + ui->sctpPlot->yAxis->setLabel(tr("TSNs")); + ui->sctpPlot->setInteractions(QCP::iRangeZoom | QCP::iRangeDrag | QCP::iSelectPlottables); + connect(ui->sctpPlot, &QCustomPlot::plottableClick, this, &SCTPGraphDialog::graphClicked); + // set axes ranges, so we see all data: + QCPRange myXRange(selected_assoc->min_secs, (selected_assoc->max_secs+1)); + if (relative) { + QCPRange myYRange(0, maxTSN - minTSN + 1); + ui->sctpPlot->yAxis->setRange(myYRange); + } else { + QCPRange myYRange(minTSN, maxTSN + 1); + ui->sctpPlot->yAxis->setRange(myYRange); + } + ui->sctpPlot->xAxis->setRange(myXRange); + ui->sctpPlot->replot(); +} + +void SCTPGraphDialog::on_pushButton_clicked() +{ + type = 1; + drawGraph(); +} + +void SCTPGraphDialog::on_pushButton_2_clicked() +{ + type = 2; + drawGraph(); +} + +void SCTPGraphDialog::on_pushButton_3_clicked() +{ + type = 3; + drawGraph(); +} + +void SCTPGraphDialog::on_pushButton_4_clicked() +{ + const sctp_assoc_info_t* selected_assoc = SCTPAssocAnalyseDialog::findAssoc(this, selected_assoc_id); + if (!selected_assoc) return; + + ui->sctpPlot->xAxis->setRange(selected_assoc->min_secs, selected_assoc->max_secs+1); + if (relative) { + if (direction == 1) { + ui->sctpPlot->yAxis->setRange(0, selected_assoc->max_tsn1 - selected_assoc->min_tsn1); + } else { + ui->sctpPlot->yAxis->setRange(0, selected_assoc->max_tsn2 - selected_assoc->min_tsn2); + } + } else { + if (direction == 1) { + ui->sctpPlot->yAxis->setRange(selected_assoc->min_tsn1, selected_assoc->max_tsn1); + } else { + ui->sctpPlot->yAxis->setRange(selected_assoc->min_tsn2, selected_assoc->max_tsn2); + } + } + ui->sctpPlot->replot(); +} + +void SCTPGraphDialog::graphClicked(QCPAbstractPlottable* plottable, int, QMouseEvent* event) +{ + frame_num = 0; + int i=0; + double times = ui->sctpPlot->xAxis->pixelToCoord(event->pos().x()); + if (plottable->name().contains("TSN", Qt::CaseInsensitive)) { + for (i = 0; i < xt.size(); i++) { + if (times <= xt.value(i)) { + frame_num = ft.at(i); + break; + } + } + } else if (plottable->name().contains("SACK", Qt::CaseInsensitive)) { + for (i = 0; i < xs.size(); i++) { + if (times <= xs.value(i)) { + frame_num = fs.at(i); + break; + } + } + } else if (plottable->name().contains("DUP", Qt::CaseInsensitive)) { + for (i = 0; i < xd.size(); i++) { + if (times <= xd.value(i)) { + frame_num = fd.at(i); + break; + } + } + } else if (plottable->name().contains("NR_GAP", Qt::CaseInsensitive)) { + for (i = 0; i < xn.size(); i++) { + if (times <= xn.value(i)) { + frame_num = fn.at(i); + break; + } + } + } else if (plottable->name().contains("GAP", Qt::CaseInsensitive)) { + for (i = 0; i < xs.size(); i++) { + if (times <= xs.value(i)) { + frame_num = fs.at(i); + break; + } + } + } + if (cap_file_ && frame_num > 0) { + cf_goto_frame(cap_file_, frame_num); + } + ui->hintLabel->setText(QString(tr("%1: %2 Time: %3 secs ")) + .arg(plottable->name()) + .arg(floor(ui->sctpPlot->yAxis->pixelToCoord(event->pos().y()) + 0.5)) + .arg(ui->sctpPlot->xAxis->pixelToCoord(event->pos().x()))); +} + +void SCTPGraphDialog::save_graph(QDialog *dlg, QCustomPlot *plot) +{ + QString file_name, extension; + QDir path(mainApp->openDialogInitialDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + QString png_filter = tr("Portable Network Graphics (*.png)"); + QString bmp_filter = tr("Windows Bitmap (*.bmp)"); + // Gaze upon my beautiful graph with lossy artifacts! + QString jpeg_filter = tr("JPEG File Interchange Format (*.jpeg *.jpg)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + file_name = WiresharkFileDialog::getSaveFileName(dlg, mainApp->windowTitleString(tr("Save Graph As…")), + path.canonicalPath(), filter, &extension); + + if (file_name.length() > 0) { + bool save_ok = false; + if (extension.compare(pdf_filter) == 0) { + save_ok = plot->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = plot->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = plot->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = plot->saveJpg(file_name); + } + // else error dialog? + if (save_ok) { + mainApp->setLastOpenDirFromFilename(file_name); + } + } +} + + +void SCTPGraphDialog::on_saveButton_clicked() +{ + save_graph(this, ui->sctpPlot); +} + +void SCTPGraphDialog::on_relativeTsn_stateChanged(int arg1) +{ + relative = arg1; + drawGraph(); +} -- cgit v1.2.3