diff options
author | Nardi Ivan <nardi.ivan@gmail.com> | 2022-08-10 18:25:44 +0200 |
---|---|---|
committer | Toni <matzeton@googlemail.com> | 2022-08-24 15:38:30 +0200 |
commit | 8bfb1712d8b69c1faf2d9e23e741659c06f4a7df (patch) | |
tree | e7cc5e1bd04a9c5ce2b3ea164bc2bb4cca780967 /src | |
parent | 0c8bc9f0555fa19d56bb686a2233772ae408f77b (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.h | 3 | ||||
-rw-r--r-- | src/lib/protocols/quic.c | 91 |
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; } |