aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2018-09-13 17:03:19 +0200
committerPeter Wu <peter@lekensteyn.nl>2018-09-14 10:28:37 +0000
commit011224bff549d57ad5b0b45d67ebf45bdda11e57 (patch)
treea41e764bbe12afc02ad90e00d1a8db03d28a582f
parent28a7a79cac425d1b1ecf06e73add41edd2241e49 (diff)
QUIC: split per-packet state to fix packet coalescing
Packet coalescing requires separate state for every QUIC packet. Split the state as each UDP datagram can contain multiple QUIC packets. Most datagrams will have only one packet, so to avoid multiple allocations, just embed the first packet into the datagram info and use a linked list for simplicity. Change-Id: Ib921e68a7312c6eccb5601b8951ff64cc9ea49cd Ping-Bug: 13881 Reviewed-on: https://code.wireshark.org/review/29647 Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl>
-rw-r--r--epan/dissectors/packet-quic.c89
1 files changed, 54 insertions, 35 deletions
diff --git a/epan/dissectors/packet-quic.c b/epan/dissectors/packet-quic.c
index b5bc34c..e11ab90 100644
--- a/epan/dissectors/packet-quic.c
+++ b/epan/dissectors/packet-quic.c
@@ -146,7 +146,6 @@ static dissector_handle_t tls_handle;
* to-do list:
* DONE key update via KEY_PHASE bit (untested)
* TODO 0-RTT decryption
- * TODO fix packet coalescing, PKN and decrypted result tracking will overwrite earlier results.
*/
typedef struct quic_decrypt_result {
@@ -209,13 +208,20 @@ typedef struct quic_info_data {
} quic_info_data_t;
/** Per-packet information about QUIC, populated on the first pass. */
-typedef struct quic_packet_info {
- quic_info_data_t *conn;
+struct quic_packet_info {
+ struct quic_packet_info *next;
guint64 packet_number; /**< Reconstructed full packet number. */
quic_decrypt_result_t decryption;
- gboolean from_server : 1;
guint8 pkn_len; /**< Length of PKN (1/2/4) or unknown (0). */
-} quic_packet_info_t;
+};
+typedef struct quic_packet_info quic_packet_info_t;
+
+/** A UDP datagram contains one or more QUIC packets. */
+typedef struct quic_datagram {
+ quic_info_data_t *conn;
+ quic_packet_info_t first_packet;
+ gboolean from_server : 1;
+} quic_datagram;
/**
* Maps CID (quic_cid_t *) to a QUIC Connection (quic_info_data_t *).
@@ -516,6 +522,7 @@ quic_decrypt_packet_number(tvbuff_t *tvb _U_, guint offset _U_, quic_cipher *cip
static guint32
dissect_quic_packet_number(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint offset,
quic_info_data_t *quic_info, quic_packet_info_t *quic_packet,
+ gboolean from_server,
quic_cipher *cipher, int pn_cipher_algo, guint64 *pkn_out)
{
proto_item *ti;
@@ -556,7 +563,7 @@ dissect_quic_packet_number(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
/* Sequential first pass, try to reconstruct full packet number. */
if (!PINFO_FD_VISITED(pinfo)) {
- if (quic_packet->from_server) {
+ if (from_server) {
pkn = quic_pkt_adjust_pkt_num(quic_info->max_server_pkn, pkn, 8 * pkn_len);
quic_info->max_server_pkn = pkn;
} else {
@@ -1625,7 +1632,7 @@ quic_process_payload(tvbuff_t *tvb _U_, packet_info *pinfo, proto_tree *tree _U_
static int
dissect_quic_initial(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, guint offset,
- quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, guint pkn_len)
+ quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, gboolean from_server, guint pkn_len)
{
proto_item *ti;
@@ -1634,7 +1641,7 @@ dissect_quic_initial(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, g
// An Initial Packet should always result in creating a new connection.
DISSECTOR_ASSERT(quic_info);
- quic_cipher *cipher = quic_packet->from_server ? &quic_info->server_handshake_cipher : &quic_info->client_handshake_cipher;
+ quic_cipher *cipher = from_server ? &quic_info->server_handshake_cipher : &quic_info->client_handshake_cipher;
quic_process_payload(tvb, pinfo, quic_tree, ti, offset,
quic_info, quic_packet, cipher, pkn_len);
offset += tvb_reported_length_remaining(tvb, offset);
@@ -1644,7 +1651,7 @@ dissect_quic_initial(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, g
static int
dissect_quic_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, guint offset,
- quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, guint pkn_len)
+ quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, gboolean from_server, guint pkn_len)
{
proto_item *ti;
@@ -1655,7 +1662,7 @@ dissect_quic_handshake(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree,
return tvb_reported_length_remaining(tvb, offset);
}
- quic_cipher *cipher = quic_packet->from_server ? &quic_info->server_handshake_cipher : &quic_info->client_handshake_cipher;
+ quic_cipher *cipher = from_server ? &quic_info->server_handshake_cipher : &quic_info->client_handshake_cipher;
quic_process_payload(tvb, pinfo, quic_tree, ti, offset,
quic_info, quic_packet, cipher, pkn_len);
offset += tvb_reported_length_remaining(tvb, offset);
@@ -1686,11 +1693,10 @@ dissect_quic_retry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree, gui
}
static void
-quic_add_connection_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, quic_packet_info_t *quic_packet)
+quic_add_connection_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, quic_info_data_t *conn)
{
proto_tree *ctree;
proto_item *pi;
- quic_info_data_t *conn = quic_packet->conn;
ctree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_quic_connection_info, NULL, "QUIC Connection information");
if (!conn) {
@@ -1761,7 +1767,7 @@ dissect_quic_long_header_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *q
static int
dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree,
- quic_packet_info_t *quic_packet)
+ quic_datagram *dgram_info, quic_packet_info_t *quic_packet)
{
guint offset = 0;
guint32 long_packet_type;
@@ -1773,7 +1779,8 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
guint64 payload_length;
guint32 pkn_len;
guint64 pkn;
- quic_info_data_t *conn = quic_packet->conn;
+ quic_info_data_t *conn = dgram_info->conn;
+ const gboolean from_server = dgram_info->from_server;
quic_cipher *cipher = NULL;
proto_tree_add_item_ret_uint(quic_tree, hf_quic_long_packet_type, tvb, offset, 1, ENC_NA, &long_packet_type);
@@ -1797,7 +1804,7 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
#ifdef HAVE_LIBGCRYPT_AEAD
/* Build handshake cipher now for PKN (and handshake) decryption. */
- if (!PINFO_FD_VISITED(pinfo) && (long_packet_type == QUIC_LPT_INITIAL && !quic_packet->from_server) &&
+ if (!PINFO_FD_VISITED(pinfo) && (long_packet_type == QUIC_LPT_INITIAL && !from_server) &&
conn && !memcmp(&dcid, &conn->client_dcid_initial, sizeof(quic_cid_t))) {
const gchar *error = NULL;
/* Create new decryption context based on the Client Connection
@@ -1808,11 +1815,11 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
}
}
if (conn) {
- cipher = !quic_packet->from_server ? &conn->client_handshake_cipher : &conn->server_handshake_cipher;
+ cipher = !from_server ? &conn->client_handshake_cipher : &conn->server_handshake_cipher;
}
#endif /* !HAVE_LIBGCRYPT_AEAD */
- pkn_len = dissect_quic_packet_number(tvb, pinfo, quic_tree, offset, conn, quic_packet,
+ pkn_len = dissect_quic_packet_number(tvb, pinfo, quic_tree, offset, conn, quic_packet, from_server,
cipher, GCRY_CIPHER_AES128, &pkn);
if (pkn_len == 0) {
return offset;
@@ -1823,10 +1830,10 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
/* Payload */
switch(long_packet_type) {
case QUIC_LPT_INITIAL: /* Initial */
- offset = dissect_quic_initial(tvb, pinfo, quic_tree, offset, conn, quic_packet, pkn_len);
+ offset = dissect_quic_initial(tvb, pinfo, quic_tree, offset, conn, quic_packet, from_server, pkn_len);
break;
case QUIC_LPT_HANDSHAKE: /* Handshake */
- offset = dissect_quic_handshake(tvb, pinfo, quic_tree, offset, conn, quic_packet, pkn_len);
+ offset = dissect_quic_handshake(tvb, pinfo, quic_tree, offset, conn, quic_packet, from_server, pkn_len);
break;
case QUIC_LPT_RETRY: /* Retry */
offset = dissect_quic_retry(tvb, pinfo, quic_tree, offset, conn, quic_packet, pkn_len);
@@ -1843,7 +1850,7 @@ dissect_quic_long_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tre
static int
dissect_quic_short_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tree,
- quic_packet_info_t *quic_packet)
+ quic_datagram *dgram_info, quic_packet_info_t *quic_packet)
{
guint offset = 0;
quic_cid_t dcid = {.len=0};
@@ -1852,12 +1859,13 @@ dissect_quic_short_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tr
proto_item *ti;
gboolean key_phase = FALSE;
quic_cipher *cipher = NULL;
- quic_info_data_t *conn = quic_packet->conn;
+ quic_info_data_t *conn = dgram_info->conn;
+ const gboolean from_server = dgram_info->from_server;
proto_tree_add_item_ret_boolean(quic_tree, hf_quic_short_kp_flag, tvb, offset, 1, ENC_NA, &key_phase);
proto_tree_add_item(quic_tree, hf_quic_short_packet_type, tvb, offset, 1, ENC_NA);
if (conn) {
- dcid.len = quic_packet->from_server ? conn->client_cids.data.len : conn->server_cids.data.len;
+ dcid.len = from_server ? conn->client_cids.data.len : conn->server_cids.data.len;
}
offset += 1;
@@ -1874,13 +1882,13 @@ dissect_quic_short_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *quic_tr
// KeyUpdate was legitimate and create new cipher. Egg: need cipher to
// decrypt PKN...
pkn = 0;
- cipher = quic_get_pp_cipher(pinfo, key_phase, pkn, conn, quic_packet->from_server);
+ cipher = quic_get_pp_cipher(pinfo, key_phase, pkn, conn, from_server);
}
#endif /* !HAVE_LIBGCRYPT_AEAD */
/* Packet Number */
- pkn_len = dissect_quic_packet_number(tvb, pinfo, quic_tree, offset, conn, quic_packet,
+ pkn_len = dissect_quic_packet_number(tvb, pinfo, quic_tree, offset, conn, quic_packet, from_server,
cipher, conn ? conn->pn_cipher_algo : 0, &pkn);
if (pkn_len == 0) {
return offset;
@@ -2041,16 +2049,17 @@ dissect_quic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree *quic_tree;
guint offset = 0;
guint32 header_form;
+ quic_datagram *dgram_info = NULL;
quic_packet_info_t *quic_packet = NULL;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUIC");
if (PINFO_FD_VISITED(pinfo)) {
- quic_packet = (quic_packet_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_quic, 0);
+ dgram_info = (quic_datagram *)p_get_proto_data(wmem_file_scope(), pinfo, proto_quic, 0);
}
- if (!quic_packet) {
- quic_packet = wmem_new0(wmem_file_scope(), quic_packet_info_t);
- p_add_proto_data(wmem_file_scope(), pinfo, proto_quic, 0, quic_packet);
+ if (!dgram_info) {
+ dgram_info = wmem_new0(wmem_file_scope(), quic_datagram);
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_quic, 0, dgram_info);
}
ti = proto_tree_add_item(tree, proto_quic, tvb, 0, -1, ENC_NA);
@@ -2067,18 +2076,28 @@ dissect_quic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
quic_extract_header(tvb, &long_packet_type, &version, &dcid, &scid);
conn = quic_connection_find(pinfo, long_packet_type, &dcid, &from_server);
quic_connection_create_or_update(&conn, pinfo, long_packet_type, version, &scid, &dcid, from_server);
- quic_packet->conn = conn;
- quic_packet->from_server = from_server;
+ dgram_info->conn = conn;
+ dgram_info->from_server = from_server;
#if 0
- proto_tree_add_debug_text(quic_tree, "Connection: %d %p DCID=%s SCID=%s from_server:%d", pinfo->num, quic_packet->conn, cid_to_string(&dcid), cid_to_string(&scid), quic_packet->from_server);
+ proto_tree_add_debug_text(quic_tree, "Connection: %d %p DCID=%s SCID=%s from_server:%d", pinfo->num, dgram_info->conn, cid_to_string(&dcid), cid_to_string(&scid), dgram_info->from_server);
} else {
- proto_tree_add_debug_text(quic_tree, "Connection: %d %p from_server:%d", pinfo->num, quic_packet->conn, quic_packet->from_server);
+ proto_tree_add_debug_text(quic_tree, "Connection: %d %p from_server:%d", pinfo->num, dgram_info->conn, dgram_info->from_server);
#endif
}
- quic_add_connection_info(tvb, pinfo, quic_tree, quic_packet);
+ quic_add_connection_info(tvb, pinfo, quic_tree, dgram_info->conn);
do {
+ if (!quic_packet) {
+ quic_packet = &dgram_info->first_packet;
+ } else if (!PINFO_FD_VISITED(pinfo)) {
+ quic_packet->next = wmem_new0(wmem_file_scope(), quic_packet_info_t);
+ quic_packet = quic_packet->next;
+ } else {
+ quic_packet = quic_packet->next;
+ DISSECTOR_ASSERT(quic_packet);
+ }
+
tvbuff_t *next_tvb = quic_get_message_tvb(tvb, offset);
proto_tree_add_item_ret_uint(quic_tree, hf_quic_header_form, next_tvb, 0, 1, ENC_NA, &header_form);
if (header_form) {
@@ -2087,9 +2106,9 @@ dissect_quic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
dissect_quic_version_negotiation(next_tvb, pinfo, quic_tree, quic_packet);
break;
}
- dissect_quic_long_header(next_tvb, pinfo, quic_tree, quic_packet);
+ dissect_quic_long_header(next_tvb, pinfo, quic_tree, dgram_info, quic_packet);
} else {
- dissect_quic_short_header(next_tvb, pinfo, quic_tree, quic_packet);
+ dissect_quic_short_header(next_tvb, pinfo, quic_tree, dgram_info, quic_packet);
}
offset += tvb_reported_length(next_tvb);
} while (tvb_reported_length_remaining(tvb, offset));