aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--image/openhand-16.pngbin0 -> 160 bytes
-rw-r--r--image/rubberband-16.pngbin0 -> 218 bytes
-rw-r--r--image/toolbar.qrc4
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui9
-rw-r--r--ui/qt/main_window_slots.cpp5
-rw-r--r--ui/qt/tcp_stream_dialog.cpp387
-rw-r--r--ui/qt/tcp_stream_dialog.h24
-rw-r--r--ui/qt/tcp_stream_dialog.ui53
-rw-r--r--ui/tap-tcp-stream.c11
-rw-r--r--ui/tap-tcp-stream.h3
11 files changed, 417 insertions, 80 deletions
diff --git a/image/openhand-16.png b/image/openhand-16.png
new file mode 100644
index 0000000000..9181c859ed
--- /dev/null
+++ b/image/openhand-16.png
Binary files differ
diff --git a/image/rubberband-16.png b/image/rubberband-16.png
new file mode 100644
index 0000000000..229db38c7f
--- /dev/null
+++ b/image/rubberband-16.png
Binary files differ
diff --git a/image/toolbar.qrc b/image/toolbar.qrc
index dc06cf7dfb..3bead0b63a 100644
--- a/image/toolbar.qrc
+++ b/image/toolbar.qrc
@@ -15,4 +15,8 @@
<file>plus-8.png</file>
<file>copy-8.png</file>
</qresource>
+ <qresource prefix="/graph">
+ <file>openhand-16.png</file>
+ <file>rubberband-16.png</file>
+ </qresource>
</RCC>
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index d8c3938005..bba37468ff 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -303,6 +303,7 @@ private slots:
void on_actionStatisticsTcpStreamStevens_triggered();
void on_actionStatisticsTcpStreamThroughput_triggered();
void on_actionStatisticsTcpStreamRoundTripTime_triggered();
+ void on_actionStatisticsTcpStreamWindowScaling_triggered();
};
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 366203bca9..3998565593 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -322,6 +322,7 @@
<addaction name="actionStatisticsTcpStreamStevens"/>
<addaction name="actionStatisticsTcpStreamThroughput"/>
<addaction name="actionStatisticsTcpStreamRoundTripTime"/>
+ <addaction name="actionStatisticsTcpStreamWindowScaling"/>
</widget>
<addaction name="actionSummary"/>
<addaction name="actionProtocol_Hierarchy"/>
@@ -1274,6 +1275,14 @@
<string>TCP round trip time</string>
</property>
</action>
+ <action name="actionStatisticsTcpStreamWindowScaling">
+ <property name="text">
+ <string>Window Scaling</string>
+ </property>
+ <property name="toolTip">
+ <string>TCP window scaling</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 9ed79afb63..3dda0dca78 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -1717,6 +1717,11 @@ void MainWindow::on_actionStatisticsTcpStreamRoundTripTime_triggered()
openTcpStreamDialog(GRAPH_RTT);
}
+void MainWindow::on_actionStatisticsTcpStreamWindowScaling_triggered()
+{
+ openTcpStreamDialog(GRAPH_WSCALE);
+}
+
// Help Menu
void MainWindow::on_actionHelpContents_triggered() {
diff --git a/ui/qt/tcp_stream_dialog.cpp b/ui/qt/tcp_stream_dialog.cpp
index dbee330d05..8a6000ea7f 100644
--- a/ui/qt/tcp_stream_dialog.cpp
+++ b/ui/qt/tcp_stream_dialog.cpp
@@ -31,8 +31,10 @@
#include "wireshark_application.h"
#include "tango_colors.h"
+#include <QCursor>
#include <QDir>
#include <QFileDialog>
+#include <QIcon>
#include <QPushButton>
#include <QDebug>
@@ -47,11 +49,16 @@ const int moving_avg_period_ = 20;
const QRgb graph_color_1 = tango_sky_blue_5;
const QRgb graph_color_2 = tango_butter_6;
+// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph
+// in zoom mode.
+const int min_zoom_pixels_ = 20;
+
const QString average_throughput_label_ = QObject::tr("Avgerage Througput (bits/s)");
const QString round_trip_time_ms_label_ = QObject::tr("Round Trip Time (ms)");
const QString segment_length_label_ = QObject::tr("Segment Length (B)");
-const QString sequence_number_label_ = QObject::tr("Relative Sequence Number (B)");
+const QString sequence_number_label_ = QObject::tr("Sequence Number (B)");
const QString time_s_label_ = QObject::tr("Time (s)");
+const QString window_size_label_ = QObject::tr("Window Size (B)");
Q_DECLARE_METATYPE(tcp_graph_type)
@@ -59,7 +66,11 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
QDialog(parent),
ui(new Ui::TCPStreamDialog),
cap_file_(cf),
+ ts_origin_conn_(true),
+ seq_origin_zero_(true),
tracer_(NULL),
+ mouse_drags_(true),
+ rubber_band_(NULL),
num_dsegs_(-1),
num_acks_(-1),
num_sack_ranges_(-1)
@@ -68,7 +79,8 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
ui->setupUi(this);
- if (!select_tcpip_session(cap_file_, &current)) {
+ struct tcpheader *header = select_tcpip_session(cap_file_, &current);
+ if (!header) {
done(QDialog::Rejected);
}
@@ -80,15 +92,20 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
ui->graphTypeComboBox->addItem(tr("Time / Sequence (Stevens)"), qVariantFromValue(GRAPH_TSEQ_STEVENS));
ui->graphTypeComboBox->addItem(tr("Throughput"), qVariantFromValue(GRAPH_THROUGHPUT));
ui->graphTypeComboBox->addItem(tr("Round Trip Time"), qVariantFromValue(GRAPH_RTT));
+ ui->graphTypeComboBox->addItem(tr("Window Scaling"), qVariantFromValue(GRAPH_WSCALE));
ui->graphTypeComboBox->setCurrentIndex(-1);
ui->graphTypeComboBox->setUpdatesEnabled(true);
+ ui->mouseHorizontalLayout->setContentsMargins(0, 0, 0, 0);
+ ui->dragToolButton->setChecked(mouse_drags_);
+
memset (&graph_, 0, sizeof(graph_));
graph_.type = graph_type;
COPY_ADDRESS(&graph_.src_address, &current.ip_src);
graph_.src_port = current.th_sport;
COPY_ADDRESS(&graph_.dst_address, &current.ip_dst);
graph_.dst_port = current.th_dport;
+ graph_.stream = header->th_stream;
QCustomPlot *sp = ui->streamPlot;
QCPPlotTitle *file_title = new QCPPlotTitle(sp, cf_get_display_name(cap_file_));
@@ -106,10 +123,6 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
// Fills the graph
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(qVariantFromValue(graph_type)));
- sp->setInteractions(
- QCP::iRangeDrag |
- QCP::iRangeZoom
- );
sp->setMouseTracking(true);
sp->graph(0)->setPen(QPen(QBrush(graph_color_1), 0.25));
sp->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5));
@@ -128,6 +141,9 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*)));
connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
+ connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
+ connect(sp, SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)),
+ this, SLOT(axisClicked(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)));
connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(transformYRange(QCPRange)));
disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
@@ -192,17 +208,38 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
toggleTracerStyle();
break;
- // Reset
case Qt::Key_0:
case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
case Qt::Key_R:
case Qt::Key_Home:
resetAxes();
break;
- case Qt::Key_S:
+
+ case Qt::Key_D:
on_otherDirectionButton_clicked();
break;
- // Alas, there is no Blade Runner-style Qt::Key_Ehance
+ case Qt::Key_G:
+ if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
+ emit goToPacket(packet_num_);
+ }
+ break;
+ case Qt::Key_S:
+ seq_origin_zero_ = seq_origin_zero_ ? false : true;
+ fillGraph();
+ break;
+ case Qt::Key_T:
+ ts_origin_conn_ = ts_origin_conn_ ? false : true;
+ fillGraph();
+ break;
+ case Qt::Key_Z:
+ if (mouse_drags_) {
+ ui->selectToolButton->toggle();
+ } else {
+ ui->dragToolButton->toggle();
+ }
+ break;
+
+ // Alas, there is no Blade Runner-style Qt::Key_Enhance
}
if (scale_range) {
@@ -223,6 +260,51 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
sp->replot();
}
QDialog::keyPressEvent(event);
+
+ // GTK+ Shortcuts:
+ // Left Mouse Button selects segment under cursor in Wiresharks packet list
+ // can also drag to zoom in on a rectangular region
+ // Middle Mouse Button zooms in (towards area under cursor)
+ // <Shift>-Middle Mouse Button zooms out
+
+ // Right Mouse Button moves the graph (if zoomed in)
+ // <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified
+
+ // 1 display Round Trip Time Graph
+ // 2 display Throughput Graph
+ // 3 display Time/Sequence Graph (Stevens)
+ // 4 display Time/Sequence Graph (tcptrace)
+ // 5 display Window Scaling Graph
+
+ // <Space bar> toggles crosshairs on/off
+
+ // i or + zoom in (towards area under mouse pointer)
+ // o or - zoom out
+ // r or <Home> restore graph to initial state (zoom out max)
+ // s toggles relative/absolute sequence numbers
+ // t toggles time origin
+ // g go to frame under cursor in Wiresharks packet list (if possible)
+
+ // <Left> move view left by 100 pixels (if zoomed in)
+ // <Right> move view right 100 pixels (if zoomed in)
+ // <Up> move view up by 100 pixels (if zoomed in)
+ // <Down> move view down by 100 pixels (if zoomed in)
+
+ // <Shift><Left> move view left by 10 pixels (if zoomed in)
+ // <Shift><Right> move view right 10 pixels (if zoomed in)
+ // <Shift><Up> move view up by 10 pixels (if zoomed in)
+ // <Shift><Down> move view down by 10 pixels (if zoomed in)
+
+ // <Ctrl><Left> move view left by 1 pixel (if zoomed in)
+ // <Ctrl><Right> move view right 1 pixel (if zoomed in)
+ // <Ctrl><Up> move view up by 1 pixel (if zoomed in)
+ // <Ctrl><Down> move view down by 1 pixel (if zoomed in)
+
+}
+
+void TCPStreamDialog::mouseReleaseEvent(QMouseEvent *event)
+{
+ mouseReleased(event);
}
void TCPStreamDialog::fillGraph()
@@ -231,7 +313,7 @@ void TCPStreamDialog::fillGraph()
if (sp->graphCount() < 1) return;
- rel_time_map_.clear();
+ time_stamp_map_.clear();
sequence_num_map_.clear();
graph_segment_list_free(&graph_);
tracer_->setGraph(NULL);
@@ -244,6 +326,8 @@ void TCPStreamDialog::fillGraph()
sp->xAxis->setLabel(time_s_label_);
sp->xAxis->setNumberFormat("gb");
sp->xAxis->setNumberPrecision(6);
+ sp->yAxis->setNumberFormat("f");
+ sp->yAxis->setNumberPrecision(0);
sp->yAxis2->setVisible(false);
sp->yAxis2->setLabel(QString());
@@ -261,24 +345,35 @@ void TCPStreamDialog::fillGraph()
// graphs. If the throughput list used the same list we could call this
// above in our ctor.
graph_segment_list_get(cap_file_, &graph_, TRUE);
+ ts_offset_ = 0;
+ seq_offset_ = 0;
+ bool first = true;
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
if (!compareHeaders(seg)) {
continue;
}
- double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
- rel_time_map_.insertMulti(rt_val, seg);
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ if (first) {
+ if (ts_origin_conn_) ts_offset_ = ts;
+ if (seq_origin_zero_) seq_offset_ = seg->th_seq;
+ first = false;
+ }
+ time_stamp_map_.insertMulti(ts - ts_offset_, seg);
}
switch (graph_.type) {
case GRAPH_TSEQ_STEVENS:
- initializeStevens();
+ fillStevens();
break;
case GRAPH_THROUGHPUT:
- initializeThroughput();
+ fillThroughput();
break;
case GRAPH_RTT:
- initializeRoundTripTime();
+ fillRoundTripTime();
+ break;
+ case GRAPH_WSCALE:
+ fillWindowScale();
break;
default:
break;
@@ -319,13 +414,15 @@ void TCPStreamDialog::resetAxes()
sp->replot();
}
-void TCPStreamDialog::initializeStevens()
+void TCPStreamDialog::fillStevens()
{
QString dlg_title = QString(tr("Sequence Numbers")) + streamDescription();
setWindowTitle(dlg_title);
title_->setText(dlg_title);
QCustomPlot *sp = ui->streamPlot;
+ sp->yAxis->setLabel(sequence_number_label_);
+
// True Stevens-style graphs don't have lines but I like them - gcc
sp->graph(0)->setLineStyle(QCPGraph::lsStepLeft);
@@ -335,15 +432,14 @@ void TCPStreamDialog::initializeStevens()
continue;
}
- double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
- rel_time.append(rt_val);
- seq.append(seg->th_seq);
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ rel_time.append(ts - ts_offset_);
+ seq.append(seg->th_seq - seq_offset_);
}
sp->graph(0)->setData(rel_time, seq);
- sp->yAxis->setLabel(sequence_number_label_);
}
-void TCPStreamDialog::initializeThroughput()
+void TCPStreamDialog::fillThroughput()
{
QString dlg_title = QString(tr("Throughput")) + streamDescription();
#ifdef MA_1_SECOND
@@ -355,6 +451,12 @@ void TCPStreamDialog::initializeThroughput()
title_->setText(dlg_title);
QCustomPlot *sp = ui->streamPlot;
+ sp->yAxis->setLabel(segment_length_label_);
+ sp->yAxis2->setLabel(average_throughput_label_);
+ sp->yAxis2->setLabelColor(QColor(graph_color_2));
+ sp->yAxis2->setTickLabelColor(QColor(graph_color_2));
+ sp->yAxis2->setVisible(true);
+
sp->graph(0)->setLineStyle(QCPGraph::lsNone);
sp->graph(1)->setVisible(true);
sp->graph(1)->setPen(QPen(QBrush(graph_color_2), 0.5));
@@ -376,10 +478,10 @@ void TCPStreamDialog::initializeThroughput()
// For now use not-really-correct initial values just to keep our vector
// lengths the same.
for (struct segment *seg = graph_.segments->next; seg != NULL; seg = seg->next) {
- double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
#ifdef MA_1_SECOND
- while (rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) {
+ while (ts - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) {
oldest_seg = oldest_seg->next;
sum -= oldest_seg->th_seglen;
}
@@ -391,7 +493,7 @@ void TCPStreamDialog::initializeThroughput()
i++;
#endif
- double dtime = rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0);
+ double dtime = ts - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0);
double av_tput;
sum += seg->th_seglen;
if (dtime > 0.0) {
@@ -400,37 +502,35 @@ void TCPStreamDialog::initializeThroughput()
av_tput = 0.0;
}
- rel_time.append(rt_val);
+ rel_time.append(ts - ts_offset_);
seg_len.append(seg->th_seglen);
// Add a data point only if our time window has advanced. Otherwise
// update the most recent point. (We might want to show a warning
// for out-of-order packets.)
- if (tput_time.size() > 0 && rt_val <= tput_time.last()) {
+ if (tput_time.size() > 0 && ts <= tput_time.last()) {
tput[tput.size() - 1] = av_tput;
} else {
tput.append(av_tput);
- tput_time.append(rt_val);
+ tput_time.append(ts);
}
}
sp->graph(0)->setData(rel_time, seg_len);
sp->graph(1)->setData(tput_time, tput);
-
- sp->yAxis->setLabel(segment_length_label_);
-
- sp->yAxis2->setLabel(average_throughput_label_);
- sp->yAxis2->setLabelColor(QColor(graph_color_2));
- sp->yAxis2->setTickLabelColor(QColor(graph_color_2));
- sp->yAxis2->setVisible(true);
}
-void TCPStreamDialog::initializeRoundTripTime()
+void TCPStreamDialog::fillRoundTripTime()
{
QString dlg_title = QString(tr("Round Trip Time")) + streamDescription();
setWindowTitle(dlg_title);
title_->setText(dlg_title);
QCustomPlot *sp = ui->streamPlot;
+ sp->xAxis->setLabel(sequence_number_label_);
+ sp->xAxis->setNumberFormat("f");
+ sp->xAxis->setNumberPrecision(0);
+ sp->yAxis->setLabel(round_trip_time_ms_label_);
+
sp->graph(0)->setLineStyle(QCPGraph::lsLine);
QVector<double> seq_no, rtt;
@@ -452,7 +552,7 @@ void TCPStreamDialog::initializeRoundTripTime()
for (u = unack; u; u = v) {
if (ack_no > u->seqno) {
- seq_no.append(u->seqno);
+ seq_no.append(u->seqno - seq_offset_);
rtt.append((rt_val - u->time) * 1000.0);
sequence_num_map_.insert(u->seqno, seg);
rtt_delete_unack_from_list(&unack, u);
@@ -462,10 +562,33 @@ void TCPStreamDialog::initializeRoundTripTime()
}
}
sp->graph(0)->setData(seq_no, rtt);
- sp->xAxis->setLabel(sequence_number_label_);
- sp->xAxis->setNumberFormat("f");
- sp->xAxis->setNumberPrecision(0);
- sp->yAxis->setLabel(round_trip_time_ms_label_);
+}
+
+void TCPStreamDialog::fillWindowScale()
+{
+ QString dlg_title = QString(tr("Window Scaling")) + streamDescription();
+ setWindowTitle(dlg_title);
+ title_->setText(dlg_title);
+
+ QCustomPlot *sp = ui->streamPlot;
+ sp->graph(0)->setLineStyle(QCPGraph::lsLine);
+
+ QVector<double> rel_time, win_size;
+ for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
+ if (!compareHeaders(seg)) {
+ continue;
+ }
+
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ guint16 flags = seg->th_flags;
+
+ if ( (flags & (TH_SYN|TH_RST)) == 0 ) {
+ rel_time.append(ts - ts_offset_);
+ win_size.append(seg->th_win);
+ }
+ }
+ sp->graph(0)->setData(rel_time, win_size);
+ sp->yAxis->setLabel(window_size_label_);
}
QString TCPStreamDialog::streamDescription()
@@ -511,58 +634,163 @@ void TCPStreamDialog::toggleTracerStyle(bool force_default)
ui->streamPlot->replot();
}
+QRectF TCPStreamDialog::getZoomRanges(QRect zoom_rect)
+{
+ QRectF zoom_ranges = QRectF();
+
+ if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) {
+ return zoom_ranges;
+ }
+
+ QCustomPlot *sp = ui->streamPlot;
+ QRect zr = zoom_rect.normalized();
+ QRect ar = sp->axisRect()->rect();
+ if (ar.intersects(zr)) {
+ QRect zsr = ar.intersected(zr);
+ zoom_ranges.setX(sp->xAxis->range().lower
+ + sp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width());
+ zoom_ranges.setWidth(sp->xAxis->range().size() * zsr.width() / ar.width());
+
+ // QRects grow down
+ zoom_ranges.setY(sp->yAxis->range().lower
+ + sp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height());
+ zoom_ranges.setHeight(sp->yAxis->range().size() * zsr.height() / ar.height());
+ }
+ return zoom_ranges;
+}
+
void TCPStreamDialog::graphClicked(QMouseEvent *event)
{
Q_UNUSED(event)
-// QRect spr = ui->streamPlot->axisRect()->rect();
- if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
- emit goToPacket(packet_num_);
+ if (mouse_drags_) {
+ if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
+ emit goToPacket(packet_num_);
+ }
+ } else {
+ if (!rubber_band_) {
+ rubber_band_ = new QRubberBand(QRubberBand::Rectangle, ui->streamPlot);
+ }
+ rb_origin_ = event->pos();
+ rubber_band_->setGeometry(QRect(rb_origin_, QSize()));
+ rubber_band_->show();
}
}
-// Setting mouseTracking on our streamPlot may not be as reliable
-// as we need. If it's not we might want to poll the mouse position
-// using a QTimer instead.
-void TCPStreamDialog::mouseMoved(QMouseEvent *event)
+void TCPStreamDialog::axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
{
- double tr_key = tracer_->position->key();
- struct segment *packet_seg = NULL;
- packet_num_ = 0;
+ Q_UNUSED(part)
+ Q_UNUSED(event)
+ QCustomPlot *sp = ui->streamPlot;
- if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) {
+ if (axis == sp->xAxis) {
switch (graph_.type) {
- case GRAPH_TSEQ_STEVENS:
case GRAPH_THROUGHPUT:
- packet_seg = rel_time_map_.value(tr_key, NULL);
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_TSEQ_TCPTRACE:
+ case GRAPH_WSCALE:
+ ts_origin_conn_ = ts_origin_conn_ ? false : true;
+ fillGraph();
break;
case GRAPH_RTT:
- packet_seg = sequence_num_map_.value(tr_key, NULL);
+ seq_origin_zero_ = seq_origin_zero_ ? false : true;
+ fillGraph();
+ break;
+ default:
+ break;
+ }
+ } else if (axis == sp->yAxis) {
+ switch (graph_.type) {
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_TSEQ_TCPTRACE:
+ seq_origin_zero_ = seq_origin_zero_ ? false : true;
+ fillGraph();
+ break;
default:
break;
}
}
+}
- if (!packet_seg) {
- tracer_->setVisible(false);
- ui->hintLabel->setText(tr("<small><i>Hover over the graph for details.</i></small>"));
+// Setting mouseTracking on our streamPlot may not be as reliable
+// as we need. If it's not we might want to poll the mouse position
+// using a QTimer instead.
+void TCPStreamDialog::mouseMoved(QMouseEvent *event)
+{
+ if (mouse_drags_) {
+ double tr_key = tracer_->position->key();
+ struct segment *packet_seg = NULL;
+ packet_num_ = 0;
+
+ if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) {
+ switch (graph_.type) {
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_THROUGHPUT:
+ case GRAPH_WSCALE:
+ packet_seg = time_stamp_map_.value(tr_key, NULL);
+ break;
+ case GRAPH_RTT:
+ packet_seg = sequence_num_map_.value(tr_key, NULL);
+ default:
+ break;
+ }
+ }
+
+ if (!packet_seg) {
+ tracer_->setVisible(false);
+ ui->hintLabel->setText(tr("<small><i>Hover over the graph for details.</i></small>"));
+ ui->streamPlot->replot();
+ return;
+ }
+
+ tracer_->setVisible(true);
+ packet_num_ = packet_seg->num;
+ QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>"))
+ .arg(cap_file_ ? tr("Click to select packet") : tr("Packet"))
+ .arg(packet_num_)
+ .arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4))
+ .arg(packet_seg->th_seglen)
+ .arg(packet_seg->th_seq)
+ .arg(packet_seg->th_ack)
+ .arg(packet_seg->th_win);
+ ui->hintLabel->setText(hint);
+ tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
ui->streamPlot->replot();
- return;
+ } else {
+ QString hint = QString(tr("<small>Click to select a portion of the graph</small>"));
+ if (rubber_band_) {
+ rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized());
+ QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos()));
+ if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) {
+ hint = QString(tr("<small>Release to zoom, x = %1 to %2, y = %3 to %4</small>"))
+ .arg(zoom_ranges.x())
+ .arg(zoom_ranges.x() + zoom_ranges.width())
+ .arg(zoom_ranges.y())
+ .arg(zoom_ranges.y() + zoom_ranges.height());
+ } else {
+ hint = QString(tr("<small>Unable to select range</small>"));
+ }
+ }
+ ui->hintLabel->setText(hint);
}
+}
- tracer_->setVisible(true);
- packet_num_ = packet_seg->num;
- QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>"))
- .arg(cap_file_ ? tr("Click to select packet") : tr("Packet"))
- .arg(packet_num_)
- .arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4))
- .arg(packet_seg->th_seglen)
- .arg(packet_seg->th_seq)
- .arg(packet_seg->th_ack)
- .arg(packet_seg->th_win);
- ui->hintLabel->setText(hint);
- tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
- ui->streamPlot->replot();
+void TCPStreamDialog::mouseReleased(QMouseEvent *event)
+{
+ if (rubber_band_) {
+ rubber_band_->hide();
+ if (!mouse_drags_) {
+ QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos()));
+ if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) {
+ QCustomPlot *sp = ui->streamPlot;
+ sp->xAxis->setRangeLower(zoom_ranges.x());
+ sp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width());
+ sp->yAxis->setRangeLower(zoom_ranges.y());
+ sp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height());
+ sp->replot();
+ }
+ }
+ }
}
void TCPStreamDialog::transformYRange(const QCPRange &y_range1)
@@ -648,6 +876,23 @@ void TCPStreamDialog::on_otherDirectionButton_clicked()
fillGraph();
}
+void TCPStreamDialog::on_dragToolButton_toggled(bool checked)
+{
+ if (checked) mouse_drags_ = true;
+ ui->streamPlot->setInteractions(
+ QCP::iRangeDrag |
+ QCP::iRangeZoom
+ );
+ ui->streamPlot->setCursor(QCursor(Qt::OpenHandCursor));
+}
+
+void TCPStreamDialog::on_selectToolButton_toggled(bool checked)
+{
+ if (checked) mouse_drags_ = false;
+ ui->streamPlot->setInteractions(0);
+ ui->streamPlot->setCursor(QCursor(Qt::CrossCursor));
+}
+
/*
* Editor modelines
*
diff --git a/ui/qt/tcp_stream_dialog.h b/ui/qt/tcp_stream_dialog.h
index 087aba9d42..481bb2bb20 100644
--- a/ui/qt/tcp_stream_dialog.h
+++ b/ui/qt/tcp_stream_dialog.h
@@ -36,6 +36,7 @@
#include "qcustomplot.h"
#include <QDialog>
+#include <QRubberBand>
namespace Ui {
class TCPStreamDialog;
@@ -58,17 +59,26 @@ public slots:
protected:
void showEvent(QShowEvent *event);
void keyPressEvent(QKeyEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
private:
Ui::TCPStreamDialog *ui;
capture_file *cap_file_;
- QMap<double, struct segment *> rel_time_map_;
+ QMap<double, struct segment *> time_stamp_map_;
+ double ts_offset_;
+ bool ts_origin_conn_;
QMap<double, struct segment *> sequence_num_map_;
+ double seq_offset_;
+ bool seq_origin_zero_;
struct tcp_graph graph_;
QCPPlotTitle *title_;
QCPItemTracer *tracer_;
+ QRectF axis_bounds_;
guint32 packet_num_;
QTransform y_axis_xfrm_;
+ bool mouse_drags_;
+ QRubberBand *rubber_band_;
+ QPoint rb_origin_;
int num_dsegs_;
int num_acks_;
@@ -76,21 +86,27 @@ private:
void fillGraph();
void resetAxes();
- void initializeStevens();
- void initializeThroughput();
- void initializeRoundTripTime();
+ void fillStevens();
+ void fillThroughput();
+ void fillRoundTripTime();
+ void fillWindowScale();
QString streamDescription();
bool compareHeaders(struct segment *seg);
void toggleTracerStyle(bool force_default = false);
+ QRectF getZoomRanges(QRect zoom_rect);
private slots:
void graphClicked(QMouseEvent *event);
+ void axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
void mouseMoved(QMouseEvent *event);
+ void mouseReleased(QMouseEvent *event);
void transformYRange(const QCPRange &y_range1);
void on_buttonBox_accepted();
void on_graphTypeComboBox_currentIndexChanged(int index);
void on_resetButton_clicked();
void on_otherDirectionButton_clicked();
+ void on_dragToolButton_toggled(bool checked);
+ void on_selectToolButton_toggled(bool checked);
};
#endif // TCP_STREAM_DIALOG_H
diff --git a/ui/qt/tcp_stream_dialog.ui b/ui/qt/tcp_stream_dialog.ui
index d19d5ed04d..b9e139129e 100644
--- a/ui/qt/tcp_stream_dialog.ui
+++ b/ui/qt/tcp_stream_dialog.ui
@@ -45,7 +45,11 @@
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10%&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10%&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
-&lt;tr&gt;&lt;th&gt;s&lt;/th&gt;&lt;td&gt;Switch direction (swap TCP endpoints)&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;d&lt;/th&gt;&lt;td&gt;Switch direction (swap TCP endpoints)&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;s&lt;/th&gt;&lt;td&gt;Toggle relative / absolute sequence numbers&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</string>
@@ -56,7 +60,7 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
@@ -81,6 +85,44 @@
</spacer>
</item>
<item>
+ <layout class="QHBoxLayout" name="mouseHorizontalLayout">
+ <item>
+ <widget class="QToolButton" name="dragToolButton">
+ <property name="toolTip">
+ <string>Drag using the mouse button.</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../image/toolbar.qrc">
+ <normaloff>:/graph/openhand-16.png</normaloff>:/graph/openhand-16.png</iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">mouseButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="selectToolButton">
+ <property name="toolTip">
+ <string>Select using the mouse button.</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../image/toolbar.qrc">
+ <normaloff>:/graph/rubberband-16.png</normaloff>:/graph/rubberband-16.png</iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">mouseButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
<widget class="QPushButton" name="resetButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reset the graph to its initial state.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -127,7 +169,9 @@
<header>elided_label.h</header>
</customwidget>
</customwidgets>
- <resources/>
+ <resources>
+ <include location="../../image/toolbar.qrc"/>
+ </resources>
<connections>
<connection>
<sender>buttonBox</sender>
@@ -162,4 +206,7 @@
</hints>
</connection>
</connections>
+ <buttongroups>
+ <buttongroup name="mouseButtonGroup"/>
+ </buttongroups>
</ui>
diff --git a/ui/tap-tcp-stream.c b/ui/tap-tcp-stream.c
index 6bbf3c1d3a..621c882b09 100644
--- a/ui/tap-tcp-stream.c
+++ b/ui/tap-tcp-stream.c
@@ -61,15 +61,18 @@ tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, cons
tg->src_port, tg->dst_port,
&tcphdr->ip_src, &tcphdr->ip_dst,
tcphdr->th_sport, tcphdr->th_dport,
- ts->direction))
+ ts->direction)
+ && tg->stream == tcphdr->th_stream)
{
struct segment *segment = (struct segment *)g_malloc(sizeof(struct segment));
segment->next = NULL;
segment->num = pinfo->fd->num;
segment->rel_secs = (guint32)pinfo->rel_ts.secs;
segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
+ /* Currently unused
segment->abs_secs = (guint32)pinfo->fd->abs_ts.secs;
segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
+ */
segment->th_seq = tcphdr->th_seq;
segment->th_ack = tcphdr->th_ack;
segment->th_win = tcphdr->th_win;
@@ -111,7 +114,8 @@ graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_k
if (!cf || !tg) return;
if (!stream_known) {
- if (!select_tcpip_session(cf, &current)) return;
+ struct tcpheader *header = select_tcpip_session(cf, &current);
+ if (!header) return;
if (tg->type == GRAPH_THROUGHPUT)
ts.direction = COMPARE_CURR_DIR;
else
@@ -122,6 +126,7 @@ graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_k
tg->src_port = current.th_sport;
COPY_ADDRESS(&tg->dst_address, &current.ip_dst);
tg->dst_port = current.th_dport;
+ tg->stream = header->th_stream;
}
/* rescan all the packets and pick up all interesting tcp headers.
@@ -328,8 +333,10 @@ select_tcpip_session(capture_file *cf, struct segment *hdrs)
hdrs->num = fdata->num;
hdrs->rel_secs = (guint32) rel_ts.secs;
hdrs->rel_usecs = rel_ts.nsecs/1000;
+ /* Currently unused
hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
+ */
hdrs->th_seq = th.tcphdrs[0]->th_seq;
hdrs->th_ack = th.tcphdrs[0]->th_ack;
hdrs->th_win = th.tcphdrs[0]->th_win;
diff --git a/ui/tap-tcp-stream.h b/ui/tap-tcp-stream.h
index 56953adc4e..1aba3c842d 100644
--- a/ui/tap-tcp-stream.h
+++ b/ui/tap-tcp-stream.h
@@ -44,8 +44,10 @@ struct segment {
guint32 num;
guint32 rel_secs;
guint32 rel_usecs;
+ /* Currently unused.
guint32 abs_secs;
guint32 abs_usecs;
+ */
guint32 th_seq;
guint32 th_ack;
@@ -70,6 +72,7 @@ struct tcp_graph {
guint16 src_port;
address dst_address;
guint16 dst_port;
+ guint32 stream;
/* Should this be a map or tree instead? */
struct segment *segments;
};