aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-quic.c
diff options
context:
space:
mode:
authorNardi Ivan <nardi.ivan@gmail.com>2021-06-21 18:01:15 +0200
committerWireshark GitLab Utility <gerald+gitlab-utility@wireshark.org>2021-07-07 13:08:19 +0000
commitc2d77d910dd201afc263bd9fcdb0f4e10862c1b3 (patch)
treeb71ce9c2c0185548b3714b3082dc5dc104ebbfa3 /epan/dissectors/packet-quic.c
parenta6932f56dc11ee0a9ff7333db7779663804036bc (diff)
QUIC: improve "Follow QUIC Stream" support
This functionality has been added in d2a660d8, where its limitations are described. Improvements: * the Substream index menu now properly filters for available stream numbers; * Follow Stream selects the first stream in the current packet Known issue (which is still there): if a packet contains multiple QUIC streams, then we will show data also from streams other than the selected one (see #16093) Note that there is no way to follow a QUIC connection. Close #17453
Diffstat (limited to 'epan/dissectors/packet-quic.c')
-rw-r--r--epan/dissectors/packet-quic.c129
1 files changed, 124 insertions, 5 deletions
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;