mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 22:58:20 +00:00

Seem reasonable give we log the server SCID. Interestingly, the Chromium examples actually have zero length (empty) source connection IDs. I wonder if that's part of their "protocol ossification avoidance" effort.
239 lines
6.5 KiB
Text
239 lines
6.5 KiB
Text
##! Initial idea for a quic.log.
|
|
|
|
@load base/frameworks/notice/weird
|
|
@load base/protocols/conn/removal-hooks
|
|
|
|
@load ./consts
|
|
|
|
module QUIC;
|
|
|
|
export {
|
|
redef enum Log::ID += { LOG };
|
|
|
|
type Info: record {
|
|
## Timestamp of first QUIC packet for this entry.
|
|
ts: time &log;
|
|
## Unique ID for the connection.
|
|
uid: string &log;
|
|
## The connection's 4-tuple of endpoint addresses/ports.
|
|
id: conn_id &log;
|
|
|
|
## QUIC version as found in the first INITIAL packet from
|
|
## the client.
|
|
version: string &log;
|
|
|
|
## First Destination Connection ID used by client. This is
|
|
## random and unpredictable, but used for packet protection
|
|
## by client and server.
|
|
client_initial_dcid: string &log &optional;
|
|
|
|
## Client's Source Connection ID from the first INITIAL packet.
|
|
client_scid: string &log &optional;
|
|
|
|
## Server chosen Connection ID usually from server's first
|
|
## INITIAL packet. This is to be used by the client in
|
|
## subsequent packets.
|
|
server_scid: string &log &optional;
|
|
|
|
## Server name extracted from SNI extension in ClientHello
|
|
## packet if available.
|
|
server_name: string &log &optional;
|
|
|
|
## First protocol extracted from ALPN extension in ClientHello
|
|
## packet if available.
|
|
client_protocol: string &log &optional;
|
|
|
|
## Experimental QUIC history.
|
|
##
|
|
## Letters have the following meaning with client-sent
|
|
## letters being capitalized:
|
|
##
|
|
## ====== ====================================================
|
|
## Letter Meaning
|
|
## ====== ====================================================
|
|
## I INIT packet
|
|
## H HANDSHAKE packet
|
|
## Z 0RTT packet
|
|
## R RETRY packet
|
|
## C CONNECTION_CLOSE packet
|
|
## S SSL Client/Server Hello
|
|
## ====== ====================================================
|
|
history: string &log &default="";
|
|
|
|
# Internal state for the history field.
|
|
history_state: vector of string;
|
|
|
|
# Internal state if this record has already been logged.
|
|
logged: bool &default=F;
|
|
};
|
|
|
|
global log_quic: event(rec: Info);
|
|
|
|
global log_policy: Log::PolicyHook;
|
|
|
|
global finalize_quic: Conn::RemovalHook;
|
|
|
|
## The maximum length of the history field.
|
|
option max_history_length = 100;
|
|
}
|
|
|
|
redef record connection += {
|
|
# XXX: We may have multiple QUIC connections with different
|
|
# Connection ID over the same UDP connection.
|
|
quic: Info &optional;
|
|
};
|
|
|
|
# Faster to modify here than re-compiling .evt files.
|
|
const quic_ports = {
|
|
443/udp, # HTTP3-over-QUIC
|
|
853/udp, # DNS-over-QUIC
|
|
784/udp, # DNS-over-QUIC early
|
|
};
|
|
|
|
function add_to_history(c: connection, is_orig: bool, what: string)
|
|
{
|
|
if ( |c$quic$history_state| == max_history_length )
|
|
return;
|
|
|
|
c$quic$history_state += is_orig ? to_upper(what[0]) : to_lower(what[0]);
|
|
|
|
if ( |c$quic$history_state| == max_history_length )
|
|
Reporter::conn_weird("QUIC_max_history_length_reached", c);
|
|
}
|
|
|
|
function log_record(quic: Info)
|
|
{
|
|
quic$history = join_string_vec(quic$history_state, "");
|
|
Log::write(LOG, quic);
|
|
quic$logged = T;
|
|
}
|
|
|
|
function set_session(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
|
{
|
|
if ( ! c?$quic )
|
|
{
|
|
c$quic = Info(
|
|
$ts=network_time(),
|
|
$uid=c$uid,
|
|
$id=c$id,
|
|
$version=version_strings[version],
|
|
);
|
|
|
|
Conn::register_removal_hook(c, finalize_quic);
|
|
}
|
|
|
|
if ( is_orig && |dcid| > 0 && ! c$quic?$client_initial_dcid )
|
|
c$quic$client_initial_dcid = bytestring_to_hexstr(dcid);
|
|
|
|
if ( is_orig )
|
|
c$quic$client_scid = bytestring_to_hexstr(scid);
|
|
else
|
|
c$quic$server_scid = bytestring_to_hexstr(scid);
|
|
}
|
|
|
|
event QUIC::initial_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
|
{
|
|
set_session(c, is_orig, version, dcid, scid);
|
|
add_to_history(c, is_orig, "INIT");
|
|
}
|
|
|
|
event QUIC::handshake_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
|
{
|
|
set_session(c, is_orig, version, dcid, scid);
|
|
add_to_history(c, is_orig, "HANDSHAKE");
|
|
}
|
|
|
|
event QUIC::zero_rtt_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string)
|
|
{
|
|
set_session(c, is_orig, version, dcid, scid);
|
|
add_to_history(c, is_orig, "ZeroRTT");
|
|
}
|
|
|
|
# RETRY packets trigger a log entry and state reset.
|
|
event QUIC::retry_packet(c: connection, is_orig: bool, version: count, dcid: string, scid: string, retry_token: string, integrity_tag: string)
|
|
{
|
|
if ( ! c?$quic )
|
|
set_session(c, is_orig, version, dcid, scid);
|
|
|
|
add_to_history(c, is_orig, "RETRY");
|
|
|
|
log_record(c$quic);
|
|
|
|
delete c$quic;
|
|
}
|
|
|
|
# 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)
|
|
{
|
|
if ( ! c?$quic )
|
|
set_session(c, is_orig, version, dcid, scid);
|
|
|
|
add_to_history(c, is_orig, "UNHANDLED_VERSION");
|
|
|
|
log_record(c$quic);
|
|
|
|
delete c$quic;
|
|
}
|
|
|
|
# Upon a connection_close_frame(), if any c$quic state is pending to be logged, do so
|
|
# now and prepare for a new entry.
|
|
event QUIC::connection_close_frame(c: connection, is_orig: bool, version: count, dcid: string, scid: string, error_code: count, reason_phrase: string)
|
|
{
|
|
if ( ! c?$quic )
|
|
return;
|
|
|
|
add_to_history(c, is_orig, "CONNECTION_CLOSE");
|
|
|
|
log_record(c$quic);
|
|
|
|
delete c$quic;
|
|
}
|
|
|
|
event ssl_extension_server_name(c: connection, is_client: bool, names: string_vec) &priority=5
|
|
{
|
|
if ( is_client && c?$quic && |names| > 0 )
|
|
c$quic$server_name = names[0];
|
|
}
|
|
|
|
event ssl_extension_application_layer_protocol_negotiation(c: connection, is_client: bool, protocols: string_vec)
|
|
{
|
|
if ( c?$quic && is_client )
|
|
{
|
|
c$quic$client_protocol = protocols[0];
|
|
if ( |protocols| > 1 )
|
|
# Probably not overly weird, but the quic.log only
|
|
# works with the first one in the hope to avoid
|
|
# vector or concatenation.
|
|
Reporter::conn_weird("QUIC_many_protocols", c, cat(protocols));
|
|
}
|
|
}
|
|
|
|
event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec)
|
|
{
|
|
if ( ! c?$quic )
|
|
return;
|
|
|
|
add_to_history(c, T, "SSL");
|
|
}
|
|
|
|
event ssl_server_hello(c: connection, version: count, record_version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) &priority=-5
|
|
{
|
|
if ( ! c?$quic )
|
|
return;
|
|
|
|
add_to_history(c, F, "SSL");
|
|
}
|
|
|
|
hook finalize_quic(c: connection)
|
|
{
|
|
if ( ! c?$quic || c$quic$logged )
|
|
return;
|
|
|
|
log_record(c$quic);
|
|
}
|
|
|
|
event zeek_init()
|
|
{
|
|
Log::create_stream(LOG, [$columns=Info, $ev=log_quic, $path="quic", $policy=log_policy]);
|
|
Analyzer::register_for_ports(Analyzer::ANALYZER_QUIC, quic_ports);
|
|
}
|