Qt: introduce sampling options for the RTT graph
This commit is contained in:
parent
f215bd6a8c
commit
ec09e1bbc7
@ -853,7 +853,18 @@ Throughput:: Average throughput and goodput.
|
||||
|
||||
Round Trip Time:: Round trip time vs time or sequence number. RTT is
|
||||
based on the acknowledgment timestamp corresponding to a particular
|
||||
segment.
|
||||
segment. The sampling method selects which segments are taken into
|
||||
account and how the RTT is computed:
|
||||
|
||||
* `All Data Packets`, all segments carrying data are computed, and when
|
||||
present, SACK is ignored.
|
||||
* `All Data Packets w/ SACK`, all segments carrying data are computed,
|
||||
the RTT value is based on SACK if present.
|
||||
* `Data Packets matching RTT`, only segments with a corresponding RTT
|
||||
value in the packet list are computed.
|
||||
* `Data Packets matching Karn RTT`, only segments with a corresponding RTT
|
||||
value in the packet list are computed, ambiguous ACKs following Karn's
|
||||
definition are excluded.
|
||||
|
||||
Window Scaling:: Window size and outstanding bytes.
|
||||
|
||||
|
@ -158,6 +158,14 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
|
||||
if (graph_type == GRAPH_WSCALE) graph_idx = gtcb->count() - 1;
|
||||
gtcb->setUpdatesEnabled(true);
|
||||
|
||||
QComboBox *smcb = ui->samplingMethodComboBox;
|
||||
smcb->setUpdatesEnabled(false);
|
||||
smcb->addItem(ui->actionSamplingAllPackets->text(), SAMPLING_ALL);
|
||||
smcb->addItem(ui->actionSamplingAllPacketsSACK->text(), SAMPLING_ALL_SACK);
|
||||
smcb->addItem(ui->actionSamplingRTT->text(), SAMPLING_RTT);
|
||||
smcb->addItem(ui->actionSamplingKarn->text(), SAMPLING_KARN);
|
||||
smcb->setUpdatesEnabled(true);
|
||||
|
||||
ui->dragRadioButton->setChecked(mouse_drags_);
|
||||
|
||||
ctx_menu_.addAction(ui->actionZoomIn);
|
||||
@ -709,8 +717,12 @@ void TCPStreamDialog::showWidgetsForGraphType()
|
||||
{
|
||||
if (graph_.type == GRAPH_RTT) {
|
||||
ui->bySeqNumberCheckBox->setVisible(true);
|
||||
ui->samplingMethodComboBox->setVisible(true);
|
||||
ui->samplingLabel->setVisible(true);
|
||||
} else {
|
||||
ui->bySeqNumberCheckBox->setVisible(false);
|
||||
ui->samplingMethodComboBox->setVisible(false);
|
||||
ui->samplingLabel->setVisible(false);
|
||||
}
|
||||
if (graph_.type == GRAPH_THROUGHPUT) {
|
||||
#ifdef MA_1_SECOND
|
||||
@ -1448,7 +1460,7 @@ rtt_selectively_ack_range(QVector<double>& x_vals, bool bySeqNumber,
|
||||
} else {
|
||||
x_vals.append(cur->time);
|
||||
}
|
||||
rtt.append((rt_val - cur->time) * 1000.0);
|
||||
rtt.append(rt_val - cur->time);
|
||||
// in this case, we will delete current unack
|
||||
// [ update "begin" if necessary - we will return it to the
|
||||
// caller to let them know we deleted it ]
|
||||
@ -1466,7 +1478,7 @@ rtt_selectively_ack_range(QVector<double>& x_vals, bool bySeqNumber,
|
||||
} else {
|
||||
x_vals.append(cur->time);
|
||||
}
|
||||
rtt.append((rt_val - cur->time) * 1000.0);
|
||||
rtt.append(rt_val - cur->time);
|
||||
// in this case, "right" marks the start of remaining bytes
|
||||
cur->seqno = right;
|
||||
continue;
|
||||
@ -1480,7 +1492,7 @@ rtt_selectively_ack_range(QVector<double>& x_vals, bool bySeqNumber,
|
||||
} else {
|
||||
x_vals.append(cur->time);
|
||||
}
|
||||
rtt.append((rt_val - cur->time) * 1000.0);
|
||||
rtt.append(rt_val - cur->time);
|
||||
// in this case, "left" is just beyond the remaining bytes
|
||||
cur->end_seqno = left;
|
||||
continue;
|
||||
@ -1496,7 +1508,7 @@ rtt_selectively_ack_range(QVector<double>& x_vals, bool bySeqNumber,
|
||||
} else {
|
||||
x_vals.append(cur->time);
|
||||
}
|
||||
rtt.append((rt_val - cur->time) * 1000.0);
|
||||
rtt.append(rt_val - cur->time);
|
||||
// then split cur into two unacked segments
|
||||
// (linking the right-hand unack after the left)
|
||||
cur->next = rtt_get_new_unack(cur->time, right, cur->end_seqno - right);
|
||||
@ -1551,6 +1563,7 @@ void TCPStreamDialog::fillRoundTripTime()
|
||||
}
|
||||
}
|
||||
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
||||
/* sender traffic analysis */
|
||||
if (compareHeaders(seg)) {
|
||||
uint32_t seqno = seg->th_seq - seq_base;
|
||||
if (seg->th_seglen && !rtt_is_retrans(unack_list, seqno)) {
|
||||
@ -1564,48 +1577,90 @@ void TCPStreamDialog::fillRoundTripTime()
|
||||
}
|
||||
rtt_put_unack_on_list(&unack_list, u);
|
||||
}
|
||||
} else {
|
||||
/* else: ignore redundant sequences (Keep-Alives, Spurious,..) */
|
||||
}
|
||||
/* receiver traffic analysis */
|
||||
else {
|
||||
uint32_t ack_no = seg->th_ack - seq_base;
|
||||
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
||||
rt_val -= ts_offset_;
|
||||
struct rtt_unack *v;
|
||||
|
||||
for (u = unack_list; u; u = v) {
|
||||
// full or partial ack of seg by ack_no
|
||||
if (tcp_seq_after(ack_no, u->seqno)) {
|
||||
// full or partial ack of seg by ack_no
|
||||
if (bySeqNumber) {
|
||||
x_vals.append(u->seqno);
|
||||
sequence_num_map_.insert(u->seqno, seg);
|
||||
} else {
|
||||
x_vals.append(u->time);
|
||||
}
|
||||
rtt.append(rt_val - u->time);
|
||||
if (tcp_seq_eq_or_after(ack_no, u->end_seqno)) {
|
||||
// fully acked segment - nothing more to see here
|
||||
// fully acked segment, but we're also acking a newer one on the next round
|
||||
if (tcp_seq_after(ack_no, u->end_seqno)) {
|
||||
/* breach RFC, take more RTT samples */
|
||||
if( graph_.rtt_sampling & RTT_ALL) {
|
||||
|
||||
if (bySeqNumber) {
|
||||
x_vals.append(u->seqno);
|
||||
sequence_num_map_.insert(u->seqno, seg);
|
||||
} else {
|
||||
x_vals.append(u->time);
|
||||
}
|
||||
rtt.append(rt_val - u->time);
|
||||
}
|
||||
v = u->next;
|
||||
rtt_delete_unack_from_list(&unack_list, u);
|
||||
// no need to compare SACK blocks for fully ACKed seg
|
||||
continue;
|
||||
} else {
|
||||
// partial ack of GSO seg
|
||||
u->seqno = ack_no;
|
||||
// (keep going - still need to compare SACK blocks...)
|
||||
}
|
||||
|
||||
// fully acked segment, currently the one being acked now
|
||||
else if (tcp_seq_eq(ack_no, u->end_seqno)) {
|
||||
if(!(graph_.rtt_sampling & RTT_KRN && (seg->ack_karn)) ) {
|
||||
if (bySeqNumber) {
|
||||
x_vals.append(u->seqno);
|
||||
sequence_num_map_.insert(u->seqno, seg);
|
||||
} else {
|
||||
x_vals.append(u->time);
|
||||
}
|
||||
rtt.append(rt_val - u->time);
|
||||
}
|
||||
/* else: ignore Karn ambiguous ACKs */
|
||||
|
||||
v = u->next;
|
||||
rtt_delete_unack_from_list(&unack_list, u);
|
||||
// no need to compare SACK blocks for fully ACKed seg
|
||||
continue;
|
||||
}
|
||||
|
||||
// partial ack of seg by ack_no
|
||||
else {
|
||||
if(!(graph_.rtt_sampling & RTT_KRN && (seg->ack_karn)) ) {
|
||||
if (bySeqNumber) {
|
||||
x_vals.append(u->seqno);
|
||||
sequence_num_map_.insert(u->seqno, seg);
|
||||
} else {
|
||||
x_vals.append(u->time);
|
||||
}
|
||||
rtt.append(rt_val - u->time);
|
||||
// partial ack of GSO seg
|
||||
u->seqno = ack_no;
|
||||
// (keep going - still need to compare SACK blocks...)
|
||||
}
|
||||
/* else: ignore Karn ambiguous ACKs */
|
||||
}
|
||||
}
|
||||
|
||||
v = u->next;
|
||||
// selectively acking u more than once
|
||||
// can shatter it into multiple intervals.
|
||||
// If we link those back into the list between u and v,
|
||||
// then each subsequent SACK selectively ACKs that range.
|
||||
for (int i = 0; i < seg->num_sack_ranges; ++i) {
|
||||
uint32_t left = seg->sack_left_edge[i] - seq_base;
|
||||
uint32_t right = seg->sack_right_edge[i] - seq_base;
|
||||
u = rtt_selectively_ack_range(x_vals, bySeqNumber, rtt,
|
||||
&unack_list, u, v,
|
||||
left, right, rt_val);
|
||||
// if range is empty after selective ack, we can
|
||||
// skip the rest of the SACK blocks
|
||||
if (u == v) break;
|
||||
if( graph_.rtt_sampling & RTT_SAK ) {
|
||||
for (int i = 0; i < seg->num_sack_ranges; ++i) {
|
||||
uint32_t left = seg->sack_left_edge[i] - seq_base;
|
||||
uint32_t right = seg->sack_right_edge[i] - seq_base;
|
||||
u = rtt_selectively_ack_range(x_vals, bySeqNumber, rtt,
|
||||
&unack_list, u, v,
|
||||
left, right, rt_val);
|
||||
// if range is empty after selective ack, we can
|
||||
// skip the rest of the SACK blocks
|
||||
if (u == v) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2092,6 +2147,33 @@ void TCPStreamDialog::on_zoomRadioButton_toggled(bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
void TCPStreamDialog::on_samplingMethodComboBox_currentIndexChanged(int index)
|
||||
{
|
||||
if (index < 0) return;
|
||||
|
||||
// reset flags
|
||||
graph_.rtt_sampling = 0;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
default:
|
||||
graph_.rtt_sampling |= RTT_ALL;
|
||||
break;
|
||||
case 1:
|
||||
graph_.rtt_sampling |= RTT_ALL;
|
||||
graph_.rtt_sampling |= RTT_SAK;
|
||||
break;
|
||||
case 2:
|
||||
graph_.rtt_sampling |= RTT_RTT;
|
||||
break;
|
||||
case 3:
|
||||
graph_.rtt_sampling |= RTT_KRN;
|
||||
break;
|
||||
}
|
||||
|
||||
fillGraph(/*reset_axes=*/true, /*set_focus=*/false);
|
||||
}
|
||||
|
||||
void TCPStreamDialog::on_bySeqNumberCheckBox_stateChanged(int /* state */)
|
||||
{
|
||||
fillGraph(/*reset_axes=*/true, /*set_focus=*/false);
|
||||
@ -2348,3 +2430,4 @@ void TCPStreamDialog::on_buttonBox_helpRequested()
|
||||
{
|
||||
mainApp->helpTopicAction(HELP_STATS_TCP_STREAM_GRAPHS_DIALOG);
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,7 @@ private slots:
|
||||
void on_dragRadioButton_toggled(bool checked);
|
||||
void on_zoomRadioButton_toggled(bool checked);
|
||||
void on_bySeqNumberCheckBox_stateChanged(int state);
|
||||
void on_samplingMethodComboBox_currentIndexChanged(int index);
|
||||
void on_showSegLengthCheckBox_stateChanged(int state);
|
||||
void on_showThroughputCheckBox_stateChanged(int state);
|
||||
void on_showGoodputCheckBox_stateChanged(int state);
|
||||
@ -193,3 +194,4 @@ private slots:
|
||||
};
|
||||
|
||||
#endif // TCP_STREAM_DIALOG_H
|
||||
|
||||
|
@ -229,6 +229,26 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="samplingLabel">
|
||||
<property name="text">
|
||||
<string>Sampling Method</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="samplingMethodComboBox">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::TabFocus</enum>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select which packets and how the RTT sampling is done</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="bySeqNumberCheckBox">
|
||||
<property name="focusPolicy">
|
||||
@ -607,6 +627,50 @@
|
||||
<string>4</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSamplingAllPackets">
|
||||
<property name="text">
|
||||
<string>All Data Packets</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sampling from all data packets</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSamplingAllPacketsSACK">
|
||||
<property name="text">
|
||||
<string>All Data Packets w/ SACK</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sampling from all data packets w/ SACK</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSamplingRTT">
|
||||
<property name="text">
|
||||
<string>Data Packets matching RTT</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sampling from RTT packets</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSamplingKarn">
|
||||
<property name="text">
|
||||
<string>Data Packets matching Karn RTT</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sampling from Karn RTT packets</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoomInX">
|
||||
<property name="text">
|
||||
<string>Zoom In X Axis</string>
|
||||
|
@ -114,6 +114,8 @@ tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, cons
|
||||
copy_address(&segment->ip_src, &tcphdr->ip_src);
|
||||
copy_address(&segment->ip_dst, &tcphdr->ip_dst);
|
||||
|
||||
segment->ack_karn=tcphdr->flagkarn;
|
||||
|
||||
segment->num_sack_ranges = MIN(MAX_TCP_SACK_RANGES, tcphdr->num_sack_ranges);
|
||||
if (segment->num_sack_ranges > 0) {
|
||||
/* Copy entries in the order they happen */
|
||||
@ -359,7 +361,7 @@ select_tcpip_session(capture_file *cf)
|
||||
return th_stream;
|
||||
}
|
||||
|
||||
int rtt_is_retrans(struct rtt_unack *list, unsigned int seqno)
|
||||
bool rtt_is_retrans(struct rtt_unack *list, unsigned int seqno)
|
||||
{
|
||||
struct rtt_unack *u;
|
||||
|
||||
|
@ -27,6 +27,19 @@ typedef enum tcp_graph_type_ {
|
||||
GRAPH_UNDEFINED
|
||||
} tcp_graph_type;
|
||||
|
||||
#define RTT_ALL 0x0001
|
||||
#define RTT_SAK 0x0002
|
||||
#define RTT_RTT 0x0004
|
||||
#define RTT_KRN 0x0008
|
||||
|
||||
typedef enum rtt_sampling_method_ {
|
||||
SAMPLING_ALL,
|
||||
SAMPLING_ALL_SACK,
|
||||
SAMPLING_RTT,
|
||||
SAMPLING_KARN,
|
||||
SAMPLING_UNDEFINED
|
||||
} rtt_sampling_method;
|
||||
|
||||
struct segment {
|
||||
struct segment *next;
|
||||
uint32_t num;
|
||||
@ -49,6 +62,8 @@ struct segment {
|
||||
address ip_src;
|
||||
address ip_dst;
|
||||
|
||||
bool ack_karn; /* true when ambiguous according to Karn's algo */
|
||||
|
||||
uint8_t num_sack_ranges;
|
||||
uint32_t sack_left_edge[MAX_TCP_SACK_RANGES];
|
||||
uint32_t sack_right_edge[MAX_TCP_SACK_RANGES];
|
||||
@ -57,6 +72,9 @@ struct segment {
|
||||
struct tcp_graph {
|
||||
tcp_graph_type type;
|
||||
|
||||
/* RTT sampling method (for RTT graphs only) */
|
||||
uint8_t rtt_sampling;
|
||||
|
||||
/* The stream this graph will show */
|
||||
address src_address;
|
||||
uint16_t src_port;
|
||||
@ -98,12 +116,28 @@ struct rtt_unack {
|
||||
unsigned int end_seqno;
|
||||
};
|
||||
|
||||
int rtt_is_retrans(struct rtt_unack * , unsigned int );
|
||||
/**
|
||||
* Check if a sequence number is currently in the Unacked list,
|
||||
* typically for avoiding adding redundant sequences.
|
||||
* In practice, the retrans meaning in this particular code is different
|
||||
* from TCP's one and would rather cover Keep-Alives and Spurious Retrans.
|
||||
*
|
||||
* @param list The list containing the Unacked sequences
|
||||
* @param seqno The sequence number to be searched for in the Unacked list
|
||||
* @return true if the list contains the sequence number, false otherwise
|
||||
*/
|
||||
bool rtt_is_retrans(struct rtt_unack *list, unsigned int seqno);
|
||||
|
||||
struct rtt_unack *rtt_get_new_unack(double , unsigned int , unsigned int );
|
||||
void rtt_put_unack_on_list(struct rtt_unack ** , struct rtt_unack * );
|
||||
void rtt_delete_unack_from_list(struct rtt_unack ** , struct rtt_unack * );
|
||||
void rtt_destroy_unack_list(struct rtt_unack ** );
|
||||
|
||||
static inline int
|
||||
tcp_seq_eq(uint32_t s1, uint32_t s2) {
|
||||
return (int32_t)(s1 - s2) == 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
tcp_seq_before(uint32_t s1, uint32_t s2) {
|
||||
return (int32_t)(s1 - s2) < 0;
|
||||
@ -119,7 +153,8 @@ tcp_seq_after(uint32_t s1, uint32_t s2) {
|
||||
return (int32_t)(s1 - s2) > 0;
|
||||
}
|
||||
|
||||
static inline int tcp_seq_before_or_eq(uint32_t s1, uint32_t s2) {
|
||||
static inline int
|
||||
tcp_seq_before_or_eq(uint32_t s1, uint32_t s2) {
|
||||
return !tcp_seq_after(s1, s2);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user