mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
quic: analyzer: Support QUIC v2
QUIC v2 changed the version *and* the packet type enumeration to prevent protocol ossification. Use an intermediary unit to handle the difference.
This commit is contained in:
parent
0b6f4ef443
commit
dabe85ebbf
3 changed files with 61 additions and 22 deletions
|
@ -3,5 +3,6 @@ module QUIC;
|
||||||
export {
|
export {
|
||||||
const version_strings: table[count] of string = {
|
const version_strings: table[count] of string = {
|
||||||
[0x00000001] = "1",
|
[0x00000001] = "1",
|
||||||
|
[0x6b3343cf] = "quicv2",
|
||||||
} &default=function(version: count): string { return fmt("unknown-%x", version); };
|
} &default=function(version: count): string { return fmt("unknown-%x", version); };
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,6 @@ protocol analyzer QUIC over UDP:
|
||||||
|
|
||||||
import QUIC;
|
import QUIC;
|
||||||
|
|
||||||
# Make the enum available.
|
|
||||||
export QUIC::LongPacketType;
|
|
||||||
|
|
||||||
on QUIC::InitialPacket -> event QUIC::initial_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id);
|
on QUIC::InitialPacket -> event QUIC::initial_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id);
|
||||||
|
|
||||||
on QUIC::RetryPacket -> event QUIC::retry_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id, self.retry_token, self.integrity_tag);
|
on QUIC::RetryPacket -> event QUIC::retry_packet($conn, $is_orig, self.header.version, self.header.dest_conn_id, self.header.src_conn_id, self.retry_token, self.integrity_tag);
|
||||||
|
|
|
@ -24,11 +24,10 @@ public function decrypt_crypto_payload(
|
||||||
# Can we decrypt?
|
# Can we decrypt?
|
||||||
function can_decrypt(long_header: LongHeaderPacket, context: ConnectionIDInfo, is_client: bool): bool {
|
function can_decrypt(long_header: LongHeaderPacket, context: ConnectionIDInfo, is_client: bool): bool {
|
||||||
|
|
||||||
if ( long_header.first_byte.packet_type != LongPacketType::INITIAL )
|
if ( ! long_header.is_initial )
|
||||||
return False;
|
return False;
|
||||||
|
|
||||||
# decrypt_crypto_payload() has known secrets for version 1, nothing else.
|
if ( long_header.version != Version1 && long_header.version != Version2 )
|
||||||
if ( long_header.version != 0x00000001 )
|
|
||||||
return False;
|
return False;
|
||||||
|
|
||||||
if ( is_client )
|
if ( is_client )
|
||||||
|
@ -81,14 +80,26 @@ type ConnectionIDInfo = struct {
|
||||||
##############
|
##############
|
||||||
# Definitions
|
# Definitions
|
||||||
##############
|
##############
|
||||||
|
const Version1: uint32 = 0x00000001;
|
||||||
|
const Version2: uint32 = 0x6b3343cf;
|
||||||
|
|
||||||
type LongPacketType = enum {
|
type LongPacketTypeV1 = enum {
|
||||||
INITIAL = 0,
|
INITIAL = 0,
|
||||||
ZERO_RTT = 1,
|
ZERO_RTT = 1,
|
||||||
HANDSHAKE = 2,
|
HANDSHAKE = 2,
|
||||||
RETRY = 3,
|
RETRY = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# V2 changed packet types to avoid ossification.
|
||||||
|
#
|
||||||
|
# https://www.rfc-editor.org/rfc/rfc9369.html#name-long-header-packet-types
|
||||||
|
type LongPacketTypeV2 = enum {
|
||||||
|
INITIAL = 1,
|
||||||
|
ZERO_RTT = 2,
|
||||||
|
HANDSHAKE = 3,
|
||||||
|
RETRY = 0,
|
||||||
|
};
|
||||||
|
|
||||||
type HeaderForm = enum {
|
type HeaderForm = enum {
|
||||||
SHORT = 0,
|
SHORT = 0,
|
||||||
LONG = 1,
|
LONG = 1,
|
||||||
|
@ -155,17 +166,56 @@ type VariableLengthInteger = unit {
|
||||||
# Long packets
|
# Long packets
|
||||||
# Generic units
|
# Generic units
|
||||||
##############
|
##############
|
||||||
|
public type LongHeaderPacketV1 = unit(inout outer: LongHeaderPacket) {
|
||||||
|
switch ( LongPacketTypeV1(outer.first_byte.packet_type) ) {
|
||||||
|
LongPacketTypeV1::INITIAL -> initial_hdr : InitialPacket(outer) {
|
||||||
|
outer.is_initial = True;
|
||||||
|
outer.encrypted_offset = outer.offset() +
|
||||||
|
self.initial_hdr.length.bytes_to_parse +
|
||||||
|
self.initial_hdr.token_length.bytes_to_parse +
|
||||||
|
self.initial_hdr.token_length.result;
|
||||||
|
outer.payload_length = self.initial_hdr.length.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LongPacketTypeV1::ZERO_RTT -> zerortt_hdr : ZeroRTTPacket(outer);
|
||||||
|
LongPacketTypeV1::HANDSHAKE -> handshake_hdr : HandshakePacket(outer);
|
||||||
|
LongPacketTypeV1::RETRY -> retry_hdr : RetryPacket(outer) {
|
||||||
|
outer.is_retry = True;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public type LongHeaderPacketV2 = unit(inout outer: LongHeaderPacket) {
|
||||||
|
switch ( LongPacketTypeV2(outer.first_byte.packet_type) ) {
|
||||||
|
LongPacketTypeV2::INITIAL -> initial_hdr : InitialPacket(outer) {
|
||||||
|
outer.is_initial = True;
|
||||||
|
outer.encrypted_offset = outer.offset() +
|
||||||
|
self.initial_hdr.length.bytes_to_parse +
|
||||||
|
self.initial_hdr.token_length.bytes_to_parse +
|
||||||
|
self.initial_hdr.token_length.result;
|
||||||
|
outer.payload_length = self.initial_hdr.length.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LongPacketTypeV2::ZERO_RTT -> zerortt_hdr : ZeroRTTPacket(outer);
|
||||||
|
LongPacketTypeV2::HANDSHAKE -> handshake_hdr : HandshakePacket(outer);
|
||||||
|
LongPacketTypeV2::RETRY -> retry_hdr : RetryPacket(outer) {
|
||||||
|
outer.is_retry = True;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
public type LongHeaderPacket = unit {
|
public type LongHeaderPacket = unit {
|
||||||
var encrypted_offset: uint64;
|
var encrypted_offset: uint64;
|
||||||
var payload_length: uint64;
|
var payload_length: uint64;
|
||||||
var client_conn_id_length: uint8;
|
var client_conn_id_length: uint8;
|
||||||
var server_conn_id_length: uint8;
|
var server_conn_id_length: uint8;
|
||||||
|
var is_initial: bool;
|
||||||
|
var is_retry: bool;
|
||||||
|
|
||||||
first_byte: bitfield(8) {
|
first_byte: bitfield(8) {
|
||||||
header_form: 7 &convert=cast<HeaderForm>(cast<uint8>($$));
|
header_form: 7 &convert=cast<HeaderForm>(cast<uint8>($$));
|
||||||
fixed_bit: 6;
|
fixed_bit: 6;
|
||||||
packet_type: 4..5 &convert=cast<LongPacketType>(cast<uint8>($$));
|
packet_type: 4..5;
|
||||||
type_specific_bits: 0..3 &convert=cast<uint8>($$);
|
type_specific_bits: 0..3 &convert=cast<uint8>($$);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -175,18 +225,9 @@ public type LongHeaderPacket = unit {
|
||||||
src_conn_id_len: uint8 { self.client_conn_id_length = $$; }
|
src_conn_id_len: uint8 { self.client_conn_id_length = $$; }
|
||||||
src_conn_id: bytes &size=self.client_conn_id_length;
|
src_conn_id: bytes &size=self.client_conn_id_length;
|
||||||
|
|
||||||
switch ( self.first_byte.packet_type ) {
|
switch ( self.version ) {
|
||||||
LongPacketType::INITIAL -> initial_hdr : InitialPacket(self) {
|
Version1 -> v1: LongHeaderPacketV1(self);
|
||||||
self.encrypted_offset = self.offset() +
|
Version2 -> v2: LongHeaderPacketV2(self);
|
||||||
self.initial_hdr.length.bytes_to_parse +
|
|
||||||
self.initial_hdr.token_length.bytes_to_parse +
|
|
||||||
self.initial_hdr.token_length.result;
|
|
||||||
self.payload_length = self.initial_hdr.length.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
LongPacketType::ZERO_RTT -> zerortt_hdr : ZeroRTTPacket(self);
|
|
||||||
LongPacketType::HANDSHAKE -> handshake_hdr : HandshakePacket(self);
|
|
||||||
LongPacketType::RETRY -> retry_hdr : RetryPacket(self);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -402,7 +443,7 @@ type Packet = unit(from_client: bool, context: ConnectionIDInfo&) {
|
||||||
|
|
||||||
# If we see a retry packet from the responder, reset the decryption
|
# If we see a retry packet from the responder, reset the decryption
|
||||||
# context such that the next DCID from the client is used for decryption.
|
# context such that the next DCID from the client is used for decryption.
|
||||||
if ( self.long_header.first_byte.packet_type == LongPacketType::RETRY ) {
|
if ( self.long_header.is_retry ) {
|
||||||
context.client_initial_processed = False;
|
context.client_initial_processed = False;
|
||||||
context.server_initial_processed = False;
|
context.server_initial_processed = False;
|
||||||
context.initial_destination_conn_id = b"";
|
context.initial_destination_conn_id = b"";
|
||||||
|
@ -470,7 +511,7 @@ type Packet = unit(from_client: bool, context: ConnectionIDInfo&) {
|
||||||
# are restablished and decryption is no longer possible
|
# are restablished and decryption is no longer possible
|
||||||
#
|
#
|
||||||
# TODO: verify if this is actually correct per RFC
|
# TODO: verify if this is actually correct per RFC
|
||||||
if ( self.long_header.first_byte.packet_type != LongPacketType::RETRY && ! from_client ) {
|
if ( ! self.long_header.is_retry && ! from_client ) {
|
||||||
context.server_initial_processed = True;
|
context.server_initial_processed = True;
|
||||||
context.client_initial_processed = True;
|
context.client_initial_processed = True;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue