aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
authorNardi Ivan <nardi.ivan@gmail.com>2020-09-04 14:11:34 +0200
committerAndersBroman <a.broman58@gmail.com>2020-09-05 07:02:04 +0000
commita46b62fcab53a42ce09764b03cccf4bd2136931d (patch)
tree5aeb4f0a3d6c13280f097326514c73d5d995b611 /epan
parent51cbb47e1ffef1de5949432a6a03519468a8ae3f (diff)
(G)QUIC: improve dissection capabilities (Q050, T050 and T051)
Fix support for Q050 and add support for T050 and T051. For these 3 versions, add dissection of (at least) Initial Packets. For salts and other info, see: "A Guide to Parsing QUIC Client Hellos for Network Middlebox Vendors" https://docs.google.com/document/d/1GV2j-PGl7YGFqmWbYvzu7-UNVIpFdbprtmN9tt6USG8/preview Note these versions are actively used by Chrome right now. Based on https://code.wireshark.org/review/#/c/37492/ done by @alagoutte
Diffstat (limited to 'epan')
-rw-r--r--epan/dissectors/packet-gquic.c101
-rw-r--r--epan/dissectors/packet-quic.c48
-rw-r--r--epan/dissectors/packet-quic.h10
3 files changed, 134 insertions, 25 deletions
diff --git a/epan/dissectors/packet-gquic.c b/epan/dissectors/packet-gquic.c
index 7ce7a86026..1764223df8 100644
--- a/epan/dissectors/packet-gquic.c
+++ b/epan/dissectors/packet-gquic.c
@@ -24,13 +24,14 @@ QUIC source code in Chromium : https://code.google.com/p/chromium/codesearch#chr
#include <epan/expert.h>
#include <epan/conversation.h>
#include <epan/dissectors/packet-http2.h>
+#include <epan/dissectors/packet-quic.h>
#include <wsutil/strtoi.h>
void proto_register_gquic(void);
void proto_reg_handoff_gquic(void);
static dissector_handle_t gquic_handle;
-
+static dissector_handle_t tls13_handshake_handle;
static int proto_gquic = -1;
static int hf_gquic_puflags = -1;
@@ -71,6 +72,9 @@ static int hf_gquic_frame_type_wu_byte_offset = -1;
static int hf_gquic_frame_type_blocked_stream_id = -1;
static int hf_gquic_frame_type_sw_send_entropy = -1;
static int hf_gquic_frame_type_sw_least_unacked_delta = -1;
+static int hf_gquic_crypto_offset = -1;
+static int hf_gquic_crypto_length = -1;
+static int hf_gquic_crypto_crypto_data = -1;
static int hf_gquic_frame_type_stream = -1;
static int hf_gquic_frame_type_stream_f = -1;
static int hf_gquic_frame_type_stream_d = -1;
@@ -187,13 +191,6 @@ static expert_field ei_gquic_tag_unknown = EI_INIT;
static expert_field ei_gquic_version_invalid = EI_INIT;
-typedef struct gquic_info_data {
- guint8 version;
- gboolean version_valid;
- gboolean encoding;
- guint16 server_port;
-} gquic_info_data_t;
-
#define GQUIC_MIN_LENGTH 3
#define GQUIC_MAGIC2 0x513032
#define GQUIC_MAGIC3 0x513033
@@ -252,6 +249,9 @@ static const value_string puflags_pkn_vals[] = {
#define FT_BLOCKED 0x05
#define FT_STOP_WAITING 0x06
#define FT_PING 0x07
+/* CRYPTO is not a real GQUIC frame, but a QUIC one. Since some GQUIC flows
+ * have this kind of frame, try handling it like all the others */
+#define FT_CRYPTO 0x08
/**************************************************************************/
/* Frame Type Special */
@@ -280,7 +280,8 @@ static const range_string frame_type_vals[] = {
{ 5,5, "BLOCKED" },
{ 6,6, "STOP_WAITING" },
{ 7,7, "PING" },
- { 8,31, "Unknown" },
+ { 8,8, "CRYPTO" },
+ { 9,31, "Unknown" },
{ 32,63, "CONGESTION_FEEDBACK (Special Frame Type)" },
{ 64,127, "ACK (Special Frame Type)" },
{ 128,256, "STREAM (Special Frame Type)" },
@@ -1196,7 +1197,6 @@ gboolean is_gquic_unencrypt(tvbuff_t *tvb, packet_info *pinfo, guint offset, gui
break;
}
} else {
-
/* Special Frame Type */
if(frame_type & FTFLAGS_STREAM){ /* Stream */
@@ -1638,13 +1638,28 @@ dissect_gquic_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, gui
}
-static int
+static guint32
+dissect_gquic_tags(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ft_tree, guint offset, gquic_info_data_t *gquic_info){
+ guint32 tag_number;
+
+ proto_tree_add_item(ft_tree, hf_gquic_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ tag_number = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
+ offset += 2;
+
+ proto_tree_add_item(ft_tree, hf_gquic_padding, tvb, offset, 2, ENC_NA);
+ offset += 2;
+
+ offset = dissect_gquic_tag(tvb, pinfo, ft_tree, offset, tag_number, gquic_info);
+
+ return offset;
+}
+
+int
dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint8 len_pkn, gquic_info_data_t *gquic_info){
proto_item *ti, *ti_ft, *ti_ftflags /*, *expert_ti*/;
proto_tree *ft_tree, *ftflags_tree;
guint8 frame_type;
guint8 num_ranges, num_revived, num_blocks = 0, num_timestamp;
- guint32 tag_number;
guint32 len_stream = 0, len_offset = 0, len_data = 0, len_largest_observed = 1, len_missing_packet = 1;
ti_ft = proto_tree_add_item(gquic_tree, hf_gquic_frame, tvb, offset, 1, ENC_NA);
@@ -1655,7 +1670,7 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr
frame_type = tvb_get_guint8(tvb, offset);
proto_item_set_text(ti_ft, "%s", rval_to_str(frame_type, frame_type_vals, "Unknown"));
- if((frame_type & FTFLAGS_SPECIAL) == 0){ /* Regular Stream Flags */
+ if((frame_type & FTFLAGS_SPECIAL) == 0 && frame_type != FT_CRYPTO){ /* Regular Stream Flags */
offset += 1;
switch(frame_type){
case FT_PADDING:{
@@ -1665,7 +1680,8 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr
ti_pad_len = proto_tree_add_uint(ft_tree, hf_gquic_frame_type_padding_length, tvb, offset, 0, pad_len);
proto_item_set_generated(ti_pad_len);
proto_item_append_text(ti_ft, " Length: %u", pad_len);
- proto_tree_add_item(ft_tree, hf_gquic_frame_type_padding, tvb, offset, -1, ENC_NA);
+ if(pad_len > 0) /* Avoid Malformed Exception with pad_len == 0 */
+ proto_tree_add_item(ft_tree, hf_gquic_frame_type_padding, tvb, offset, -1, ENC_NA);
offset += pad_len;
}
break;
@@ -1757,7 +1773,37 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr
ftflags_tree = proto_item_add_subtree(ti_ftflags, ett_gquic_ftflags);
proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream , tvb, offset, 1, ENC_NA);
- if(frame_type & FTFLAGS_STREAM){ /* Stream Flags */
+ if(frame_type == FT_CRYPTO) {
+ guint64 crypto_offset, crypto_length;
+ gint32 lenvar;
+
+ DISSECTOR_ASSERT(gquic_info->version_valid && gquic_info->version >= 50);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", CRYPTO");
+ offset += 1;
+ proto_tree_add_item_ret_varint(ft_tree, hf_gquic_crypto_offset, tvb, offset, -1, ENC_VARINT_QUIC, &crypto_offset, &lenvar);
+ offset += lenvar;
+ proto_tree_add_item_ret_varint(ft_tree, hf_gquic_crypto_length, tvb, offset, -1, ENC_VARINT_QUIC, &crypto_length, &lenvar);
+ offset += lenvar;
+ proto_tree_add_item(ft_tree, hf_gquic_crypto_crypto_data, tvb, offset, (guint32)crypto_length, ENC_NA);
+
+ if (gquic_info->version == 50) {
+ message_tag = tvb_get_ntohl(tvb, offset);
+ ti = proto_tree_add_item_ret_string(ft_tree, hf_gquic_tag, tvb, offset, 4, ENC_ASCII|ENC_NA, wmem_packet_scope(), &message_tag_str);
+ proto_item_append_text(ti, " (%s)", val_to_str(message_tag, message_tag_vals, "Unknown Tag"));
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(message_tag, message_tag_vals, "Unknown"));
+ offset += 4;
+
+ offset = dissect_gquic_tags(tvb, pinfo, ft_tree, offset, gquic_info);
+ } else { /* T050 and T051 */
+ tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, (int)crypto_length);
+ col_set_writable(pinfo->cinfo, -1, FALSE);
+ call_dissector_with_data(tls13_handshake_handle, next_tvb, pinfo, ft_tree, GUINT_TO_POINTER(crypto_offset));
+ col_set_writable(pinfo->cinfo, -1, TRUE);
+ offset += (guint32)crypto_length;
+ }
+
+ } else if(frame_type & FTFLAGS_STREAM){ /* Stream Flags */
proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_f, tvb, offset, 1, ENC_NA);
proto_tree_add_item(ftflags_tree, hf_gquic_frame_type_stream_d, tvb, offset, 1, ENC_NA);
if(frame_type & FTFLAGS_STREAM_D){
@@ -1799,14 +1845,7 @@ dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tr
col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(message_tag, message_tag_vals, "Unknown"));
offset += 4;
- proto_tree_add_item(ft_tree, hf_gquic_tag_number, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- tag_number = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
- offset += 2;
-
- proto_tree_add_item(ft_tree, hf_gquic_padding, tvb, offset, 2, ENC_NA);
- offset += 2;
-
- offset = dissect_gquic_tag(tvb, pinfo, ft_tree, offset, tag_number, gquic_info);
+ offset = dissect_gquic_tags(tvb, pinfo, ft_tree, offset, gquic_info);
break;
}
case 3: { /* Reserved H2 HEADERS (or PUSH_PROMISE..) */
@@ -2412,6 +2451,21 @@ proto_register_gquic(void)
FT_UINT64, BASE_DEC, NULL, 0x0,
"A variable length packet number delta with the same length as the packet header's packet number", HFILL }
},
+ { &hf_gquic_crypto_offset,
+ { "Offset", "gquic.crypto.offset",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Byte offset into the stream", HFILL }
+ },
+ { &hf_gquic_crypto_length,
+ { "Length", "gquic.crypto.length",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Length of the Crypto Data field", HFILL }
+ },
+ { &hf_gquic_crypto_crypto_data,
+ { "Crypto Data", "gquic.crypto.crypto_data",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "The cryptographic message data", HFILL }
+ },
{ &hf_gquic_frame_type_stream,
{ "Stream", "gquic.frame_type.stream",
FT_BOOLEAN, 8, NULL, FTFLAGS_STREAM,
@@ -2920,6 +2974,7 @@ proto_register_gquic(void)
void
proto_reg_handoff_gquic(void)
{
+ tls13_handshake_handle = find_dissector("tls13-handshake");
dissector_add_uint_range_with_preference("udp.port", "", gquic_handle);
heur_dissector_add("udp", dissect_gquic_heur, "Google QUIC", "gquic", proto_gquic, HEURISTIC_ENABLE);
}
diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c
index 5110bd211c..cf369b0d19 100644
--- a/epan/dissectors/packet-quic.c
+++ b/epan/dissectors/packet-quic.c
@@ -326,6 +326,7 @@ 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. */
+ gquic_info_data_t *gquic_info; /**< GQUIC info for >Q050 flows. */
} quic_info_data_t;
/** Per-packet information about QUIC, populated on the first pass. */
@@ -376,6 +377,13 @@ static inline guint8 quic_draft_version(guint32 version) {
if (version == 0xfaceb002) {
return 27;
}
+ /* GQUIC Q050, T050 and T051: they are not really based on any drafts,
+ * but we must return a sensible value */
+ if (version == 0x51303530 ||
+ version == 0x54303530 ||
+ version == 0x54303531) {
+ return 27;
+ }
return 0;
}
@@ -387,7 +395,9 @@ static inline gboolean is_quic_draft_max(guint32 version, guint8 max_version) {
const value_string quic_version_vals[] = {
{ 0x00000000, "Version Negotiation" },
{ 0x51303434, "Google Q044" },
- { 0x51303530, "Google Q050 (draft-27)" },
+ { 0x51303530, "Google Q050" },
+ { 0x54303530, "Google T050" },
+ { 0x54303531, "Google T051" },
{ 0xfaceb001, "Facebook mvfst (draft-22)" },
{ 0xfaceb002, "Facebook mvfst (draft-27)" },
{ 0xff000004, "draft-04" },
@@ -894,6 +904,22 @@ quic_connection_create(packet_info *pinfo, guint32 version)
conversation_t *conv = find_or_create_conversation(pinfo);
conversation_add_proto_data(conv, proto_quic, conn);
+ if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531) {
+ gquic_info_data_t *gquic_info;
+
+ gquic_info = wmem_new(wmem_file_scope(), gquic_info_data_t);
+ if (version == 0x51303530)
+ gquic_info->version = 50;
+ else if (version == 0x54303530)
+ gquic_info->version = 150;
+ else
+ gquic_info->version = 151;
+ gquic_info->encoding = ENC_LITTLE_ENDIAN;
+ gquic_info->version_valid = TRUE;
+ gquic_info->server_port = pinfo->destport;
+ conn->gquic_info = gquic_info;
+ }
+
return conn;
}
@@ -1985,6 +2011,14 @@ quic_derive_initial_secrets(const quic_cid_t *cid,
0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94,
0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45
};
+ static const guint8 hanshake_salt_draft_t50[20] = {
+ 0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, 0x91, 0x55, 0x80,
+ 0x30, 0x4c, 0x43, 0xa2, 0x36, 0x7c, 0x60, 0x48, 0x83, 0x10
+ };
+ static const gint8 hanshake_salt_draft_t51[20] = {
+ 0x7a, 0x4e, 0xde, 0xf4, 0xe7, 0xcc, 0xee, 0x5f, 0xa4, 0x50,
+ 0x6c, 0x19, 0x12, 0x4f, 0xc8, 0xcc, 0xda, 0x6e, 0x03, 0x3d
+ };
gcry_error_t err;
guint8 secret[HASH_SHA2_256_LENGTH];
@@ -1992,6 +2026,12 @@ quic_derive_initial_secrets(const quic_cid_t *cid,
if (version == 0x51303530) {
err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_q50, sizeof(hanshake_salt_draft_q50),
cid->cid, cid->len, secret);
+ } else if (version == 0x54303530) {
+ err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t50, sizeof(hanshake_salt_draft_t50),
+ cid->cid, cid->len, secret);
+ } else if (version == 0x54303531) {
+ err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t51, sizeof(hanshake_salt_draft_t51),
+ cid->cid, cid->len, secret);
} else if (is_quic_draft_max(version, 22)) {
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_22, sizeof(handshake_salt_draft_22),
cid->cid, cid->len, secret);
@@ -2382,7 +2422,11 @@ quic_process_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_
guint decrypted_offset = 0;
while (tvb_reported_length_remaining(decrypted_tvb, decrypted_offset) > 0) {
- decrypted_offset = dissect_quic_frame_type(decrypted_tvb, pinfo, tree, decrypted_offset, quic_info, from_server);
+ if (quic_info->version == 0x51303530 || quic_info->version == 0x54303530 || quic_info->version == 0x54303531) {
+ decrypted_offset = dissect_gquic_frame_type(decrypted_tvb, pinfo, tree, decrypted_offset, pkn_len, quic_info->gquic_info);
+ } else {
+ decrypted_offset = dissect_quic_frame_type(decrypted_tvb, pinfo, tree, decrypted_offset, quic_info, from_server);
+ }
}
} else if (quic_info->skip_decryption) {
expert_add_info_format(pinfo, ti, &ei_quic_decryption_failed,
diff --git a/epan/dissectors/packet-quic.h b/epan/dissectors/packet-quic.h
index 1802ec2329..5bb3074b58 100644
--- a/epan/dissectors/packet-quic.h
+++ b/epan/dissectors/packet-quic.h
@@ -50,6 +50,16 @@ void *quic_stream_get_proto_data(struct _packet_info *pinfo, quic_stream_info
/** Returns the number of items for quic.connection.number. */
WS_DLL_PUBLIC guint32 get_quic_connections_count(void);
+typedef struct gquic_info_data {
+ guint8 version;
+ gboolean version_valid;
+ gboolean encoding;
+ guint16 server_port;
+} gquic_info_data_t;
+
+int
+dissect_gquic_frame_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *gquic_tree, guint offset, guint8 len_pkn, gquic_info_data_t *gquic_info);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */