QUIC: Introduce discarded_packet() event

And include its occurrence into the history as X. The event raising is
configurable with a new const redef QUIC::max_discarded_packet_events.
This commit is contained in:
Arne Welzel 2025-10-08 17:25:41 +02:00
parent 9345a8c84e
commit 586b7b94cb
15 changed files with 96 additions and 8 deletions

5
NEWS
View file

@ -64,6 +64,11 @@ New Functionality
- The DNS analyzer now returns the set of parameters for SVCB data. It previously handled - The DNS analyzer now returns the set of parameters for SVCB data. It previously handled
SVCB packets, but omitted the parameters while parsing. SVCB packets, but omitted the parameters while parsing.
- The QUIC analyzer now raises QUIC::discarded_packet() when a packet with fixed_bit
set to 0 is encountered. Such an occurrence is included in the QUIC history as ``X``.
This functionality can be controlled with ``QUIC::max_discarded_packet_events``,
setting this variable to -1 disabled the ``QUIC::discarded_packet`` event.
Changed Functionality Changed Functionality
--------------------- ---------------------

View file

@ -59,6 +59,7 @@ export {
## C CONNECTION_CLOSE packet ## C CONNECTION_CLOSE packet
## S SSL Client/Server Hello ## S SSL Client/Server Hello
## U Unfamiliar QUIC version ## U Unfamiliar QUIC version
## X Discarded packet after successful decryption of INITIAL packets.
## ====== ==================================================== ## ====== ====================================================
history: string &log &default=""; history: string &log &default="";
@ -77,6 +78,10 @@ export {
## The maximum length of the history field. ## The maximum length of the history field.
option max_history_length = 100; option max_history_length = 100;
## Maximum number of QUIC::discarded packet() events to generate.
## Set to 0 for unlimited, -1 for disabled.
const max_discarded_packet_events: int = 100 &redef;
} }
redef record connection += { redef record connection += {
@ -164,6 +169,18 @@ event QUIC::retry_packet(c: connection, is_orig: bool, version: count, dcid: str
delete c$quic; delete c$quic;
} }
event QUIC::discarded_packet(c: connection, is_orig: bool, total_decrypted: count)
{
if ( ! c?$quic )
{
# This should not happen.
Reporter::conn_weird("QUIC_spurious_discarded_packet", c);
return;
}
add_to_history(c, is_orig, "Xdiscarded");
}
# If we couldn't handle a version, log it as a single record. # If we couldn't handle a version, log it as a single record.
event QUIC::unhandled_version(c: connection, is_orig: bool, version: count, dcid: string, scid: string) event QUIC::unhandled_version(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
{ {

View file

@ -94,3 +94,15 @@ global QUIC::connection_close_frame: event(c: connection, is_orig: bool, version
## ##
## scid: The Source Connection ID field. ## scid: The Source Connection ID field.
global QUIC::unhandled_version: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string); global QUIC::unhandled_version: event(c: connection, is_orig: bool, version: count, dcid: string, scid: string);
## Generated when a QUIC packet with fixed_bit 0 is encountered.
##
## This event is only generated if some INITIAL QUIC packets were successfully
## decrypted previously.
##
## c: The connection.
##
## is_orig: True if the packet is from the the connection's originator.
##
## total_decrypted: The number of QUIC packets successfully decrypted previously.
global QUIC::discarded_packet: event(c: connection, is_orig: bool, total_decrypted: count);

View file

@ -22,3 +22,5 @@ on QUIC::ConnectionClosePayload -> event QUIC::connection_close_frame($conn, $is
self.error_code.result_, self.reason_phrase); self.error_code.result_, self.reason_phrase);
on QUIC::UnhandledVersion -> event QUIC::unhandled_version($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id); on QUIC::UnhandledVersion -> event QUIC::unhandled_version($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id);
on QUIC::Packet if ( self.raise_discarded_packet ) -> event QUIC::discarded_packet($conn, $is_orig, self.total_decrypted);

View file

@ -6,6 +6,8 @@ module QUIC;
import spicy; import spicy;
import zeek; import zeek;
global max_discarded_packet_events: optional<int64>;
# The interface to the C++ code that handles the decryption of the INITIAL packet payload using well-known keys # The interface to the C++ code that handles the decryption of the INITIAL packet payload using well-known keys
public function decrypt_crypto_payload(version: uint32, data: bytes, connection_id: bytes, encrypted_offset: uint64, payload_offset: uint64, from_client: bool): bytes &cxxname="QUIC_decrypt_crypto_payload"; public function decrypt_crypto_payload(version: uint32, data: bytes, connection_id: bytes, encrypted_offset: uint64, payload_offset: uint64, from_client: bool): bytes &cxxname="QUIC_decrypt_crypto_payload";
@ -125,6 +127,9 @@ type Context = struct {
server_sink: sink&; server_sink: sink&;
ssl_handle: zeek::ProtocolHandle &optional; ssl_handle: zeek::ProtocolHandle &optional;
total_decrypted: uint64;
total_discarded: uint64;
}; };
############## ##############
@ -483,6 +488,8 @@ type Packet = unit(from_client: bool, context: Context&) {
var crypto: CryptoSinkUnit&; var crypto: CryptoSinkUnit&;
var crypto_sink: sink&; var crypto_sink: sink&;
var raise_discarded_packet: bool = False;
var total_decrypted: uint64;
# Attach an SSL analyzer to this connection once. # Attach an SSL analyzer to this connection once.
on %init { on %init {
@ -606,6 +613,9 @@ type Packet = unit(from_client: bool, context: Context&) {
# We were able to decrypt the INITIAL packet. Confirm QUIC! # We were able to decrypt the INITIAL packet. Confirm QUIC!
spicy::accept_input(); spicy::accept_input();
# Keep track of the packets decrypted, too.
++context.total_decrypted;
} }
# If this packet has a SHORT header, consume until &eod, there's nothing # If this packet has a SHORT header, consume until &eod, there's nothing
@ -618,9 +628,20 @@ type Packet = unit(from_client: bool, context: Context&) {
} }
False -> { False -> {
# Consume the packet if fixed_bit is not 1. Basically discard it. # Consume the packet if fixed_bit is not 1. Basically discard it.
# : skip bytes &eod {
# # TODO: Raise QUIC::discarded_packet() when this happens. ++context.total_discarded;
: skip bytes &eod;
# Initialize the global if it hasn't been initialized yet.
if (!max_discarded_packet_events)
max_discarded_packet_events = zeek::as_int(zeek::get_value("QUIC::max_discarded_packet_events"));
# If enabled and we haven't raised too many events yet, set
# a flag such that in .evt QUIC::discarded_packet() is raised.
if (cast<int64>(context.total_decrypted) > 0 && (*max_discarded_packet_events == 0 || cast<int64>(context.total_discarded) <= *max_discarded_packet_events)) {
self.raise_discarded_packet = True;
self.total_decrypted = context.total_decrypted;
}
}
} }
}; };
}; };

View file

@ -7,5 +7,5 @@
#open XXXX-XX-XX-XX-XX-XX #open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid client_scid server_scid server_name client_protocol history #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid client_scid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string string #types time string addr port addr port string string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 765b:b77b:dad1:7e73:56f9:5a0f:52a3:f4f 39264 725f:1f71:525f:a3b:525f:3673:525f:3673 443 1 ceef7990f5bb4071 1e6dc7 eeef7990f5bb4071 tr6.snapchat.com h3 IIISZiiiiishIH XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 765b:b77b:dad1:7e73:56f9:5a0f:52a3:f4f 39264 725f:1f71:525f:a3b:525f:3673:525f:3673 443 1 ceef7990f5bb4071 1e6dc7 eeef7990f5bb4071 tr6.snapchat.com h3 IXIXISZXiiiiishIH
#close XXXX-XX-XX-XX-XX-XX #close XXXX-XX-XX-XX-XX-XX

View file

@ -7,5 +7,5 @@
#open XXXX-XX-XX-XX-XX-XX #open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid client_scid server_scid server_name client_protocol history #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid client_scid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string string #types time string addr port addr port string string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 172.17.0.2 34347 64.233.166.94 443 1 815d62c70884f4b51e8ccadd5beed372 e5ec6b26584229be98a164349ae910351c40d10b c15d62c70884f4b5 www.google.de h3 ISishIHhHhhH XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 172.17.0.2 34347 64.233.166.94 443 1 815d62c70884f4b51e8ccadd5beed372 e5ec6b26584229be98a164349ae910351c40d10b c15d62c70884f4b5 www.google.de h3 ISXishIHXhHhhH
#close XXXX-XX-XX-XX-XX-XX #close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 IIISZiiiiishIH

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 IXIXISZXiiiiishIH

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 IIISZiiiiishIH

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 IXIISZiiiiishIH

View file

@ -7,5 +7,5 @@
#open XXXX-XX-XX-XX-XX-XX #open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid client_scid server_scid server_name client_protocol history #fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version client_initial_dcid client_scid server_scid server_name client_protocol history
#types time string addr port addr port string string string string string string string #types time string addr port addr port string string string string string string string
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 82.239.54.117 44174 250.58.23.113 443 1 c5a5015ae8f479784a 34696c 01275b138ee6aca8a6276b132ae6b3547cf7773f blog.cloudflare.com h3 ISiishIhhhHHhHH XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 82.239.54.117 44174 250.58.23.113 443 1 c5a5015ae8f479784a 34696c 01275b138ee6aca8a6276b132ae6b3547cf7773f blog.cloudflare.com h3 ISXixisxhIXhhhHHhHH
#close XXXX-XX-XX-XX-XX-XX #close XXXX-XX-XX-XX-XX-XX

View file

@ -1,3 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
ts uid history ts uid history
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 ISi XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 ISX

View file

@ -0,0 +1,19 @@
# @TEST-DOC: Test the QUIC::max_discarded_packet_events setting and its.
# @TEST-REQUIRES: ${SCRIPTS}/have-spicy
# @TEST-EXEC: zeek -r $TRACES/quic/quic-39264-rand.pcap base/protocols/quic QUIC::max_discarded_packet_events=-1
# @TEST-EXEC: test ! -f analyzer.log || cat analyzer.log >&2
# @TEST-EXEC: zeek-cut -m ts uid history service_name < quic.log > quic.log.no-discarded-packets
# @TEST-EXEC: zeek -r $TRACES/quic/quic-39264-rand.pcap base/protocols/quic QUIC::max_discarded_packet_events=1
# @TEST-EXEC: test ! -f analyzer.log || cat analyzer.log >&2
# @TEST-EXEC: zeek-cut -m ts uid history service_name < quic.log > quic.log.one-discarded-packet
# @TEST-EXEC: zeek -r $TRACES/quic/quic-39264-rand.pcap base/protocols/quic
# @TEST-EXEC: test ! -f analyzer.log || cat analyzer.log >&2
# @TEST-EXEC: zeek-cut -m ts uid history service_name < quic.log > quic.log.default-discarded-packets
# @TEST-EXEC: btest-diff quic.log.no-discarded-packets
# @TEST-EXEC: btest-diff quic.log.one-discarded-packet
# @TEST-EXEC: btest-diff quic.log.default-discarded-packets

View file

@ -1 +1 @@
31094f4840d0abc8fdf7f810e281851bd057931b 0aecb0467324a7db7758f54a677e61b4b0532b60