%%% -*- erlang -*- %%% %%% QUIC protocol constants and records %%% RFC 9000 - QUIC: A UDP-Based Multiplexed and Secure Transport %%% -ifndef(QUIC_HRL). -define(QUIC_HRL, true). %%==================================================================== %% QUIC Version %%==================================================================== -define(QUIC_VERSION_1, 16#00000001). -define(QUIC_VERSION_2, 16#6b3343cf). %%==================================================================== %% Packet Types (Long Header) %%==================================================================== -define(PACKET_TYPE_INITIAL, 16#00). -define(PACKET_TYPE_0RTT, 16#01). -define(PACKET_TYPE_HANDSHAKE, 16#02). -define(PACKET_TYPE_RETRY, 16#03). %%==================================================================== %% Frame Types (RFC 9000 Section 12.4) %%==================================================================== -define(FRAME_PADDING, 16#00). -define(FRAME_PING, 16#01). -define(FRAME_ACK, 16#02). -define(FRAME_ACK_ECN, 16#03). -define(FRAME_RESET_STREAM, 16#04). -define(FRAME_STOP_SENDING, 16#05). -define(FRAME_CRYPTO, 16#06). -define(FRAME_NEW_TOKEN, 16#07). % 0x08-0x0f depending on flags -define(FRAME_STREAM, 16#08). -define(FRAME_MAX_DATA, 16#10). -define(FRAME_MAX_STREAM_DATA, 16#11). -define(FRAME_MAX_STREAMS_BIDI, 16#12). -define(FRAME_MAX_STREAMS_UNI, 16#13). -define(FRAME_DATA_BLOCKED, 16#14). -define(FRAME_STREAM_DATA_BLOCKED, 16#15). -define(FRAME_STREAMS_BLOCKED_BIDI, 16#16). -define(FRAME_STREAMS_BLOCKED_UNI, 16#17). -define(FRAME_NEW_CONNECTION_ID, 16#18). -define(FRAME_RETIRE_CONNECTION_ID, 16#19). -define(FRAME_PATH_CHALLENGE, 16#1a). -define(FRAME_PATH_RESPONSE, 16#1b). -define(FRAME_CONNECTION_CLOSE, 16#1c). -define(FRAME_CONNECTION_CLOSE_APP, 16#1d). -define(FRAME_HANDSHAKE_DONE, 16#1e). %% DATAGRAM Frames (RFC 9221) -define(FRAME_DATAGRAM, 16#30). -define(FRAME_DATAGRAM_WITH_LEN, 16#31). %% RESET_STREAM_AT Frame (draft-ietf-quic-reliable-stream-reset-07) -define(FRAME_RESET_STREAM_AT, 16#24). %%==================================================================== %% Stream Frame Flags (bits 0-2 of frame type 0x08-0x0f) %%==================================================================== % Offset field present -define(STREAM_FLAG_OFF, 16#04). % Length field present -define(STREAM_FLAG_LEN, 16#02). % Final frame for stream -define(STREAM_FLAG_FIN, 16#01). %%==================================================================== %% Transport Error Codes (RFC 9000 Section 20.1) %%==================================================================== -define(QUIC_NO_ERROR, 16#00). -define(QUIC_INTERNAL_ERROR, 16#01). -define(QUIC_CONNECTION_REFUSED, 16#02). -define(QUIC_FLOW_CONTROL_ERROR, 16#03). -define(QUIC_STREAM_LIMIT_ERROR, 16#04). -define(QUIC_STREAM_STATE_ERROR, 16#05). -define(QUIC_FINAL_SIZE_ERROR, 16#06). -define(QUIC_FRAME_ENCODING_ERROR, 16#07). -define(QUIC_TRANSPORT_PARAMETER_ERROR, 16#08). -define(QUIC_CONNECTION_ID_LIMIT_ERROR, 16#09). -define(QUIC_PROTOCOL_VIOLATION, 16#0a). -define(QUIC_INVALID_TOKEN, 16#0b). -define(QUIC_APPLICATION_ERROR, 16#0c). -define(QUIC_CRYPTO_BUFFER_EXCEEDED, 16#0d). -define(QUIC_KEY_UPDATE_ERROR, 16#0e). -define(QUIC_AEAD_LIMIT_REACHED, 16#0f). -define(QUIC_NO_VIABLE_PATH, 16#10). % 0x100-0x1ff for TLS alerts -define(QUIC_CRYPTO_ERROR_BASE, 16#100). %% Application-level error codes (for stream deadlines) %% Default error code when stream deadline expires -define(QUIC_STREAM_DEADLINE_EXCEEDED, 16#FF). %%==================================================================== %% HTTP/3 Error Codes (RFC 9114 Section 8.1) %%==================================================================== -define(H3_NO_ERROR, 16#100). -define(H3_GENERAL_PROTOCOL_ERROR, 16#101). -define(H3_INTERNAL_ERROR, 16#102). -define(H3_STREAM_CREATION_ERROR, 16#103). -define(H3_CLOSED_CRITICAL_STREAM, 16#104). -define(H3_FRAME_UNEXPECTED, 16#105). -define(H3_FRAME_ERROR, 16#106). -define(H3_EXCESSIVE_LOAD, 16#107). -define(H3_ID_ERROR, 16#108). -define(H3_SETTINGS_ERROR, 16#109). -define(H3_MISSING_SETTINGS, 16#10a). -define(H3_REQUEST_REJECTED, 16#10b). -define(H3_REQUEST_CANCELLED, 16#10c). -define(H3_REQUEST_INCOMPLETE, 16#10d). -define(H3_MESSAGE_ERROR, 16#10e). -define(H3_CONNECT_ERROR, 16#10f). -define(H3_VERSION_FALLBACK, 16#110). %%==================================================================== %% Transport Parameters (RFC 9000 Section 18.2) %%==================================================================== -define(TP_ORIGINAL_DCID, 16#00). -define(TP_MAX_IDLE_TIMEOUT, 16#01). -define(TP_STATELESS_RESET_TOKEN, 16#02). -define(TP_MAX_UDP_PAYLOAD_SIZE, 16#03). -define(TP_INITIAL_MAX_DATA, 16#04). -define(TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, 16#05). -define(TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, 16#06). -define(TP_INITIAL_MAX_STREAM_DATA_UNI, 16#07). -define(TP_INITIAL_MAX_STREAMS_BIDI, 16#08). -define(TP_INITIAL_MAX_STREAMS_UNI, 16#09). -define(TP_ACK_DELAY_EXPONENT, 16#0a). -define(TP_MAX_ACK_DELAY, 16#0b). -define(TP_DISABLE_ACTIVE_MIGRATION, 16#0c). -define(TP_PREFERRED_ADDRESS, 16#0d). -define(TP_ACTIVE_CONNECTION_ID_LIMIT, 16#0e). -define(TP_INITIAL_SCID, 16#0f). -define(TP_RETRY_SCID, 16#10). %% RFC 9221 - QUIC Datagrams -define(TP_MAX_DATAGRAM_FRAME_SIZE, 16#20). %% draft-ietf-quic-reliable-stream-reset-07 - Reliable RESET_STREAM -define(TP_RESET_STREAM_AT, 16#17f7586d2cb571). %%==================================================================== %% Crypto Constants %%==================================================================== %% Initial salt for QUIC v1 (RFC 9001 Section 5.2) %% 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a -define(QUIC_V1_INITIAL_SALT, <<16#38, 16#76, 16#2c, 16#f7, 16#f5, 16#59, 16#34, 16#b3, 16#4d, 16#17, 16#9a, 16#e6, 16#a4, 16#c8, 16#0c, 16#ad, 16#cc, 16#bb, 16#7f, 16#0a>> ). %% Initial salt for QUIC v2 (RFC 9369 Section 5.2) -define(QUIC_V2_INITIAL_SALT, <<16#0d, 16#be, 16#91, 16#3e, 16#26, 16#56, 16#d1, 16#93, 16#83, 16#14, 16#86, 16#ac, 16#d1, 16#64, 16#9b, 16#f5, 16#77, 16#95, 16#c0, 16#80>> ). %% HKDF labels -define(QUIC_LABEL_CLIENT_IN, <<"client in">>). -define(QUIC_LABEL_SERVER_IN, <<"server in">>). -define(QUIC_LABEL_QUIC_KEY, <<"quic key">>). -define(QUIC_LABEL_QUIC_IV, <<"quic iv">>). -define(QUIC_LABEL_QUIC_HP, <<"quic hp">>). -define(QUIC_LABEL_QUIC_KU, <<"quic ku">>). %%==================================================================== %% TLS 1.3 Message Types (RFC 8446 Section 4) %%==================================================================== -define(TLS_CLIENT_HELLO, 1). -define(TLS_SERVER_HELLO, 2). -define(TLS_NEW_SESSION_TICKET, 4). -define(TLS_END_OF_EARLY_DATA, 5). -define(TLS_ENCRYPTED_EXTENSIONS, 8). -define(TLS_CERTIFICATE, 11). -define(TLS_CERTIFICATE_REQUEST, 13). -define(TLS_CERTIFICATE_VERIFY, 15). -define(TLS_FINISHED, 20). -define(TLS_KEY_UPDATE, 24). -define(TLS_MESSAGE_HASH, 254). %%==================================================================== %% TLS 1.3 Extension Types (RFC 8446 Section 4.2) %%==================================================================== -define(EXT_SERVER_NAME, 0). -define(EXT_SUPPORTED_GROUPS, 10). -define(EXT_SIGNATURE_ALGORITHMS, 13). -define(EXT_ALPN, 16). % RFC 8446 Section 4.2.11 -define(EXT_PRE_SHARED_KEY, 41). % RFC 8446 Section 4.2.10 -define(EXT_EARLY_DATA, 42). -define(EXT_SUPPORTED_VERSIONS, 43). -define(EXT_PSK_KEY_EXCHANGE_MODES, 45). -define(EXT_KEY_SHARE, 51). -define(EXT_QUIC_TRANSPORT_PARAMS, 57). %%==================================================================== %% TLS 1.3 Named Groups (RFC 8446 Section 4.2.7) %%==================================================================== -define(GROUP_SECP256R1, 16#0017). -define(GROUP_SECP384R1, 16#0018). -define(GROUP_SECP521R1, 16#0019). -define(GROUP_X25519, 16#001d). -define(GROUP_X448, 16#001e). %%==================================================================== %% TLS 1.3 Signature Algorithms (RFC 8446 Section 4.2.3) %%==================================================================== -define(SIG_RSA_PKCS1_SHA256, 16#0401). -define(SIG_RSA_PKCS1_SHA384, 16#0501). -define(SIG_RSA_PKCS1_SHA512, 16#0601). -define(SIG_ECDSA_SECP256R1_SHA256, 16#0403). -define(SIG_ECDSA_SECP384R1_SHA384, 16#0503). -define(SIG_ECDSA_SECP521R1_SHA512, 16#0603). -define(SIG_RSA_PSS_RSAE_SHA256, 16#0804). -define(SIG_RSA_PSS_RSAE_SHA384, 16#0805). -define(SIG_RSA_PSS_RSAE_SHA512, 16#0806). -define(SIG_ED25519, 16#0807). -define(SIG_ED448, 16#0808). %%==================================================================== %% TLS 1.3 Cipher Suites (RFC 8446 Section B.4) %%==================================================================== -define(TLS_AES_128_GCM_SHA256, 16#1301). -define(TLS_AES_256_GCM_SHA384, 16#1302). -define(TLS_CHACHA20_POLY1305_SHA256, 16#1303). %%==================================================================== %% TLS Versions %%==================================================================== -define(TLS_VERSION_1_2, 16#0303). -define(TLS_VERSION_1_3, 16#0304). %%==================================================================== %% Default Values %%==================================================================== -define(DEFAULT_MAX_UDP_PAYLOAD_SIZE, 1200). % 30 seconds -define(DEFAULT_MAX_IDLE_TIMEOUT, 30000). -define(DEFAULT_MAX_STREAMS_BIDI, 100). -define(DEFAULT_MAX_STREAMS_UNI, 100). % 512KB - stream-level flow control (quic-go default for stream) -define(DEFAULT_INITIAL_MAX_STREAM_DATA, 524288). % 768KB - connection = 1.5x stream window (512KB * 1.5) -define(DEFAULT_INITIAL_MAX_DATA, 786432). -define(DEFAULT_ACK_DELAY_EXPONENT, 3). % 25ms -define(DEFAULT_MAX_ACK_DELAY, 25). %% Auto-tuning constants % Connection window should be >= 1.5x largest stream window -define(CONNECTION_FLOW_CONTROL_MULTIPLIER, 1.5). % 8MB cap on receive window -define(DEFAULT_MAX_RECEIVE_WINDOW, 8388608). % Double window if consumed in < 4*RTT (aggressive), else linear growth -define(AUTO_TUNE_RTT_FACTOR, 4). %% Congestion Control - Initial Window %% RFC 9002 default: min(10 * max_datagram_size, max(14720, 2 * max_datagram_size)) %% = ~14720 bytes for 1200 byte datagrams (~12 packets) %% For distribution/bulk transfer workloads, a higher initial window reduces %% slow start duration and improves throughput for large messages. % 64KB - recommended for distribution/LAN -define(INITIAL_WINDOW_DISTRIBUTION, 65536). % 16KB - conservative floor to avoid starvation in bursty virtual networks -define(MINIMUM_WINDOW_DISTRIBUTION, 16384). % 128KB - aggressive for high-bandwidth LAN %% Recovery Duration - Distribution-specific %% Longer minimum recovery duration for distribution to handle packet reordering %% in virtual networks (Docker bridge) without rapid recovery re-entry. -define(MIN_RECOVERY_DURATION_DISTRIBUTION, 200). %% Flow Control - Distribution-specific limits %% Higher limits for distribution to avoid flow control blocking during %% large message transfers (e.g., code loading, large term passing). % 16MB connection-level limit -define(DIST_INITIAL_MAX_DATA, 16777216). % 4MB per-stream limit -define(DIST_INITIAL_MAX_STREAM_DATA, 4194304). %% MTU - Distribution-specific %% Use known-safe MTU for LAN instead of PMTU probing. %% 1452 = 1500 (Ethernet) - 40 (IPv6 header) - 8 (UDP header) %% This is safe for both IPv4 and IPv6 over standard Ethernet. -define(DIST_MAX_UDP_PAYLOAD_SIZE, 1452). -define(INITIAL_WINDOW_AGGRESSIVE, 131072). %% UDP Socket Buffer Sizes %% 7MB buffers improve throughput by 40%+ (matches quic-go, quiche, lsquic) %% OS may cap to lower value (Linux: check net.core.rmem_max, macOS: typically 2-4MB) %% Note: Erlang uses "recbuf" (not "recvbuf") and "sndbuf" -define(DEFAULT_UDP_RECBUF, 7340032). -define(DEFAULT_UDP_SNDBUF, 7340032). %% UDP Packet Batching (GSO/GRO on Linux) %% Maximum packets to batch before auto-flush -define(DEFAULT_MAX_BATCH_PACKETS, 64). %% Default GSO segment size (QUIC packet size for batching) -define(DEFAULT_GSO_SEGMENT_SIZE, 1200). %%==================================================================== %% Records %%==================================================================== %% Crypto keys for an encryption level -record(crypto_keys, { key :: binary(), iv :: binary(), hp :: binary(), cipher :: aes_128_gcm | aes_256_gcm | chacha20_poly1305 }). %% Key Update State (RFC 9001 Section 6) %% Tracks the key phase and keys for 1-RTT packet encryption. %% Maintains both current and previous keys for decryption during key update. -record(key_update_state, { %% Current key phase (0 or 1), toggles on each key update current_phase = 0 :: 0 | 1, %% Current keys for sending and receiving current_keys :: {#crypto_keys{}, #crypto_keys{}} | undefined, %% Previous keys for decryption (kept during key update transition) %% Set to undefined when no key update is in progress prev_keys :: {#crypto_keys{}, #crypto_keys{}} | undefined, %% Application traffic secrets (needed for deriving next keys) client_app_secret :: binary() | undefined, server_app_secret :: binary() | undefined, %% Key update state machine %% idle: normal operation, no key update in progress %% initiated: we sent a packet with new key phase, awaiting response %% responding: we received a packet with new key phase, transitioning update_state = idle :: idle | initiated | responding }). %% Path State for Connection Migration (RFC 9000 Section 9) %% Tracks the validation state and metrics for a network path. -record(path_state, { %% Remote address for this path remote_addr :: {inet:ip_address(), inet:port_number()}, %% Path validation status %% unknown: path not yet validated %% validating: PATH_CHALLENGE sent, waiting for PATH_RESPONSE %% validated: PATH_RESPONSE received successfully %% failed: validation failed (timeout or mismatch) status = unknown :: unknown | validating | validated | failed, %% PATH_CHALLENGE data (8 bytes) for validation challenge_data :: binary() | undefined, %% Number of PATH_CHALLENGE attempts challenge_count = 0 :: non_neg_integer(), %% Anti-amplification: bytes sent/received on this path bytes_sent = 0 :: non_neg_integer(), bytes_received = 0 :: non_neg_integer(), %% RTT estimation for this path rtt :: non_neg_integer() | undefined, %% CID used on this path (RFC 9000 Section 9.5) dcid :: binary() | undefined, %% NAT rebinding vs active migration (RFC 9000 Section 9.3) %% NAT rebinding: only port changed, same IP %% Active migration: different IP address is_nat_rebinding = false :: boolean() }). %% Preferred Address for Server Migration (RFC 9000 Section 9.6) %% Server advertises this to clients for connection migration. -record(preferred_address, { %% IPv4 address (optional) ipv4_addr :: inet:ip4_address() | undefined, ipv4_port :: inet:port_number() | undefined, %% IPv6 address (optional) ipv6_addr :: inet:ip6_address() | undefined, ipv6_port :: inet:port_number() | undefined, %% Connection ID for the preferred address cid :: binary(), %% Stateless reset token (16 bytes) stateless_reset_token :: binary() }). %% Session Ticket for 0-RTT (RFC 9001 Section 4.6) %% Stores session ticket information for resumption. -record(session_ticket, { %% Server name (SNI) this ticket is valid for server_name :: binary(), %% Ticket data (opaque to client) ticket :: binary(), %% Ticket lifetime in seconds lifetime :: non_neg_integer(), %% Ticket age add (for obfuscation) age_add :: non_neg_integer(), %% Ticket nonce (for PSK derivation) nonce :: binary(), %% Resumption master secret (for deriving PSK) resumption_secret :: binary(), %% Max early data size (0 = no early data) max_early_data :: non_neg_integer(), %% When this ticket was received received_at :: non_neg_integer(), %% Cipher suite used for the original connection cipher :: atom(), %% ALPN used for the original connection alpn :: binary() | undefined }). %% Connection ID Entry for CID Pool (RFC 9000 Section 5.1) %% Manages multiple connection IDs for connection migration. -record(cid_entry, { %% Sequence number assigned by the peer seq_num :: non_neg_integer(), %% The connection ID cid :: binary(), %% Stateless reset token (16 bytes, optional for seq 0) stateless_reset_token :: binary() | undefined, %% Status: active (can be used), retired (no longer valid) status = active :: active | retired }). %% Stream state -record(stream_state, { id :: non_neg_integer(), state :: idle | open | half_closed_local | half_closed_remote | closed | reset | stopped, %% Send state send_offset :: non_neg_integer(), send_max_data :: non_neg_integer(), send_fin :: boolean(), send_buffer :: iolist(), %% Receive state recv_offset :: non_neg_integer(), recv_max_data :: non_neg_integer(), recv_fin :: boolean(), %% #{Offset => Data} for out-of-order reassembly recv_buffer :: map(), %% Final size (set when FIN received) final_size :: non_neg_integer() | undefined, %% Stream Priority (RFC 9218) %% Urgency: 0-7 (lower = more urgent, default 3) %% Incremental: boolean (data can be processed incrementally) urgency = 3 :: 0..7, incremental = false :: boolean(), %% Stream Deadlines %% deadline: absolute timestamp in ms (erlang:system_time(millisecond)) deadline :: non_neg_integer() | infinity | undefined, %% deadline_timer: erlang timer reference for deadline expiry deadline_timer :: reference() | undefined, %% deadline_action: what to do when deadline expires %% - notify: send {quic, ConnRef, {stream_deadline, StreamId}} to owner %% - reset: send RESET_STREAM and clean up %% - both: notify AND reset (default) deadline_action = both :: reset | notify | both, %% deadline_error_code: error code for RESET_STREAM on deadline expiry deadline_error_code = 16#FF :: non_neg_integer(), %% RESET_STREAM_AT reliable size (draft-ietf-quic-reliable-stream-reset-07) %% When set, data up to this offset must be delivered before reset takes effect reset_reliable_size :: non_neg_integer() | undefined, %% Error code from RESET_STREAM_AT (must not change once set) reset_error :: non_neg_integer() | undefined }). %% Sent packet info for loss detection -record(sent_packet, { pn :: non_neg_integer(), time_sent :: non_neg_integer(), ack_eliciting :: boolean(), in_flight :: boolean(), size :: non_neg_integer(), frames :: [term()] }). %% Packet number space -record(pn_space, { %% Send state next_pn :: non_neg_integer(), largest_acked :: non_neg_integer() | undefined, %% Receive state largest_recv :: non_neg_integer() | undefined, recv_time :: non_neg_integer() | undefined, ack_ranges :: [{non_neg_integer(), non_neg_integer()}], ack_eliciting_in_flight :: non_neg_integer(), %% Loss detection loss_time :: non_neg_integer() | undefined, sent_packets :: #{non_neg_integer() => #sent_packet{}} }). %% QUIC packet -record(quic_packet, { type :: initial | handshake | zero_rtt | one_rtt | retry, version :: non_neg_integer() | undefined, dcid :: binary(), scid :: binary() | undefined, token :: binary() | undefined, pn :: non_neg_integer() | undefined, % frames list or encrypted payload payload :: binary() | [term()] }). %% Connection state -record(conn_state, { %% Connection IDs % Source Connection ID scid :: binary(), % Destination Connection ID dcid :: binary(), % Original DCID (for Initial packets) original_dcid :: binary(), %% Connection state state :: idle | handshaking | connected | draining | closed, role :: client | server, version :: non_neg_integer(), %% Socket socket :: gen_udp:socket() | undefined, remote_addr :: {inet:ip_address(), inet:port_number()}, local_addr :: {inet:ip_address(), inet:port_number()}, %% Owner process owner :: pid(), %% Crypto state (per encryption level) initial_keys :: #crypto_keys{} | undefined, handshake_keys :: #crypto_keys{} | undefined, app_keys :: #crypto_keys{} | undefined, %% TLS state tls_state :: term(), alpn :: binary() | undefined, %% Flow control max_data_local :: non_neg_integer(), max_data_remote :: non_neg_integer(), data_sent :: non_neg_integer(), data_received :: non_neg_integer(), %% Stream management streams :: #{non_neg_integer() => #stream_state{}}, next_stream_id_bidi :: non_neg_integer(), next_stream_id_uni :: non_neg_integer(), max_streams_bidi_local :: non_neg_integer(), max_streams_bidi_remote :: non_neg_integer(), max_streams_uni_local :: non_neg_integer(), max_streams_uni_remote :: non_neg_integer(), %% Packet numbers pn_space_initial :: #pn_space{}, pn_space_handshake :: #pn_space{}, pn_space_app :: #pn_space{}, %% Timers idle_timeout :: non_neg_integer(), last_activity :: non_neg_integer(), %% Transport parameters transport_params :: map() }). %%==================================================================== %% QUIC-LB (RFC 9312) - QUIC Load Balancer Configuration %%==================================================================== %% Config Rotation for unroutable packets (CR bits = 0b111) -define(LB_CR_UNROUTABLE, 7). %% Maximum server ID length (1-15 bytes) -define(LB_MAX_SERVER_ID_LEN, 15). %% Maximum nonce length (4-18 bytes) -define(LB_MAX_NONCE_LEN, 18). %% Minimum nonce length -define(LB_MIN_NONCE_LEN, 4). %% QUIC-LB Configuration record %% Defines how the load balancer encodes server identity in CIDs -record(lb_config, { %% Config rotation bits (0-6, 7 = unroutable) config_rotation = 0 :: 0..6, %% Encoding algorithm algorithm = plaintext :: plaintext | stream_cipher | block_cipher, %% Server ID (1-15 bytes identifying this server) server_id :: binary(), %% Length of server ID in bytes server_id_len :: 1..15, %% Length of nonce in bytes (4-18) nonce_len = 4 :: 4..18, %% Encryption key (16 bytes for AES-128, required for cipher algorithms) key :: binary() | undefined }). %% CID generation configuration record %% Combines LB config with additional parameters for CID generation -record(cid_config, { %% Optional LB config (undefined = use random CIDs) lb_config :: #lb_config{} | undefined, %% Target CID length (1-20 bytes) cid_len = 8 :: 1..20, %% Reset secret for stateless reset token generation reset_secret :: binary() | undefined }). %%==================================================================== %% PMTU Discovery (RFC 8899 - DPLPMTUD) %%==================================================================== %% PMTU Discovery state machine %% Implements Datagram Packetization Layer Path MTU Discovery %% Uses quic-go's loss array algorithm for better random loss tolerance -record(pmtu_state, { %% State machine state (RFC 8899 Section 5.2) %% disabled: PMTU discovery not active %% base: Using base MTU (1200), ready to probe %% searching: Binary search for optimal MTU in progress %% search_complete: Found optimal MTU %% error: Black hole detected, fell back to base MTU state = disabled :: disabled | base | searching | search_complete | error, %% Base MTU - minimum guaranteed to work (QUIC minimum) base_mtu = 1200 :: pos_integer(), %% Current effective MTU for sending current_mtu = 1200 :: pos_integer(), %% Maximum MTU to probe (from peer's max_udp_payload_size or config) max_mtu = 1500 :: pos_integer(), %% Binary search minimum (confirmed working size) search_min = 1200 :: pos_integer(), %% Loss array: up to 3 sizes that failed probing (quic-go algorithm) %% Sorted ascending, 'undefined' for unused slots %% Initialized with [max_mtu, undefined, undefined] %% When full (3 losses), the largest becomes the new search max lost = [1500, undefined, undefined] :: [pos_integer() | undefined], %% Track if last probe was lost (affects next probe size calculation) %% When true: probe (search_min + lost[0]) / 2 %% When false: probe (search_min + get_max()) / 2 last_probe_lost = false :: boolean(), %% Current probe size being tested probe_size = 0 :: non_neg_integer(), %% Packet number of the last sent probe (for ACK matching) probe_pn :: non_neg_integer() | undefined, %% Generation counter for path migration (quic-go pattern) %% Incremented on path change to ignore stale ACKs/losses generation = 0 :: non_neg_integer(), %% Timer reference for probe timeout probe_timer :: reference() | undefined, %% Timer reference for periodic re-probing (raise timer) raise_timer :: reference() | undefined, %% Black hole detection: consecutive losses at current MTU black_hole_count = 0 :: non_neg_integer(), %% Threshold for black hole detection (consecutive losses) black_hole_threshold = 6 :: pos_integer() }). %% PMTU Discovery configuration options -define(PMTU_DEFAULT_PROBE_TIMEOUT, 5000). -define(PMTU_DEFAULT_RAISE_INTERVAL, 600000). -define(PMTU_SEARCH_THRESHOLD, 10). % QUIC_HRL -endif.