Skip to content

Mandatory Connection-ID Transport Parameter Absence Uses the Wrong Close Code #139

@LiD0209

Description

@LiD0209

Mandatory Connection-ID Transport Parameter Absence Uses the Wrong Close Code

Summary

RFC 9000 requires a TRANSPORT_PARAMETER_ERROR when a peer omits a mandatory connection-ID transport parameter. This applies when initial_source_connection_id is absent from either endpoint, and when a client receives server transport parameters without original_destination_connection_id.

quiche detects these missing parameters, but the failure path closes the connection with IETF_QUIC_PROTOCOL_VIOLATION. For IETF QUIC, that maps to the wire transport error PROTOCOL_VIOLATION, not TRANSPORT_PARAMETER_ERROR.

Standard Requirement

An endpoint MUST treat the absence of the initial_source_connection_id
transport parameter from either endpoint or the absence of the
original_destination_connection_id transport parameter from the server as a
connection error of type TRANSPORT_PARAMETER_ERROR.
TRANSPORT_PARAMETER_ERROR (0x08):

An endpoint received transport parameters that were badly formatted, included
an invalid value, omitted a mandatory transport parameter, included a forbidden
transport parameter, or were otherwise in error.

The standard requires the specific transport error code TRANSPORT_PARAMETER_ERROR for the missing mandatory connection-ID parameters.

Relevant Source Code

Source: quiche-main/quiche-main/quiche/quic/core/quic_config.cc:1428-1434

1428:     if (params.initial_source_connection_id.has_value()) {
1429:       received_initial_source_connection_id_ =
1430:           *params.initial_source_connection_id;
1431:     }
1432:     if (params.retry_source_connection_id.has_value()) {
1433:       received_retry_source_connection_id_ = *params.retry_source_connection_id;
1434:     }

quiche records initial_source_connection_id only if it is present.

Source: quiche-main/quiche-main/quiche/quic/core/quic_config.cc:1323-1325

1323:   if (!is_resumption && params.original_destination_connection_id.has_value()) {
1324:     received_original_destination_connection_id_ =
1325:         *params.original_destination_connection_id;

quiche records original_destination_connection_id only if it is present.

Source: quiche-main/quiche-main/quiche/quic/core/quic_connection.cc:329-352

329:   // Validate initial_source_connection_id.
330:   QuicConnectionId expected_initial_source_connection_id;
331:   if (perspective_ == Perspective::IS_CLIENT) {
332:     expected_initial_source_connection_id = default_path_.server_connection_id;
333:   } else {
334:     expected_initial_source_connection_id = default_path_.client_connection_id;
335:   }
336:   if (!config.HasReceivedInitialSourceConnectionId() ||
337:       config.ReceivedInitialSourceConnectionId() !=
338:           expected_initial_source_connection_id) {
345:     std::string error_details =
346:         absl::StrCat("Bad initial_source_connection_id: expected ",
347:                      expected_initial_source_connection_id.ToString(),
348:                      ", received ", received_value);
349:     CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
350:                     ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
351:     return false;
352:   }

The missing initial_source_connection_id branch closes with IETF_QUIC_PROTOCOL_VIOLATION.

Source: quiche-main/quiche-main/quiche/quic/core/quic_connection.cc:353-371

353:   if (perspective_ == Perspective::IS_CLIENT) {
354:     // Validate original_destination_connection_id.
355:     if (!config.HasReceivedOriginalConnectionId() ||
356:         config.ReceivedOriginalConnectionId() !=
357:             GetOriginalDestinationConnectionId()) {
364:       std::string error_details =
365:           absl::StrCat("Bad original_destination_connection_id: expected ",
366:                        GetOriginalDestinationConnectionId().ToString(),
367:                        ", received ", received_value);
368:       CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
369:                       ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
370:       return false;
371:     }

The missing server original_destination_connection_id branch also closes with IETF_QUIC_PROTOCOL_VIOLATION.

Source: quiche-main/quiche-main/quiche/quic/core/quic_error_codes.cc:578-579

578:     case IETF_QUIC_PROTOCOL_VIOLATION:
579:       return {true, static_cast<uint64_t>(PROTOCOL_VIOLATION)};

For IETF QUIC, IETF_QUIC_PROTOCOL_VIOLATION maps to the wire error PROTOCOL_VIOLATION.

Source: quiche-main/quiche-main/quiche/quic/core/frames/quic_connection_close_frame.cc:28-34

28:   QuicErrorCodeToIetfMapping mapping =
29:       QuicErrorCodeToTransportErrorCode(error_code);
30:   if (ietf_error != NO_IETF_QUIC_ERROR) {
31:     wire_error_code = ietf_error;
32:   } else {
33:     wire_error_code = mapping.error_code;
34:   }

When no explicit IETF transport error override is supplied, the close frame uses the mapped wire error.

Implementation Behavior

quiche validates both mandatory connection-ID transport parameters after transport parameter processing. If either initial_source_connection_id is absent, or the client-side server original_destination_connection_id is absent, validation fails and the connection is closed.

The close code used by these branches is IETF_QUIC_PROTOCOL_VIOLATION, which becomes PROTOCOL_VIOLATION on the wire.

Inconsistency Reason

The standard requires TRANSPORT_PARAMETER_ERROR when a mandatory connection-ID transport parameter is absent. quiche instead sends PROTOCOL_VIOLATION for the missing-parameter branches.

This is a close-code mismatch: the connection is rejected, but the observable IETF transport error code does not match RFC 9000 Section 7.3.

Impact

The CID authentication check still prevents the connection from continuing with missing mandatory parameters. The interoperability issue is that peers and conformance tests observe PROTOCOL_VIOLATION instead of the required TRANSPORT_PARAMETER_ERROR.

Fix Direction

Separate missing-parameter checks from value-mismatch checks in ValidateConfigConnectionIds.

When initial_source_connection_id is absent, or when a client sees that server original_destination_connection_id is absent, close with an explicit IETF transport error of TRANSPORT_PARAMETER_ERROR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions