diff options
-rw-r--r-- | src/lib/protocols/quic.c | 53 | ||||
-rw-r--r-- | tests/cfgs/default/pcap/gquic_only_from_server.pcap | bin | 0 -> 40244 bytes | |||
-rw-r--r-- | tests/cfgs/default/result/gquic_only_from_server.pcap.out | 28 | ||||
-rw-r--r-- | tests/cfgs/default/result/radius_false_positive.pcapng.out | 16 |
4 files changed, 78 insertions, 19 deletions
diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c index 183d2e524..0b8674f38 100644 --- a/src/lib/protocols/quic.c +++ b/src/lib/protocols/quic.c @@ -1491,11 +1491,31 @@ void process_chlo(struct ndpi_detection_module_struct *ndpi_struct, } } +static int may_be_gquic_rej(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) +{ + struct ndpi_packet_struct *packet = &ndpi_struct->packet; + void *ptr; + + /* Common case: msg from server default port */ + if(packet->udp->source != ntohs(443)) + return 0; + /* GQUIC. Common case: cid length 8, no version, packet number length 1 */ + if(packet->payload[0] != 0x08) + return 0; + if(packet->payload_packet_len < 1 + 8 + 1 + 12 /* Message auth hash */ + 16 /* Arbitrary length */) + return 0; + /* Search for "REJ" tag in the first 16 bytes after the hash */ + ptr = memchr(&packet->payload[1 + 8 + 1 + 12], 'R', 16 - 3); + if(ptr && memcmp(ptr, "REJ", 3) == 0) + return 1; + return 0; +} + static int may_be_0rtt(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow) + struct ndpi_flow_struct *flow, uint32_t *version) { 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; @@ -1512,23 +1532,23 @@ static int may_be_0rtt(struct ndpi_detection_module_struct *ndpi_struct, pub_bit3 = ((first_byte & 0x20) != 0); pub_bit4 = ((first_byte & 0x10) != 0); - version = ntohl(*((u_int32_t *)&packet->payload[1])); + *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) && + 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) && + 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); + NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not 0-RTT Packet\n", *version); return 0; } - if(is_version_quic_v2(version) && + 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); + NDPI_LOG_DBG2(ndpi_struct, "Version 0x%x not 0-RTT Packet\n", *version); return 0; } @@ -1546,7 +1566,7 @@ static int may_be_0rtt(struct ndpi_detection_module_struct *ndpi_struct, 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); + *version, dest_conn_id_len, source_conn_id_len); return 0; } @@ -1847,16 +1867,18 @@ 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 try only 1 easy case: + * CHLO/CH is VERY hard. Let try only 2 easy cases: * * out-of-order 0-RTT, i.e 0-RTT packets received before the Initial; * in that case, keep looking for the Initial + * * with only GQUIC packets from server (usefull with unidirectional + * captures) look for Rejection packet * Avoid the generic cases and let's see if anyone complains... */ 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); + ret = may_be_0rtt(ndpi_struct, flow, &version); if(ret == 1) { NDPI_LOG_DBG(ndpi_struct, "Found 0-RTT, keep looking for Initial\n"); flow->l4.udp.quic_0rtt_found = 1; @@ -1864,14 +1886,23 @@ static void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, /* 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); + flow->protos.tls_quic.quic_version = version; } 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); + flow->protos.tls_quic.quic_version = 0; /* unknown */ return; } + ret = may_be_gquic_rej(ndpi_struct, flow); + if(ret == 1) { + NDPI_LOG_INFO(ndpi_struct, "GQUIC REJ\n"); + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); + flow->protos.tls_quic.quic_version = 0; /* unknown */ + return; + } } NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; diff --git a/tests/cfgs/default/pcap/gquic_only_from_server.pcap b/tests/cfgs/default/pcap/gquic_only_from_server.pcap Binary files differnew file mode 100644 index 000000000..a0c521b03 --- /dev/null +++ b/tests/cfgs/default/pcap/gquic_only_from_server.pcap diff --git a/tests/cfgs/default/result/gquic_only_from_server.pcap.out b/tests/cfgs/default/result/gquic_only_from_server.pcap.out new file mode 100644 index 000000000..7a0f0605c --- /dev/null +++ b/tests/cfgs/default/result/gquic_only_from_server.pcap.out @@ -0,0 +1,28 @@ +Guessed flow protos: 0 + +DPI Packets (UDP): 1 (1.00 pkts/flow) +Confidence DPI : 1 (flows) +Num dissector calls: 1 (1.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) +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) +LRU cache stun_zoom: 0/0/0 (insert/search/found) +Automa host: 0/0 (search/found) +Automa domain: 0/0 (search/found) +Automa tls cert: 0/0 (search/found) +Automa risk mask: 0/0 (search/found) +Automa common alpns: 0/0 (search/found) +Patricia risk mask: 2/0 (search/found) +Patricia risk mask IPv6: 0/0 (search/found) +Patricia risk: 0/0 (search/found) +Patricia risk IPv6: 0/0 (search/found) +Patricia protocols: 2/0 (search/found) +Patricia protocols IPv6: 0/0 (search/found) + +QUIC 30 39740 1 + + 1 UDP 213.202.7.26:443 -> 10.189.122.71:60524 [VLAN: 1508][proto: 188/QUIC][IP: 0/Unknown][Encrypted][Confidence: DPI][DPI packets: 1][cat: Web/5][30 pkts/39740 bytes -> 0 pkts/0 bytes][Goodput ratio: 97/0][0.09 sec][bytes ratio: 1.000 (Upload)][IAT c2s/s2c min/avg/max/stddev: 0/0 3/0 59/0 11/0][Pkt Len c2s/s2c min/avg/max/stddev: 69/0 1325/0 1396/0 275/0][Risk: ** Unidirectional Traffic **][Risk Score: 10][Risk Info: No client to server traffic][PLAIN TEXT (AESGCC20)][Plen Bins: 3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,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,93,0,0,0,0,0] diff --git a/tests/cfgs/default/result/radius_false_positive.pcapng.out b/tests/cfgs/default/result/radius_false_positive.pcapng.out index 7d0aa0535..9fc1b1750 100644 --- a/tests/cfgs/default/result/radius_false_positive.pcapng.out +++ b/tests/cfgs/default/result/radius_false_positive.pcapng.out @@ -1,16 +1,16 @@ -Guessed flow protos: 1 +Guessed flow protos: 0 -DPI Packets (UDP): 10 (10.00 pkts/flow) -Confidence Match by port : 1 (flows) -Num dissector calls: 205 (205.00 diss/flow) +DPI Packets (UDP): 1 (1.00 pkts/flow) +Confidence DPI : 1 (flows) +Num dissector calls: 1 (1.00 diss/flow) LRU cache ookla: 0/0/0 (insert/search/found) -LRU cache bittorrent: 0/3/0 (insert/search/found) +LRU cache bittorrent: 0/0/0 (insert/search/found) LRU cache zoom: 0/0/0 (insert/search/found) LRU cache stun: 0/0/0 (insert/search/found) LRU cache tls_cert: 0/0/0 (insert/search/found) -LRU cache mining: 0/1/0 (insert/search/found) +LRU cache mining: 0/0/0 (insert/search/found) LRU cache msteams: 0/0/0 (insert/search/found) -LRU cache stun_zoom: 0/1/0 (insert/search/found) +LRU cache stun_zoom: 0/0/0 (insert/search/found) Automa host: 0/0 (search/found) Automa domain: 0/0 (search/found) Automa tls cert: 0/0 (search/found) @@ -25,4 +25,4 @@ Patricia protocols IPv6: 2/0 (search/found) QUIC 10 7479 1 - 1 UDP [2bc6:b5ac:cb3b:676b::18]:443 -> [3dba:3762:c186:e122:89b0:5170:a86c:ecff]:53129 [proto: 188/QUIC][IP: 0/Unknown][Encrypted][Confidence: Match by port][DPI packets: 10][cat: Web/5][10 pkts/7479 bytes -> 0 pkts/0 bytes][Goodput ratio: 92/0][0.34 sec][bytes ratio: 1.000 (Upload)][IAT c2s/s2c min/avg/max/stddev: 0/0 38/0 290/0 90/0][Pkt Len c2s/s2c min/avg/max/stddev: 82/0 748/0 1292/0 549/0][Risk: ** Unidirectional Traffic **][Risk Score: 10][Risk Info: No client to server traffic][PLAIN TEXT (AESGCC20at)][Plen Bins: 20,0,0,0,0,0,20,10,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,50,0,0,0,0,0,0,0,0,0] + 1 UDP [2bc6:b5ac:cb3b:676b::18]:443 -> [3dba:3762:c186:e122:89b0:5170:a86c:ecff]:53129 [proto: 188/QUIC][IP: 0/Unknown][Encrypted][Confidence: DPI][DPI packets: 1][cat: Web/5][10 pkts/7479 bytes -> 0 pkts/0 bytes][Goodput ratio: 92/0][0.34 sec][bytes ratio: 1.000 (Upload)][IAT c2s/s2c min/avg/max/stddev: 0/0 38/0 290/0 90/0][Pkt Len c2s/s2c min/avg/max/stddev: 82/0 748/0 1292/0 549/0][Risk: ** Unidirectional Traffic **][Risk Score: 10][Risk Info: No client to server traffic][PLAIN TEXT (AESGCC20at)][Plen Bins: 20,0,0,0,0,0,20,10,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,50,0,0,0,0,0,0,0,0,0] |