HTTP2, QUIC: fix "Follow Stream"

"Follow Stream" functionality assumes that all data in a single packet
belongs to the same stream. That is not true for HTTP2 and QUIC, where
we end up having data from unrelated streams.

Filter out the unwanted data directly in the protocol dissector code with
a custom `tap_handler` (as TCP already does).

Close #16093
This commit is contained in:
Nardi Ivan 2021-06-28 13:58:36 +02:00 committed by AndersBroman
parent a7a7849259
commit 3cb302f05b
10 changed files with 119 additions and 7 deletions

View File

@ -3529,6 +3529,7 @@ set(_test_group_list
suite_fileformats
suite_follow
suite_follow_dccp
suite_follow_multistream
suite_io
suite_mergecap
suite_netperfmeter

View File

@ -269,6 +269,11 @@ typedef struct {
tcp_flow_t *fwd_flow;
} http2_session_t;
typedef struct http2_follow_tap_data {
tvbuff_t *tvb;
guint64 stream_id;
} http2_follow_tap_data_t;
#ifdef HAVE_NGHTTP2
/* Decode as functions */
static gpointer
@ -2163,6 +2168,20 @@ http2_follow_index_filter(guint stream, guint sub_stream)
return g_strdup_printf("tcp.stream eq %u and http2.streamid eq %u", stream, sub_stream);
}
static tap_packet_status
follow_http2_tap_listener(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
{
follow_info_t *follow_info = (follow_info_t *)tapdata;
const http2_follow_tap_data_t *follow_data = (const http2_follow_tap_data_t *)data;
if (follow_info->substream_id != SUBSTREAM_UNUSED &&
follow_info->substream_id != follow_data->stream_id) {
return TAP_PACKET_DONT_REDRAW;
}
return follow_tvb_tap_listener(tapdata, pinfo, NULL, follow_data->tvb);
}
static guint8
dissect_http2_header_flags(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *http2_tree, guint offset, guint8 type)
{
@ -3474,7 +3493,12 @@ dissect_http2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
tap_queue_packet(http2_tap, pinfo, http2_stats);
if (have_tap_listener(http2_follow_tap)) {
tap_queue_packet(http2_follow_tap, pinfo, tvb);
http2_follow_tap_data_t *follow_data = wmem_new0(wmem_packet_scope(), http2_follow_tap_data_t);
follow_data->tvb = tvb;
follow_data->stream_id = streamid;
tap_queue_packet(http2_follow_tap, pinfo, follow_data);
}
return tvb_captured_length(tvb);
@ -4113,7 +4137,7 @@ proto_register_http2(void)
http2_follow_tap = register_tap("http2_follow");
register_follow_stream(proto_http2, "http2_follow", http2_follow_conv_filter, http2_follow_index_filter, tcp_follow_address_filter,
tcp_port_to_display, follow_tvb_tap_listener);
tcp_port_to_display, follow_http2_tap_listener);
}
static void http2_stats_tree_init(stats_tree* st)

View File

@ -342,6 +342,11 @@ typedef struct _quic_follow_stream {
guint64 stream_id;
} quic_follow_stream;
typedef struct quic_follow_tap_data {
tvbuff_t *tvb;
guint64 stream_id;
} quic_follow_tap_data_t;
/**
* State for a single QUIC connection, identified by one or more Destination
* Connection IDs (DCID).
@ -1843,7 +1848,12 @@ dissect_quic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree
proto_tree_add_item(ft_tree, hf_quic_stream_data, tvb, offset, (int)length, ENC_NA);
if (have_tap_listener(quic_follow_tap)) {
tap_queue_packet(quic_follow_tap, pinfo, tvb_new_subset_length(tvb, offset, (int)length));
quic_follow_tap_data_t *follow_data = wmem_new0(wmem_packet_scope(), quic_follow_tap_data_t);
follow_data->tvb = tvb_new_subset_remaining(tvb, offset);
follow_data->stream_id = stream_id;
tap_queue_packet(quic_follow_tap, pinfo, follow_data);
}
quic_stream_state *stream = quic_get_stream_state(pinfo, quic_info, from_server, stream_id);
quic_stream_info stream_info = {
@ -3935,10 +3945,15 @@ quic_follow_address_filter(address *src_addr _U_, address *dst_addr _U_, int src
static tap_packet_status
follow_quic_tap_listener(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
{
// TODO fix filtering for multiple streams, see
// https://gitlab.com/wireshark/wireshark/-/issues/16093
follow_tvb_tap_listener(tapdata, pinfo, NULL, data);
return TAP_PACKET_DONT_REDRAW;
follow_info_t *follow_info = (follow_info_t *)tapdata;
const quic_follow_tap_data_t *follow_data = (const quic_follow_tap_data_t *)data;
if (follow_info->substream_id != SUBSTREAM_UNUSED &&
follow_info->substream_id != follow_data->stream_id) {
return TAP_PACKET_DONT_REDRAW;
}
return follow_tvb_tap_listener(tapdata, pinfo, NULL, follow_data->tvb);
}
guint32 get_quic_connections_count(void)

View File

@ -134,6 +134,7 @@ follow_reset_stream(follow_info_t* info)
info->server_ip.len = 0;
info->fragments[0] = info->fragments[1] = NULL;
info->seq[0] = info->seq[1] = 0;
info->substream_id = SUBSTREAM_UNUSED;
}
void

View File

@ -78,6 +78,8 @@ struct _follow_info;
typedef gboolean (*follow_print_line_func)(char *, size_t, gboolean, void *);
typedef frs_return_t (*follow_read_stream_func)(struct _follow_info *follow_info, follow_print_line_func follow_print, void *arg);
#define SUBSTREAM_UNUSED G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF)
typedef struct {
gboolean is_server;
guint32 packet_num;
@ -98,6 +100,7 @@ typedef struct _follow_info {
address client_ip;
address server_ip;
void* gui_data;
guint64 substream_id; /**< Sub-stream; used only by HTTP2 and QUIC */
} follow_info_t;
struct register_follow;

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,63 @@
#
# Wireshark tests
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
'''Follow QUIC/HTTP2 Stream tests'''
import subprocesstest
import fixtures
@fixtures.mark_usefixtures('test_env')
@fixtures.uses_fixtures
class case_follow_multistream(subprocesstest.SubprocessTestCase):
def test_follow_http2_multistream(self, cmd_tshark, capture_file):
'''Checks whether Follow HTTP2 correctly handles multiple streams on the same packet.'''
# Test 1:
# 1. While following stream 25 we should ignore stream 21 at frame 65
proc = self.assertRun((cmd_tshark,
'-r', capture_file('http2_follow_multistream.pcapng'),
'-qz', 'follow,http2,raw,0,25',
))
self.assertIn("""\
===================================================================
Follow: http2,raw
Filter: tcp.stream eq 0 and http2.streamid eq 25
Node 0: 10.9.0.2:59246
Node 1: 104.16.40.2:443
00003301250000001900000005018205a46292c861b1d10c4a6b0b29fcc529ac426cb6a5a3b6a9282aed484759780db6f0e35fa23fc687c5c0c3c2bf
00000408000000001900be0000
0003ac01040000001988e0cfcecddd009021ea496a4ac8292db0c9f4b567a0c4f5ffbd05412c35695916114ff482d12ffa53e57a4fecd45035ea2a54f95e93fb35140d73d9329f2bd27f66a281ae43d2a7fab6a40e52ac6aa83545ff4a7fab6a40e52ac5ee3a3fd29f2b9eb49a937b2d1e9721e950f5a4d49bd968f4ba195c748fd9ea1f842e43d2a78f1e1798e79a82a473523a87316c5c87a54f1e3c2f31cf350558750e8f493110b90f4a891cd48ea1cc5b1798e79a82ae43d2a78f1e17f47b536c655c87a5442fe926a665c87a7ed435332c8b08a7fa416897fd29f2bd27f66a281af5152a7caf49fd9a8a06b9ec994f95e93fb35140d721e95241a47714f95cf5a4d49bd968f4b90f4a9e3c785e639e6a0a91cd48ea1cc5b1721e953c78f0bcc73cd41561d43a3d24c442e43d2a7cae9350542f48eb8cfe5721e95075997a475c67f2b90f4a84b0a349bb9487a693d485cf64ca0e45e43db1d0525062755ea2a7ed490b28eda12b22c229fe905a25ff4a7caf49fd9a8a06bd454a9f2bd27f66a281ae7b2653e57a4fecd45035c87a7ed496c1d255916114f95cf5a4d49bd968f4b90f4a9e3c785e639e6a0a91cd48ea1cc5b1721e953c78f0bcc73cd41561d43a3d24c442e43d2a78f1e17f47b536c655aa390e7ea62ae43d2a26c193a96c495095cf64ca78f1e1745b6772fa98dee93ae43d2a0c843db5250bca6b0b29fcae43d2a0c843db5250bca6b0b29fcae43d2b92a53c78f0bfa3da9b632ae43d3f6a213ea82ac8b08a7fa416897fd29f2bd27f66a281af5152a7caf49fd9a8a06b9ec994f95e93fb35140d721e953fd5b5207295635541aa2ffafb5087aaa2912b22c229fe905a25ff4a7caf49fd9a8a06bd454a9f2bd27f66a281ae7b2653e57a4fecd45035c87a54f95cf5a4d49bd968f4b90f4a9e3c785e639e6a0a91cd48ea1cc5b1721e953c78f0bcc73cd41561d43a3d24c442e43d2a7cae9350542f48eb8cfe5721e95075997a475c67f2b90f4fda849cd448b22c229f2b9eb49a937b2d1e9721e953c78f0bcc73cd415239a91d4398b62e43d2a78f1e1798e79a82ac3a8747a498885c87a54f1e3c2fe8f6a6d8cab54721cfd4c55c87a544d832752d892a12b9ec994f1e3c2e8b6cee5f531bdd275c87a5419087b6a4a1794d61653f95c87a5419087b6a4a1794d61653f95c87a57254a78f1e17f47b536c655c87a76c96df3dbf4a004a681d8a0801754082e32e5c644a62d1bfdbda7f1a9f91a75965d2004e05b5e32c961c9d56aec3c8bda85bc15095e93fb5fc1ecea8d9d8d7d6d5d4d37f138f65a74437e479c2be07596995a1d1bb
0003f80000000000191f8b08000000000000037c55db6ee336107def5724ecc220615ac906db3ec850b740364153e4d266bb794917062d8d646e255220295f56d6bf77a84b6ca5415f649373869c3967662853ea7625e8f4e44e7f97792ea288542a81542a48085b0bf362a89b79ff37b8de8a9bd48802a2b452b1935a515693cac2897546c68eccbd2370c75574fa9e1b2eb9e59a570738b0da80ab8c3a8167087250995bcdde7f8df0fa33f211025b2df1287ace0f461642c3c5e108c16a880437d13b4a7e4cb76226db9066b156a9cc080b12e104252b6d1d61dc0e30fcaf23db1bad8971bd912ad19b4024c9d51a94bb95d68102434901d68a0c085ff1d37386b1608ae6aa10329f4cce683065bffacfdf81ffd617bc390b1c58478f706c32a13ad2533201bf8cc81454ac13f8f27873a98b522bbc6f84679850450de303d797b9444c702d0da47a7b27be69f304c62203bf441f7eee8e0f0c94b98881124cdec1d6451d1784bf6ca4db452a0d725aa9c5fa82e03d7a1a9189363293eaedb07a5a721d0b4f78d061197711049968a9bac74bf67be269457612e9e55f561e3d998cd7a7a82c9695a872473068f9ffaa8d7d03a76ff506cca5b040d994fcd6492adb732a2abda2070eb0da3c891253b481705845adcc5ce31adc5fb2005d397a54b86abf4fe987f373d6f08b9fda9f7454a83688adc5420299ad1ce63925e51603c572b9cc85b7e45a24be5b1a9e1f3972c56adf0626ea89f499dd8a1d98fdfef92b97510d9ec2d071891219d1ba618dcf91bb77016a062aa1124fe126282bbbc23c1b1e1f2e70fe82be79e1d961ef90c1860cfb1daad06335ca45a6c86d27242a6258d7846dbbbae8f7cf0ff741290cb20c6db46c6e37d2c52bea825817855009ab631461c8382ca963f3a501f1cfbcdd3760e5772061f26a3fd5a6588012cbdc7baddfb2a2e0bdb9786556622d33e1bc297b65b2325355b9282aeb166b3032dd9170f70ad3eecbae861798469983c31097af60b9464e48b8c1eda6e1e588359c62e73ca7bece6783d83ca644abdb6ec501994e463e69cf61d0d54d8f7fec086af1eb11be355f2313573d4d2da6781bf36920ab0565ff05ddbf70d6227623c41098b00f1bf587d12518b7a3a49d500fa5bb5104c756de6f9ce8d2cd90195ee7620979d83b1fc00d1ba8e9d418a8f9dcaeee509aa74e993692e52892deb193e8c0ead39164978362adfbe6a8fe5fdcfdbd18214ea6a11b5a65bc9ea3aef8d84be60ddcb17018b15f9cccf145d08f90e0948d1d35537286b3c24995596ceb79d725b554d285c7e1e3a0daefeb060ba619a6e5b73f2b30bbe149e96f60fcd86bf450c230e03370fd8cff040eb9b5f4385338bcb9810f83d6c7033274a379c98fa6734886b13ff3531a63652fb10ed1cd7ff8170000ffff030036351cd507080000
000000000100000019
===================================================================
""".replace("\r\n", "\n"),
proc.stdout_str)
def test_follow_quic_multistream(self, cmd_tshark, capture_file):
'''Checks whether Follow QUIC correctly handles multiple streams on the same packet.'''
# Test 2:
# 1. While following stream 40 we should ignore stream 36 at frame 426, and stream 11 at frame 623
proc = self.assertRun((cmd_tshark,
'-r', capture_file('quic_follow_multistream.pcapng'),
'-qz', 'follow,quic,raw,0,40',
))
self.assertIn("""\
===================================================================
Follow: quic,raw
Filter: quic.connection.number eq 0 and quic.stream.stream_id eq 40
Node 0: 10.0.2.15:57172
Node 1: 172.217.5.110:443
01143000d1aed782e4a7dd81a3a280a0df9f9e9d9c9b
010f3700d982fd819380adf89eeab0aeac00424203500200e4d7dcfefe2fa7a5df3632669896a543445e8a68d10ebc13d704f6af5e7460740ba4e67985c9c8c6b3b9b6b65ce9af3404e6213f749da27b3c523060867c215f30e43343a1a055e4924757cba0cd8fb3b9219712521b197ba3b55d06907660c4a29fee6cb94abce7d1800346c603f68021dad3fdfb7bbada302be0f3e9456a7134b3f0f9c82deb126fa10875b570f96976ea311b77d2e620d2a3cd652555db30dc8da347a1f2be10bc129a180d576cb394a9cfef499a0a037f3a34c6e3047507f07fff215080120182587a6277871ae3e16e2c44ecebdbe77ce1ca6f447a35d2c89749a5054e09679b28fa46bbc58dd10b404ac36436c3e2305f72ff18b847b85e181c0f0ba058c05780a2a12f8ccac69fdf819e4e60b2765852c6304c44064b9ae30b170c8181fde020cee9cb83994176942023abd0c35b0ca571ca0f77a63764b5a170a24f76a4734ef16f858af2950a69b4da1902b8968722e9a328c2f9176d3e71159c82d564e776b8ee6fb648ae94b537ec0efd0898e679d60d230e5f7748a399af565014ddcc22b74b794474155c26c7d02ec9a4180edb2ccd322cd3081a21ed51c0e8e157adb448af052c34dd636a80cb426540cdf0444fcab899105702754b35a95257f9574ab8459a2d20366ed939d280266974f236815603dbc57c250b35dcc0e5a6fa35da21198550625e157282755cfedd0b3f1e1de923dd3f83b1548aeb7411f1e41ae97ca47bb77b467a2462cc8cc8cc1ae95b3edd1c46faee87c1f7e365b7b8782dcbe1a5a7130fa25b94765b32be7029ed5bfeafc40c
000103
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
===================================================================
""".replace("\r\n", "\n"),
proc.stdout_str)

View File

@ -403,6 +403,7 @@ follow_arg_filter(const char **opt_argp, follow_info_t *follow_info)
((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
{
*opt_argp += len;
follow_info->substream_id = cli_follow_info->sub_stream_index;
}
}
else
@ -521,6 +522,7 @@ static void follow_stream(const char *opt_argp, void *userdata)
}
follow_info = g_new0(follow_info_t, 1);
follow_info->gui_data = cli_follow_info;
follow_info->substream_id = SUBSTREAM_UNUSED;
cli_follow_info->follower = follower;
follow_arg_mode(&opt_argp, follow_info);

View File

@ -117,6 +117,7 @@ FollowStreamDialog::FollowStreamDialog(QWidget &parent, CaptureFile &cf, follow_
memset(&follow_info_, 0, sizeof(follow_info_));
follow_info_.show_stream = BOTH_HOSTS;
follow_info_.substream_id = SUBSTREAM_UNUSED;
ui->teStreamContent->installEventFilter(this);
@ -984,6 +985,8 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index,
filter_out_filter_ = QString("!(%1)").arg(follow_filter);
}
follow_info_.substream_id = sub_stream_num;
/* data will be passed via tap callback*/
if (!registerTapListener(get_follow_tap_string(follower_), &follow_info_,
follow_filter.toUtf8().constData(),