aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--src/include/ndpi_typedefs.h3
-rw-r--r--src/lib/protocols/quic.c91
-rw-r--r--tests/pcap/quic_0RTT.pcapbin2644 -> 8468 bytes
-rw-r--r--tests/result/quic_0RTT.pcap.out21
4 files changed, 101 insertions, 14 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;
}
diff --git a/tests/pcap/quic_0RTT.pcap b/tests/pcap/quic_0RTT.pcap
index 7ade88654..95e7c2b6c 100644
--- a/tests/pcap/quic_0RTT.pcap
+++ b/tests/pcap/quic_0RTT.pcap
Binary files differ
diff --git a/tests/result/quic_0RTT.pcap.out b/tests/result/quic_0RTT.pcap.out
index 102ca515e..9496dd97f 100644
--- a/tests/result/quic_0RTT.pcap.out
+++ b/tests/result/quic_0RTT.pcap.out
@@ -1,8 +1,8 @@
Guessed flow protos: 0
-DPI Packets (UDP): 1 (1.00 pkts/flow)
-Confidence DPI : 1 (flows)
-Num dissector calls: 64 (64.00 diss/flow)
+DPI Packets (UDP): 4 (2.00 pkts/flow)
+Confidence DPI : 2 (flows)
+Num dissector calls: 186 (93.00 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/0/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
@@ -10,20 +10,23 @@ LRU cache stun: 0/0/0 (insert/search/found)
LRU cache tls_cert: 0/0/0 (insert/search/found)
LRU cache mining: 0/0/0 (insert/search/found)
LRU cache msteams: 0/0/0 (insert/search/found)
-Automa host: 2/0 (search/found)
-Automa domain: 1/0 (search/found)
+Automa host: 3/1 (search/found)
+Automa domain: 2/0 (search/found)
Automa tls cert: 0/0 (search/found)
Automa risk mask: 1/0 (search/found)
Automa common alpns: 0/0 (search/found)
-Patricia risk mask: 0/0 (search/found)
+Patricia risk mask: 2/0 (search/found)
Patricia risk: 0/0 (search/found)
-Patricia protocols: 0/0 (search/found)
+Patricia protocols: 4/3 (search/found)
+Google 15 5178 1
QUIC 2 2588 1
JA3 Host Stats:
IP Address # JA3C
- 1 ::1 1
+ 1 192.168.2.100 1
+ 2 ::1 1
- 1 UDP [::1]:60459 <-> [::1]:4443 [proto: 188/QUIC][Encrypted][Confidence: DPI][cat: Web/5][1 pkts/1294 bytes <-> 1 pkts/1294 bytes][Goodput ratio: 95/95][0.00 sec][Hostname/SNI: abcd][ALPN: h3-32][TLS Supported Versions: TLSv1.3;TLSv1.3 (draft);TLSv1.3 (draft);TLSv1.3 (draft)][Risk: ** Known Proto on Non Std Port **][Risk Score: 50][Risk Info: No server to client traffic][TLSv1.3][JA3C: a7b629a5bd67bfc25e2c78b3daa4c12f][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0]
+ 1 UDP 192.168.2.100:51972 <-> 142.250.181.227:443 [proto: 188.126/QUIC.Google][Encrypted][Confidence: DPI][cat: Web/5][7 pkts/2168 bytes <-> 8 pkts/3010 bytes][Goodput ratio: 86/89][0.23 sec][Hostname/SNI: ssl.gstatic.com][ALPN: h3][TLS Supported Versions: TLSv1.3][bytes ratio: -0.163 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 36/10 121/30 45/14][Pkt Len c2s/s2c min/avg/max/stddev: 75/67 310/376 1292/1292 416/426][TLSv1.3][JA3C: 06b6b2a2cba0b7deeaaa6a3d8374d627][Plen Bins: 26,20,20,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0]
+ 2 UDP [::1]:60459 <-> [::1]:4443 [proto: 188/QUIC][Encrypted][Confidence: DPI][cat: Web/5][1 pkts/1294 bytes <-> 1 pkts/1294 bytes][Goodput ratio: 95/95][0.00 sec][Hostname/SNI: abcd][ALPN: h3-32][TLS Supported Versions: TLSv1.3;TLSv1.3 (draft);TLSv1.3 (draft);TLSv1.3 (draft)][Risk: ** Known Proto on Non Std Port **][Risk Score: 50][Risk Info: No server to client traffic][TLSv1.3][JA3C: a7b629a5bd67bfc25e2c78b3daa4c12f][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0]