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:
parent
a7a7849259
commit
3cb302f05b
|
@ -3529,6 +3529,7 @@ set(_test_group_list
|
|||
suite_fileformats
|
||||
suite_follow
|
||||
suite_follow_dccp
|
||||
suite_follow_multistream
|
||||
suite_io
|
||||
suite_mergecap
|
||||
suite_netperfmeter
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
@ -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)
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue