Switch QUIC version negotiation to RFC codepoint
This CL replaces the codepoint from draft-ietf-quic-version-negotiation-13 with the one from RFC 9368. The flag protection is designed so that we'll be fully switched over once we deprecate the quic_version_negotiation_rfc flag.
It's worth noting that this means that older software won't interoperate with new software when it comes to this extension. This is acceptable because this extension is currently not relied upon: since we don't support compatible version negotiation, the only benefit of this extension is to prevent version downgrades, but right now our code only supports versions of IETF QUIC with equal security properties, so it does not actually provide any protections at the moment.
Note that this also means that the strings and logs don't differentiate between the old and new value, so logs will be slightly ambiguous during the transition period if we get a log without knowing the value of the flag.
Alternatively, we could have instead used a succession of flags with support for multiple versions throughout a lengthy transition, but that would have significantly increased the engineering work and delayed the transition for no observable benefit.
Protected by FLAGS_quic_reloadable_flag_quic_version_negotiation_rfc.
PiperOrigin-RevId: 708402132
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 1c7b8bd..dbdcb33 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -62,6 +62,7 @@
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_update_transmission_info_on_frame_acked, false, true, "If true, QuicUnackedPacketMap will update transmission info after session_notifier_->OnFrameAcked.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_use_alarm_multiplexer, false, false, "Manages all of the connection alarms via QuicAlarmMultiplexer.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_use_received_client_addresses_cache, true, true, "If true, use a LRU cache to record client addresses of packets received on server's original address.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_version_negotiation_rfc, false, true, "If true, use the RFC version of the version negotiation transport parameter.")
QUICHE_FLAG(bool, quiche_restart_flag_quic_support_flow_label2, false, true, "If true, QUIC will support reading and writing IPv6 flow labels.")
QUICHE_FLAG(bool, quiche_restart_flag_quic_support_release_time_for_gso, false, false, "If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so.")
QUICHE_FLAG(bool, quiche_restart_flag_quic_testonly_default_false, false, false, "A testonly restart flag that will always default to false.")
diff --git a/quiche/quic/core/crypto/transport_parameters.cc b/quiche/quic/core/crypto/transport_parameters.cc
index f03e67e..f675fb6 100644
--- a/quiche/quic/core/crypto/transport_parameters.cc
+++ b/quiche/quic/core/crypto/transport_parameters.cc
@@ -27,6 +27,7 @@
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_ip_address.h"
namespace quic {
@@ -72,8 +73,10 @@
kGoogleQuicVersion =
0x4752, // Used to transmit version and supported_versions.
- kMinAckDelay = 0xDE1A, // draft-iyengar-quic-delayed-ack.
- kVersionInformation = 0xFF73DB, // draft-ietf-quic-version-negotiation.
+ kMinAckDelay = 0xDE1A, // draft-iyengar-quic-delayed-ack.
+ kVersionInformationDraft =
+ 0xFF73DB, // draft-ietf-quic-version-negotiation-13.
+ kVersionInformation = 0x11, // RFC 9368.
// draft-ietf-quic-reliable-stream-reset.
kReliableStreamReset = 0x17F7586D2CB571,
@@ -147,6 +150,14 @@
case TransportParameters::kMinAckDelay:
return "min_ack_delay_us";
case TransportParameters::kVersionInformation:
+ if (!GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ break;
+ }
+ return "version_information";
+ case TransportParameters::kVersionInformationDraft:
+ if (GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ break;
+ }
return "version_information";
case TransportParameters::kReliableStreamReset:
return "reliable_stream_reset";
@@ -181,9 +192,12 @@
case TransportParameters::kGoogleConnectionOptions:
case TransportParameters::kGoogleQuicVersion:
case TransportParameters::kMinAckDelay:
- case TransportParameters::kVersionInformation:
case TransportParameters::kReliableStreamReset:
return true;
+ case TransportParameters::kVersionInformation:
+ return GetQuicReloadableFlag(quic_version_negotiation_rfc);
+ case TransportParameters::kVersionInformationDraft:
+ return !GetQuicReloadableFlag(quic_version_negotiation_rfc);
}
return false;
}
@@ -1243,7 +1257,13 @@
const uint64_t version_information_length =
sizeof(in.version_information->chosen_version) +
sizeof(QuicVersionLabel) * other_versions.size();
- if (!writer.WriteVarInt62(TransportParameters::kVersionInformation) ||
+ TransportParameters::TransportParameterId version_information_param_id =
+ TransportParameters::kVersionInformation;
+ if (!GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ version_information_param_id =
+ TransportParameters::kVersionInformationDraft;
+ }
+ if (!writer.WriteVarInt62(version_information_param_id) ||
!writer.WriteVarInt62(
/* transport parameter length */ version_information_length) ||
!writer.WriteUInt32(in.version_information->chosen_version)) {
@@ -1549,6 +1569,50 @@
}
} break;
case TransportParameters::kVersionInformation: {
+ if (!GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ // Treat this as an unknown parameter.
+ if (out->custom_parameters.find(param_id) !=
+ out->custom_parameters.end()) {
+ *error_details = "Received a second unknown parameter" +
+ TransportParameterIdToString(param_id);
+ return false;
+ }
+ out->custom_parameters[param_id] =
+ std::string(value_reader.ReadRemainingPayload());
+ break;
+ }
+ if (out->version_information.has_value()) {
+ *error_details = "Received a second version_information";
+ return false;
+ }
+ out->version_information = TransportParameters::VersionInformation();
+ if (!value_reader.ReadUInt32(
+ &out->version_information->chosen_version)) {
+ *error_details = "Failed to read chosen version";
+ return false;
+ }
+ while (!value_reader.IsDoneReading()) {
+ QuicVersionLabel other_version;
+ if (!value_reader.ReadUInt32(&other_version)) {
+ *error_details = "Failed to parse other version";
+ return false;
+ }
+ out->version_information->other_versions.push_back(other_version);
+ }
+ } break;
+ case TransportParameters::kVersionInformationDraft: {
+ if (GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ // Treat this as an unknown parameter.
+ if (out->custom_parameters.find(param_id) !=
+ out->custom_parameters.end()) {
+ *error_details = "Received a second unknown parameter" +
+ TransportParameterIdToString(param_id);
+ return false;
+ }
+ out->custom_parameters[param_id] =
+ std::string(value_reader.ReadRemainingPayload());
+ break;
+ }
if (out->version_information.has_value()) {
*error_details = "Received a second version_information";
return false;
diff --git a/quiche/quic/core/crypto/transport_parameters_test.cc b/quiche/quic/core/crypto/transport_parameters_test.cc
index 8f3c6c9..3c24731 100644
--- a/quiche/quic/core/crypto/transport_parameters_test.cc
+++ b/quiche/quic/core/crypto/transport_parameters_test.cc
@@ -601,6 +601,97 @@
0x04, // length
0x01, 0x23, 0x45, 0x67, // initial version
// version_information
+ 0x11, // parameter id
+ 0x0C, // length
+ 0x01, 0x23, 0x45, 0x67, // chosen version
+ 0x01, 0x23, 0x45, 0x67, // other version 1
+ 0x89, 0xab, 0xcd, 0xef, // other version 2
+ };
+ const uint8_t kClientParamsDraft[] = {
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x04, // parameter id
+ 0x02, // length
+ 0x40, 0x65, // value
+ // initial_max_stream_data_bidi_local
+ 0x05, // parameter id
+ 0x02, // length
+ 0x47, 0xD1, // value
+ // initial_max_stream_data_bidi_remote
+ 0x06, // parameter id
+ 0x02, // length
+ 0x47, 0xD2, // value
+ // initial_max_stream_data_uni
+ 0x07, // parameter id
+ 0x02, // length
+ 0x4B, 0xB8, // value
+ // initial_max_streams_bidi
+ 0x08, // parameter id
+ 0x01, // length
+ 0x15, // value
+ // initial_max_streams_uni
+ 0x09, // parameter id
+ 0x01, // length
+ 0x16, // value
+ // ack_delay_exponent
+ 0x0a, // parameter id
+ 0x01, // length
+ 0x0a, // value
+ // max_ack_delay
+ 0x0b, // parameter id
+ 0x01, // length
+ 0x33, // value
+ // min_ack_delay_us
+ 0x80, 0x00, 0xde, 0x1a, // parameter id
+ 0x02, // length
+ 0x43, 0xe8, // value
+ // disable_active_migration
+ 0x0c, // parameter id
+ 0x00, // length
+ // reliable_stream_reset
+ 0xc0, 0x17, 0xf7, 0x58, 0x6d, 0x2c, 0xb5, 0x71, // parameter id
+ 0x00, // length
+ // active_connection_id_limit
+ 0x0e, // parameter id
+ 0x01, // length
+ 0x34, // value
+ // initial_source_connection_id
+ 0x0f, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ // discard
+ 0x57, 0x3e, // parameter id
+ 0x10, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // google_handshake_message
+ 0x66, 0xab, // parameter id
+ 0x24, // length
+ 0x01, 0x00, 0x01, 0x06, 0x03, 0x03, 0x92, 0x65, 0x5f, 0x52, 0x30, 0x27,
+ 0x0d, 0x49, 0x64, 0xa4, 0xf9, 0x9b, 0x15, 0xbb, 0xad, 0x22, 0x07, 0x36,
+ 0xd9, 0x72, 0xae, 0xa9, 0x7b, 0xf9, 0xac, 0x49, 0x4e, 0xad, 0x62, 0xe6,
+ // initial_round_trip_time_us
+ 0x71, 0x27, // parameter id
+ 0x01, // length
+ 0x35, // value
+ // google_connection_options
+ 0x71, 0x28, // parameter id
+ 0x0c, // length
+ 'A', 'L', 'P', 'N', // value
+ 'E', 'F', 'G', 0x00,
+ 'H', 'I', 'J', 0xff,
+ // Google version extension
+ 0x80, 0x00, 0x47, 0x52, // parameter id
+ 0x04, // length
+ 0x01, 0x23, 0x45, 0x67, // initial version
+ // version_information
0x80, 0xFF, 0x73, 0xDB, // parameter id
0x0C, // length
0x01, 0x23, 0x45, 0x67, // chosen version
@@ -611,6 +702,10 @@
const uint8_t* client_params =
reinterpret_cast<const uint8_t*>(kClientParams);
size_t client_params_length = ABSL_ARRAYSIZE(kClientParams);
+ if (!GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ client_params = reinterpret_cast<const uint8_t*>(kClientParamsDraft);
+ client_params_length = ABSL_ARRAYSIZE(kClientParamsDraft);
+ }
TransportParameters new_params;
std::string error_details;
ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_CLIENT,
@@ -862,6 +957,110 @@
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
// version_information
+ 0x11, // parameter id
+ 0x0C, // length
+ 0x01, 0x23, 0x45, 0x67, // chosen version
+ 0x01, 0x23, 0x45, 0x67, // other version 1
+ 0x89, 0xab, 0xcd, 0xef, // other version 2
+ };
+ const uint8_t kServerParamsDraft[] = {
+ // original_destination_connection_id
+ 0x00, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37,
+ // max_idle_timeout
+ 0x01, // parameter id
+ 0x02, // length
+ 0x6e, 0xec, // value
+ // stateless_reset_token
+ 0x02, // parameter id
+ 0x10, // length
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ // max_udp_payload_size
+ 0x03, // parameter id
+ 0x02, // length
+ 0x63, 0x29, // value
+ // initial_max_data
+ 0x04, // parameter id
+ 0x02, // length
+ 0x40, 0x65, // value
+ // initial_max_stream_data_bidi_local
+ 0x05, // parameter id
+ 0x02, // length
+ 0x47, 0xD1, // value
+ // initial_max_stream_data_bidi_remote
+ 0x06, // parameter id
+ 0x02, // length
+ 0x47, 0xD2, // value
+ // initial_max_stream_data_uni
+ 0x07, // parameter id
+ 0x02, // length
+ 0x4B, 0xB8, // value
+ // initial_max_streams_bidi
+ 0x08, // parameter id
+ 0x01, // length
+ 0x15, // value
+ // initial_max_streams_uni
+ 0x09, // parameter id
+ 0x01, // length
+ 0x16, // value
+ // ack_delay_exponent
+ 0x0a, // parameter id
+ 0x01, // length
+ 0x0a, // value
+ // max_ack_delay
+ 0x0b, // parameter id
+ 0x01, // length
+ 0x33, // value
+ // min_ack_delay_us
+ 0x80, 0x00, 0xde, 0x1a, // parameter id
+ 0x02, // length
+ 0x43, 0xe8, // value
+ // disable_active_migration
+ 0x0c, // parameter id
+ 0x00, // length
+ // reliable_stream_reset
+ 0xc0, 0x17, 0xf7, 0x58, 0x6d, 0x2c, 0xb5, 0x71, // parameter id
+ 0x00, // length
+ // preferred_address
+ 0x0d, // parameter id
+ 0x31, // length
+ 0x41, 0x42, 0x43, 0x44, // IPv4 address
+ 0x48, 0x84, // IPv4 port
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // IPv6 address
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x63, 0x36, // IPv6 port
+ 0x08, // connection ID length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xEF, // connection ID
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // stateless reset token
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ // active_connection_id_limit
+ 0x0e, // parameter id
+ 0x01, // length
+ 0x34, // value
+ // initial_source_connection_id
+ 0x0f, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ // retry_source_connection_id
+ 0x10, // parameter id
+ 0x08, // length
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x76,
+ // google_connection_options
+ 0x71, 0x28, // parameter id
+ 0x0c, // length
+ 'A', 'L', 'P', 'N', // value
+ 'E', 'F', 'G', 0x00,
+ 'H', 'I', 'J', 0xff,
+ // Google version extension
+ 0x80, 0x00, 0x47, 0x52, // parameter id
+ 0x0d, // length
+ 0x01, 0x23, 0x45, 0x67, // negotiated_version
+ 0x08, // length of supported versions array
+ 0x01, 0x23, 0x45, 0x67,
+ 0x89, 0xab, 0xcd, 0xef,
+ // version_information
0x80, 0xFF, 0x73, 0xDB, // parameter id
0x0C, // length
0x01, 0x23, 0x45, 0x67, // chosen version
@@ -872,6 +1071,10 @@
const uint8_t* server_params =
reinterpret_cast<const uint8_t*>(kServerParams);
size_t server_params_length = ABSL_ARRAYSIZE(kServerParams);
+ if (!GetQuicReloadableFlag(quic_version_negotiation_rfc)) {
+ server_params = reinterpret_cast<const uint8_t*>(kServerParamsDraft);
+ server_params_length = ABSL_ARRAYSIZE(kServerParamsDraft);
+ }
TransportParameters new_params;
std::string error_details;
ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_SERVER,