Merge remote-tracking branch 'origin/topic/awelzel/4847-quic-discard-fixed-bit-is-zero'
Some checks are pending
pre-commit / pre-commit (push) Waiting to run

* origin/topic/awelzel/4847-quic-discard-fixed-bit-is-zero:
  QUIC: Skip packets with fixed_bit 0
This commit is contained in:
Arne Welzel 2025-10-09 09:16:33 +02:00
commit 9345a8c84e
9 changed files with 138 additions and 74 deletions

View file

@ -1,3 +1,10 @@
8.1.0-dev.642 | 2025-10-09 09:16:33 +0200
* GH-4847: QUIC: Skip packets with fixed_bit 0 (Arne Welzel, Corelight)
The RFC specifies that QUIC packets with unset fixed_bit need to be
discarded. Do so.
8.1.0-dev.639 | 2025-10-08 09:21:05 +0200
* logging/writers: Disable heartbeats (Arne Welzel, Corelight)

3
NEWS
View file

@ -73,6 +73,9 @@ Changed Functionality
- The ``get_current_packet_header()`` now populates the returned record also for
fragmented IP datagrams.
- The QUIC parser discards packets with the fixed_bit field set to 0, rather than
continuing to parse potentially running into analyzer violations.
Removed Functionality
---------------------

View file

@ -1 +1 @@
8.1.0-dev.639
8.1.0-dev.642

View file

@ -516,6 +516,16 @@ type Packet = unit(from_client: bool, context: Context&) {
# Peek into the first byte and determine the header type.
first_byte: bitfield(8) {
header_form: 7 &convert=HeaderForm($$);
# The next bit (0x40) of byte 0 is set to 1. Packets containing a zero
# value for this bit are not valid packets in this version and MUST be
# discarded. A value of 1 for this bit allows QUIC to coexist with other
# protocols; see [RFC7983].
#
# https://datatracker.ietf.org/doc/html/rfc9000#name-short-header-packets
#
# We use this to avoid parsing the short/long header below.
fixed_bit: 6;
};
# TODO: Consider bitfield based look-ahead-parsing in the switch below
@ -525,6 +535,8 @@ type Packet = unit(from_client: bool, context: Context&) {
}
# Depending on the header, parse it and update the src/dest ConnectionID's
switch (cast<bool>(self.first_byte.fixed_bit)) {
True -> {
switch (self.first_byte.header_form) {
HeaderForm::SHORT -> short_header: ShortHeader(context.client_cid_len);
HeaderForm::LONG -> long_header: LongHeaderPacket {
@ -597,12 +609,20 @@ type Packet = unit(from_client: bool, context: Context&) {
}
# If this packet has a SHORT header, consume until &eod, there's nothing
# we can do with it anyhow.
# we can do with it anyhow, but only if fixed_bit == 1.
: ShortPacketPayload if(self.first_byte.header_form == HeaderForm::SHORT);
# If this was packet with a long header and decrypted data exists, attempt
# to parse the plain QUIC frames from it.
frames: Frame(self.long_header, from_client, self.crypto, self.crypto_sink)[] &parse-from=self.decrypted_data if(self.first_byte.header_form == HeaderForm::LONG && |self.decrypted_data| > 0);
}
False -> {
# Consume the packet if fixed_bit is not 1. Basically discard it.
#
# # TODO: Raise QUIC::discarded_packet() when this happens.
: skip bytes &eod;
}
};
};
##############

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 service
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 Dd quic,ssl

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path quic
#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
#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
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssl
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p version cipher curve server_name resumed last_alert next_protocol established ssl_history cert_chain_fps client_cert_chain_fps sni_matches_cert
#types time string addr port addr port string string string string bool string string bool string vector[string] vector[string] bool
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 765b:b77b:dad1:7e73:56f9:5a0f:52a3:f4f 39264 725f:1f71:525f:a3b:525f:3673:525f:3673 443 TLSv13 TLS_AES_128_GCM_SHA256 x25519 tr6.snapchat.com T - - F Cs - - -
#close XXXX-XX-XX-XX-XX-XX

Binary file not shown.

View file

@ -0,0 +1,9 @@
# @TEST-DOC: Regression test for #4847, QUIC packets with fixed_bit 0 are discarded.
# @TEST-REQUIRES: ${SCRIPTS}/have-spicy
# @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 < conn.log > conn.log.cut
# @TEST-EXEC: btest-diff conn.log.cut
# @TEST-EXEC: btest-diff ssl.log
# @TEST-EXEC: btest-diff quic.log