aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNardi Ivan <nardi.ivan@gmail.com>2022-08-10 18:25:44 +0200
committerToni <matzeton@googlemail.com>2022-08-24 15:38:30 +0200
commit8bfb1712d8b69c1faf2d9e23e741659c06f4a7df (patch)
treee7cc5e1bd04a9c5ce2b3ea164bc2bb4cca780967 /src
parent0c8bc9f0555fa19d56bb686a2233772ae408f77b (diff)
QUIC: add support for 0-RTT packets received before the Initial
RFC9001 4.6.1: "A client that wishes to send 0-RTT packets uses the early_data extension in the ClientHello message of a subsequent handshake; see Section 4.2.10 of [TLS13]. It then sends application data in 0-RTT packets." That means the client sends before the CH (in the Initial) and then the 0-RTT (in the same UDP datagram or not)". However, because of packet loss or out-of-order delivery, it might happens that a 0-RTT packet is received before the Initial (the original one or a retransmission). For example, Google and Facebook servers save 0-RTT packets for a small amount of time in hopes of receiving the corresponding Initial. Update the QUIC dissector to detect 0-RTT packets and keep looking for the Initial. Issue found by @utoni in #1706; the trace example has been taken from that PR.
Diffstat (limited to 'src')
-rw-r--r--src/include/ndpi_typedefs.h3
-rw-r--r--src/lib/protocols/quic.c91
2 files changed, 89 insertions, 5 deletions
diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h
index 377ed0e2e..94c22a667 100644
--- a/src/include/ndpi_typedefs.h
+++ b/src/include/ndpi_typedefs.h
@@ -787,6 +787,9 @@ struct ndpi_flow_udp_struct {
/* NDPI_PROTOCOL_XBOX */
u_int32_t xbox_stage:1;
+ /* NDPI_PROTOCOL_QUIC */
+ u_int32_t quic_0rtt_found:1;
+
/* NDPI_PROTOCOL_SKYPE */
u_int8_t skype_crc[4];
diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c
index 530ba27b2..79fd5a823 100644
--- a/src/lib/protocols/quic.c
+++ b/src/lib/protocols/quic.c
@@ -1433,6 +1433,67 @@ static void process_chlo(struct ndpi_detection_module_struct *ndpi_struct,
}
}
+static int may_be_0rtt(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow)
+{
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+ uint32_t version;
+ u_int8_t first_byte;
+ u_int8_t pub_bit1, pub_bit2, pub_bit3, pub_bit4;
+ u_int8_t dest_conn_id_len, source_conn_id_len;
+
+ /* First byte + version + dest_conn_id_len */
+ if(packet->payload_packet_len < 5 + 1) {
+ NDPI_LOG_DBG2(ndpi_struct, "Pkt too short\n");
+ return 0;
+ }
+
+ first_byte = packet->payload[0];
+ pub_bit1 = ((first_byte & 0x80) != 0);
+ pub_bit2 = ((first_byte & 0x40) != 0);
+ pub_bit3 = ((first_byte & 0x20) != 0);
+ pub_bit4 = ((first_byte & 0x10) != 0);
+
+ version = ntohl(*((u_int32_t *)&packet->payload[1]));
+
+ /* IETF versions, Long header, fixed bit (ignore QUIC-bit-greased case), 0RTT */
+
+ if(!(is_version_quic(version) &&
+ pub_bit1 && pub_bit2)) {
+ NDPI_LOG_DBG2(ndpi_struct, "Invalid header or version\n");
+ return 0;
+ }
+ if(!is_version_quic_v2(version) &&
+ (pub_bit3 != 0 || pub_bit4 != 1)) {
+ NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not 0-RTT Packet\n", version);
+ return 0;
+ }
+ if(is_version_quic_v2(version) &&
+ (pub_bit3 != 1 || pub_bit4 != 0)) {
+ NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not 0-RTT Packet\n", version);
+ return 0;
+ }
+
+ /* Check that CIDs lengths are valid */
+ dest_conn_id_len = packet->payload[5];
+ if(packet->payload_packet_len < 5 + 1 + dest_conn_id_len) {
+ NDPI_LOG_DBG2(ndpi_struct, "Dcid too short\n");
+ return 0;
+ }
+ source_conn_id_len = packet->payload[5 + 1 + dest_conn_id_len];
+ if(packet->payload_packet_len < 5 + 1 + dest_conn_id_len + 1 + source_conn_id_len) {
+ NDPI_LOG_DBG2(ndpi_struct, "Scid too short\n");
+ return 0;
+ }
+ if(dest_conn_id_len > QUIC_MAX_CID_LENGTH ||
+ source_conn_id_len > QUIC_MAX_CID_LENGTH) {
+ NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x invalid CIDs length %u %u\n",
+ version, dest_conn_id_len, source_conn_id_len);
+ return 0;
+ }
+
+ return 1;
+}
static int may_be_initial_pkt(struct ndpi_detection_module_struct *ndpi_struct,
uint32_t *version)
@@ -1623,7 +1684,7 @@ static void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct,
uint32_t clear_payload_len = 0;
const u_int8_t *crypto_data;
uint64_t crypto_data_len;
- int is_quic;
+ int is_initial_quic, ret;
NDPI_LOG_DBG2(ndpi_struct, "search QUIC\n");
@@ -1635,12 +1696,32 @@ static void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct,
* CHLO/ClientHello message and we need (only) it to sub-classify
* the flow.
* Detecting QUIC sessions where the first captured packet is not a
- * CHLO/CH is VERY hard. Let's try avoiding it and let's see if
- * anyone complains...
+ * CHLO/CH is VERY hard. Let try only 1 easy case:
+ * * out-of-order 0-RTT, i.e 0-RTT packets received before the Initial;
+ * in that case, keep looking for the Initial
+ * Avoid the generic cases and let's see if anyone complains...
*/
- is_quic = may_be_initial_pkt(ndpi_struct, &version);
- if(!is_quic) {
+ is_initial_quic = may_be_initial_pkt(ndpi_struct, &version);
+ if(!is_initial_quic) {
+ if(!is_ch_reassembler_pending(flow)) { /* Better safe than sorry */
+ ret = may_be_0rtt(ndpi_struct, flow);
+ if(ret == 1) {
+ NDPI_LOG_DBG(ndpi_struct, "Found 0-RTT, keep looking for Initial\n");
+ flow->l4.udp.quic_0rtt_found = 1;
+ if(flow->packet_counter >= 3) {
+ /* We haven't still found an Initial.. give up */
+ NDPI_LOG_INFO(ndpi_struct, "QUIC 0RTT\n");
+ ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+ }
+ return;
+ } else if(flow->l4.udp.quic_0rtt_found == 1) {
+ /* Unknown packet (probably an Handshake one) after a 0-RTT */
+ NDPI_LOG_INFO(ndpi_struct, "QUIC 0RTT (without Initial)\n");
+ ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+ return;
+ }
+ }
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}