aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debian/libwireshark0.symbols2
-rw-r--r--doc/tshark.pod2
-rw-r--r--docbook/wsug_src/WSUG_chapter_advanced.adoc2
-rw-r--r--epan/dissectors/packet-quic.c129
-rw-r--r--epan/dissectors/packet-quic.h16
-rw-r--r--ui/qt/follow_stream_dialog.cpp55
-rw-r--r--ui/qt/main_window_slots.cpp1
7 files changed, 189 insertions, 18 deletions
diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols
index 48334cdee8..7b52712fb9 100644
--- a/debian/libwireshark0.symbols
+++ b/debian/libwireshark0.symbols
@@ -1332,6 +1332,8 @@ libwireshark.so.0 libwireshark0 #MINVER#
q932_PresentedNumberScreened_vals@Base 2.1.0
q932_PresentedNumberUnscreened_vals@Base 2.1.0
q932_ScreeningIndicator_vals@Base 2.5.0
+ quic_get_stream_id_le@Base 3.5.0
+ quic_get_stream_id_ge@Base 3.5.0
qs_func_vals@Base 1.9.1
qs_rate_vals_ext@Base 1.9.1
raknet_add_udp_dissector@Base 2.3.0
diff --git a/doc/tshark.pod b/doc/tshark.pod
index cefb3f63b9..2bb10e2e21 100644
--- a/doc/tshark.pod
+++ b/doc/tshark.pod
@@ -1261,7 +1261,7 @@ stream on the first TCP session (index 0) with HTTP/2 Stream ID 1.
0000002B 00 40 00 00 00 00 00 00 01 89 50 4e 47 0d 0a 1a .@...... ..PNG...
QUIC streams can be selected through B<-z "follow,quic,hex,3,0">, the first
-number indicates the UDP stream index whereas the second number selects the QUIC
+number indicates the QUIC connection number whereas the second number selects the QUIC
Stream ID.
=item B<-z> h225,counter[I<,filter>]
diff --git a/docbook/wsug_src/WSUG_chapter_advanced.adoc b/docbook/wsug_src/WSUG_chapter_advanced.adoc
index cd793ea1a5..5f72b769ec 100644
--- a/docbook/wsug_src/WSUG_chapter_advanced.adoc
+++ b/docbook/wsug_src/WSUG_chapter_advanced.adoc
@@ -187,7 +187,7 @@ a HTTP/2 Stream Index (field name `http2.streamid`) which are unique within a
TCP connection. The “Stream” selector determines the TCP connection whereas the
“Substream” selector is used to pick the HTTP/2 Stream ID.
-The QUIC protocol is similar, the first number selects the UDP stream index
+The QUIC protocol is similar, the first number selects the QUIC connection number
while the "Substream" field selects the QUIC Stream ID.
.The “Follow SIP Call” dialog box
diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c
index 50947a527a..12069f024e 100644
--- a/epan/dissectors/packet-quic.c
+++ b/epan/dissectors/packet-quic.c
@@ -40,6 +40,7 @@
* - STREAM with sizes larger than 32 bit are unsupported. STREAM sizes can be
* up to 62 bit in QUIC, but the TVB and reassembly API is limited to 32 bit.
* - Out-of-order and overlapping STREAM frame data is not handled.
+ * - "Follow QUIC Stream" doesn't work with STREAM IDs larger than 32 bit
*/
#include <config.h>
@@ -334,6 +335,14 @@ typedef struct _quic_stream_state {
} quic_stream_state;
/**
+ * Data used to allow "Follow QUIC Stream" functionality
+ */
+typedef struct _quic_follow_stream {
+ guint32 num;
+ guint64 stream_id;
+} quic_follow_stream;
+
+/**
* State for a single QUIC connection, identified by one or more Destination
* Connection IDs (DCID).
*/
@@ -366,6 +375,8 @@ typedef struct quic_info_data {
dissector_handle_t app_handle; /**< Application protocol handle (NULL if unknown). */
wmem_map_t *client_streams; /**< Map from Stream ID -> STREAM info (guint64 -> quic_stream_state), sent by the client. */
wmem_map_t *server_streams; /**< Map from Stream ID -> STREAM info (guint64 -> quic_stream_state), sent by the server. */
+ wmem_list_t *streams_list; /**< Ordered list of QUIC Stream ID in this connection (both directions). Used by "Follow QUIC Stream" functionality */
+ wmem_map_t *streams_map; /**< Map pinfo->num --> First stream in that frame (guint -> quic_follow_stream). Used by "Follow QUIC Stream" functionality */
gquic_info_data_t *gquic_info; /**< GQUIC info for >Q050 flows. */
} quic_info_data_t;
@@ -666,6 +677,8 @@ static const val64_string quic_frame_id_direction[] = {
static void
quic_extract_header(tvbuff_t *tvb, guint8 *long_packet_type, guint32 *version,
quic_cid_t *dcid, quic_cid_t *scid);
+static void
+quic_streams_add(packet_info *pinfo, quic_info_data_t *quic_info, guint64 stream_id);
static void
quic_hp_cipher_reset(quic_hp_cipher *hp_cipher)
@@ -1805,6 +1818,10 @@ dissect_quic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree
proto_item_append_text(ti_ft, " fin=%d", !!(frame_type & FTFLAGS_STREAM_FIN));
+ if (!PINFO_FD_VISITED(pinfo)) {
+ quic_streams_add(pinfo, quic_info, stream_id);
+ }
+
if (frame_type & FTFLAGS_STREAM_OFF) {
proto_tree_add_item_ret_varint(ft_tree, hf_quic_stream_offset, tvb, offset, -1, ENC_VARINT_QUIC, &stream_offset, &lenvar);
offset += lenvar;
@@ -3774,6 +3791,104 @@ quic_cleanup(void)
/* Follow QUIC Stream functionality {{{ */
+static void
+quic_streams_add(packet_info *pinfo, quic_info_data_t *quic_info, guint64 stream_id)
+{
+ /* List: ordered list of Stream IDs in this connection */
+ if (!quic_info->streams_list) {
+ quic_info->streams_list = wmem_list_new(wmem_file_scope());
+ }
+ if (!wmem_list_find(quic_info->streams_list, (void *)(stream_id))) {
+ wmem_list_insert_sorted(quic_info->streams_list, (void *)(stream_id),
+ uint64_compare);
+ }
+
+ /* Map: first Stream ID for each UDP payload */
+ quic_follow_stream *stream;
+ if (!quic_info->streams_map) {
+ quic_info->streams_map = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
+ }
+ stream = wmem_map_lookup(quic_info->streams_map, GUINT_TO_POINTER(pinfo->num));
+ if (!stream) {
+ stream = wmem_new0(wmem_file_scope(), quic_follow_stream);
+ stream->num = pinfo->num;
+ stream->stream_id = stream_id;
+ wmem_map_insert(quic_info->streams_map, GUINT_TO_POINTER(stream->num), stream);
+ }
+}
+
+static quic_info_data_t *
+get_conn_by_number(guint conn_number)
+{
+ quic_info_data_t *conn;
+ wmem_list_frame_t *elem;
+
+ elem = wmem_list_head(quic_connections);
+ while (elem) {
+ conn = (quic_info_data_t *)wmem_list_frame_data(elem);
+ if (conn->number == conn_number)
+ return conn;
+ elem = wmem_list_frame_next(elem);
+ }
+ return NULL;
+}
+
+gboolean
+quic_get_stream_id_le(guint streamid, guint sub_stream_id, guint *sub_stream_id_out)
+{
+ quic_info_data_t *quic_info;
+ wmem_list_frame_t *curr_entry;
+ guint64 prev_stream_id;
+
+ quic_info = get_conn_by_number(streamid);
+ if (!quic_info) {
+ return FALSE;
+ }
+
+ prev_stream_id = G_MAXUINT64;
+ curr_entry = wmem_list_head(quic_info->streams_list);
+ while (curr_entry) {
+ if ((guint64)wmem_list_frame_data(curr_entry) > sub_stream_id &&
+ prev_stream_id != G_MAXUINT64) {
+ *sub_stream_id_out = (guint)prev_stream_id;
+ return TRUE;
+ }
+ prev_stream_id = (guint64)wmem_list_frame_data(curr_entry);
+ curr_entry = wmem_list_frame_next(curr_entry);
+ }
+
+ if (prev_stream_id != G_MAXUINT64) {
+ *sub_stream_id_out = (guint)prev_stream_id;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+quic_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id_out)
+{
+ quic_info_data_t *quic_info;
+ wmem_list_frame_t *curr_entry;
+
+ quic_info = get_conn_by_number(streamid);
+ if (!quic_info) {
+ return FALSE;
+ }
+
+ curr_entry = wmem_list_head(quic_info->streams_list);
+ while (curr_entry) {
+ if ((guint64)wmem_list_frame_data(curr_entry) >= sub_stream_id) {
+ /* StreamIDs are 64 bits long in QUIC, but "Follow Stream" generic code uses guint variables */
+ *sub_stream_id_out = (guint)(guint64)wmem_list_frame_data(curr_entry);
+ return TRUE;
+ }
+ curr_entry = wmem_list_frame_next(curr_entry);
+ }
+
+ return FALSE;
+}
+
static gchar *
quic_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint *stream, guint *sub_stream)
{
@@ -3784,11 +3899,15 @@ quic_follow_conv_filter(epan_dissect_t *edt _U_, packet_info *pinfo, guint *stre
if (!conn) {
return NULL;
}
- // XXX Look up stream ID for the current packet.
- guint stream_id = 0;
- *stream = conn->number;
- *sub_stream = stream_id;
- return g_strdup_printf("quic.connection.number eq %u and quic.stream.stream_id eq %u", conn->number, stream_id);
+
+ /* First Stream ID in the selected packet */
+ quic_follow_stream *s;
+ s = wmem_map_lookup(conn->streams_map, GUINT_TO_POINTER(pinfo->num));
+ if (s) {
+ *stream = conn->number;
+ *sub_stream = (guint)s->stream_id;
+ return g_strdup_printf("quic.connection.number eq %u and quic.stream.stream_id eq %u", conn->number, *sub_stream);
+ }
}
return NULL;
diff --git a/epan/dissectors/packet-quic.h b/epan/dissectors/packet-quic.h
index 3b2fb80435..8790f86ce8 100644
--- a/epan/dissectors/packet-quic.h
+++ b/epan/dissectors/packet-quic.h
@@ -79,6 +79,22 @@ void
quic_add_loss_bits(packet_info *pinfo, guint64 value);
void
quic_proto_tree_add_version(tvbuff_t *tvb, proto_tree *tree, int hfindex, guint offset);
+
+/**
+ * Retrieves the QUIC Stream ID which is smaller than or equal to the provided
+ * ID. If available, sub_stream_id_out will be set and TRUE is returned.
+ */
+WS_DLL_PUBLIC gboolean
+quic_get_stream_id_le(guint streamid, guint sub_stream_id, guint *sub_stream_id_out);
+
+/**
+ * Retrieves the QUIC Stream ID which is greater than or equal to the provided
+ * ID. If available, sub_stream_id_out will be set and TRUE is returned.
+ */
+WS_DLL_PUBLIC gboolean
+quic_get_stream_id_ge(guint streamid, guint sub_stream_id, guint *sub_stream_id_out);
+
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp
index 4e36e09ea5..cdd598d256 100644
--- a/ui/qt/follow_stream_dialog.cpp
+++ b/ui/qt/follow_stream_dialog.cpp
@@ -252,6 +252,9 @@ void FollowStreamDialog::updateWidgets(bool follow_in_progress)
ui->cbDirections->setEnabled(enable);
ui->cbCharset->setEnabled(enable);
ui->streamNumberSpinBox->setEnabled(enable);
+ if (follow_type_ == FOLLOW_HTTP2 || follow_type_ == FOLLOW_QUIC) {
+ ui->subStreamNumberSpinBox->setEnabled(enable);
+ }
ui->leFind->setEnabled(enable);
ui->bFind->setEnabled(enable);
b_filter_out_->setEnabled(enable);
@@ -399,12 +402,32 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num)
sub_stream_num = ui->subStreamNumberSpinBox->value();
ui->subStreamNumberSpinBox->blockSignals(false);
+ /* We need to find a suitable sub stream for the new stream */
+ guint sub_stream_num_new = static_cast<guint>(sub_stream_num);
+ gboolean ok;
if (sub_stream_num < 0) {
- sub_stream_num = 0;
+ // Stream ID 0 should always exist as it is used for control messages.
+ sub_stream_num_new = 0;
+ ok = TRUE;
+ } else if (follow_type_ == FOLLOW_HTTP2) {
+ ok = http2_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
+ if (!ok) {
+ ok = http2_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
+ }
+ } else if (follow_type_ == FOLLOW_QUIC) {
+ ok = quic_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
+ if (!ok) {
+ ok = quic_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
+ }
+ } else {
+ // Should not happen, this field is only visible for suitable protocols.
+ return;
}
+ sub_stream_num = static_cast<gint>(sub_stream_num_new);
- if (stream_num >= 0) {
+ if (stream_num >= 0 && ok) {
follow(previous_filter_, true, stream_num, sub_stream_num);
+ previous_sub_stream_num_ = sub_stream_num;
}
}
@@ -432,8 +455,11 @@ void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_n
ok = http2_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
}
} else if (follow_type_ == FOLLOW_QUIC) {
- // TODO clamp the stream IDs correctly for QUIC
- ok = TRUE;
+ if (previous_sub_stream_num_ < sub_stream_num) {
+ ok = quic_get_stream_id_ge(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
+ } else {
+ ok = quic_get_stream_id_le(static_cast<guint>(stream_num), sub_stream_num_new, &sub_stream_num_new);
+ }
} else {
// Should not happen, this field is only visible for suitable protocols.
return;
@@ -935,9 +961,15 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index,
follow_filter = gchar_free_to_qstring(get_follow_conv_func(follower_)(cap_file_.capFile()->edt, &cap_file_.capFile()->edt->pi, &stream_num, &sub_stream_num));
}
if (follow_filter.isEmpty()) {
- QMessageBox::warning(this,
- tr("Error creating filter for this stream."),
- tr("A transport or network layer header is needed."));
+ if (follow_type_ == FOLLOW_QUIC) {
+ QMessageBox::warning(this,
+ tr("Error creating filter for this stream."),
+ tr("QUIC streams not found on the selected packet."));
+ } else {
+ QMessageBox::warning(this,
+ tr("Error creating filter for this stream."),
+ tr("A transport or network layer header is needed."));
+ }
return false;
}
@@ -1038,17 +1070,18 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index,
ui->streamNumberSpinBox->setMaximum(stream_count-1);
ui->streamNumberSpinBox->setValue(stream_num);
ui->streamNumberSpinBox->blockSignals(false);
- ui->streamNumberSpinBox->setToolTip(tr("%Ln total stream(s).", "", stream_count));
+ ui->streamNumberSpinBox->setToolTip(tr("Total number of QUIC connections: %Ln", "", stream_count));
ui->streamNumberLabel->setToolTip(ui->streamNumberSpinBox->toolTip());
- // TODO extract number of QUIC streams?
- stream_count = G_MAXINT32;
+ guint substream_max_id = 0;
+ quic_get_stream_id_le(static_cast<guint>(stream_num), G_MAXINT32, &substream_max_id);
+ stream_count = static_cast<gint>(substream_max_id);
ui->subStreamNumberSpinBox->blockSignals(true);
ui->subStreamNumberSpinBox->setEnabled(true);
ui->subStreamNumberSpinBox->setMaximum(stream_count);
ui->subStreamNumberSpinBox->setValue(sub_stream_num);
ui->subStreamNumberSpinBox->blockSignals(false);
- ui->subStreamNumberSpinBox->setToolTip(tr("%Ln total sub stream(s).", "", stream_count));
+ ui->subStreamNumberSpinBox->setToolTip(tr("Max QUIC Stream ID for the selected connection: %Ln", "", stream_count));
ui->subStreamNumberSpinBox->setToolTip(ui->subStreamNumberSpinBox->toolTip());
ui->subStreamNumberSpinBox->setVisible(true);
ui->subStreamNumberLabel->setVisible(true);
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 7ed97620b0..4e6eb12743 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -1189,6 +1189,7 @@ void MainWindow::setMenusForSelectedPacket()
is_dccp = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "dccp");
is_http = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "http");
is_http2 = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "http2");
+ /* TODO: to follow a QUIC stream we need a *decrypted* QUIC connection, i.e. checking for "quic" in the protocol stack is not enough */
is_quic = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "quic");
is_sip = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "sip");
}