Add an QUIC version reserved for negotiation to help test negotiation gfe-relnote: n/a, test-only changes PiperOrigin-RevId: 247473641 Change-Id: If063e6cc1f833f865a400298d8ade5bbe2c6ec42
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index 7179513..777291d 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -569,8 +569,7 @@ // Client supports IETF QUIC, while it is not supported by server. bool ClientSupportsIetfQuicNotSupportedByServer() { - return GetParam().client_supported_versions[0].transport_version > - QUIC_VERSION_43 && + return client_supported_versions_[0].transport_version > QUIC_VERSION_43 && FilterSupportedVersions(GetParam().server_supported_versions)[0] .transport_version <= QUIC_VERSION_43; } @@ -579,8 +578,7 @@ // closes connection when version negotiation is received. bool ServerSendsVersionNegotiation() { return GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation) && - GetParam().client_supported_versions[0] != - GetParam().negotiated_version; + client_supported_versions_[0] != GetParam().negotiated_version; } bool SupportsIetfQuicWithTls(ParsedQuicVersion version) { @@ -712,6 +710,24 @@ client_->client()->GetNumSentClientHellos()); } +TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) { + client_supported_versions_.insert(client_supported_versions_.begin(), + QuicVersionReservedForNegotiation()); + ASSERT_TRUE(Initialize()); + ASSERT_TRUE(ServerSendsVersionNegotiation()); + + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + + int expected_num_client_hellos = 3; + if (BothSidesSupportStatelessRejects()) { + ++expected_num_client_hellos; + } + + EXPECT_EQ(expected_num_client_hellos, + client_->client()->GetNumSentClientHellos()); +} + TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) { QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId( GetParam().negotiated_version.transport_version);
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 23f386b..eea8618 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -686,7 +686,7 @@ DCHECK_EQ(connection_id_, packet.connection_id); if (perspective_ == Perspective::IS_SERVER) { const std::string error_details = - "Server receieved version negotiation packet."; + "Server received version negotiation packet."; QUIC_BUG << error_details; QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation); TearDownLocalConnectionState(QUIC_INTERNAL_ERROR, error_details, @@ -729,6 +729,7 @@ return; } + ParsedQuicVersion original_version = version(); if (!SelectMutualVersion(packet.versions)) { CloseConnection( QUIC_INVALID_VERSION, @@ -741,10 +742,23 @@ return; } + if (original_version.handshake_protocol != version().handshake_protocol) { + const std::string error_details = + "In-connection version negotiation between mismatched handshake " + " protocols " + + ParsedQuicVersionToString(original_version) + " and " + + ParsedQuicVersionToString(version()) + " is currently unsupported."; + QUIC_DLOG(WARNING) << error_details; + TearDownLocalConnectionState(QUIC_INVALID_VERSION, error_details, + ConnectionCloseSource::FROM_SELF); + return; + } + QUIC_DLOG(INFO) << ENDPOINT << "Negotiated version: " - << QuicVersionToString(transport_version()); + << ParsedQuicVersionToString(version()); no_stop_waiting_frames_ = transport_version() > QUIC_VERSION_43; version_negotiation_state_ = NEGOTIATION_IN_PROGRESS; + RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); }
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 8995d00..87b3e0c 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -7114,16 +7114,13 @@ if (connection_.SupportsMultiplePacketNumberSpaces()) { return; } - if (connection_.transport_version() == QUIC_VERSION_99) { - return; - } - // Start out with some unsupported version. + const bool expect_failure = + GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation) || + connection_.version().handshake_protocol != + QuicVersionReservedForNegotiation().handshake_protocol; + // Start out with an unsupported version. QuicConnectionPeer::GetFramer(&connection_) - ->set_version_for_tests(ParsedQuicVersion( - PROTOCOL_UNSUPPORTED, - GetParam().version.transport_version == QUIC_VERSION_99 - ? QUIC_VERSION_99 - : QUIC_VERSION_UNSUPPORTED)); + ->set_version_for_tests(QuicVersionReservedForNegotiation()); // Send a version negotiation packet. std::unique_ptr<QuicEncryptedPacket> encrypted( @@ -7132,12 +7129,12 @@ AllSupportedVersions())); std::unique_ptr<QuicReceivedPacket> received( ConstructReceivedPacket(*encrypted, QuicTime::Zero())); - if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) { + if (expect_failure) { EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_VERSION, _, ConnectionCloseSource::FROM_SELF)); } connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received); - if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) { + if (expect_failure) { EXPECT_FALSE(connection_.connected()); return; }
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 75ee18b..7517b18 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -1424,7 +1424,8 @@ QuicFramer::BuildIetfVersionNegotiationPacket( QuicConnectionId connection_id, const ParsedQuicVersionVector& versions) { - QUIC_DVLOG(1) << "Building IETF version negotiation packet."; + QUIC_DVLOG(1) << "Building IETF version negotiation packet: " + << ParsedQuicVersionVectorToString(versions); DCHECK(!versions.empty()); size_t len = kPacketHeaderTypeSize + kConnectionIdLengthSize + connection_id.length() + @@ -1586,6 +1587,9 @@ packet.versions.push_back(ParseQuicVersionLabel(version_label)); } while (!reader->IsDoneReading()); + QUIC_DLOG(INFO) << ENDPOINT << "parsed version negotiation: " + << ParsedQuicVersionVectorToString(packet.versions); + visitor_->OnVersionNegotiationPacket(packet); return true; }
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc index 01e08d2..c5c63ca 100644 --- a/quic/core/quic_versions.cc +++ b/quic/core/quic_versions.cc
@@ -98,9 +98,12 @@ case QUIC_VERSION_99: if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 && GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) { - return 0xff000000 + GetQuicFlag(FLAGS_quic_ietf_draft_version); + return MakeVersionLabel(0xff, 0x00, 0x00, + GetQuicFlag(FLAGS_quic_ietf_draft_version)); } return MakeVersionLabel(proto, '0', '9', '9'); + case QUIC_VERSION_RESERVED_FOR_NEGOTIATION: + return MakeVersionLabel(0xda, 0x5a, 0x3a, 0x3a); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicTransportVersion to be written to the wire. @@ -404,6 +407,11 @@ return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED); } +ParsedQuicVersion QuicVersionReservedForNegotiation() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, + QUIC_VERSION_RESERVED_FOR_NEGOTIATION); +} + std::string AlpnForVersion(ParsedQuicVersion parsed_version) { if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 && parsed_version.transport_version == QUIC_VERSION_99 && @@ -435,6 +443,8 @@ SetQuicReloadableFlag(quic_validate_packet_number_post_decryption, true); SetQuicReloadableFlag(quic_print_tag_hex, true); SetQuicReloadableFlag(quic_send_version_negotiation_fixed_bit, true); + SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, true); + SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, true); SetQuicRestartFlag(quic_server_drop_version_negotiation, true); SetQuicRestartFlag(quic_enable_accept_random_ipn, true); }
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h index 7264810..7853134 100644 --- a/quic/core/quic_versions.h +++ b/quic/core/quic_versions.h
@@ -106,6 +106,11 @@ QUIC_VERSION_47 = 47, // Allow variable-length QUIC connection IDs. QUIC_VERSION_99 = 99, // Dumping ground for IETF QUIC changes which are not // yet ready for production. + // QUIC_VERSION_RESERVED_FOR_NEGOTIATION is sent over the wire as da5a3a3a + // which is part of a range reserved by the IETF for version negotiation + // testing. It is intentionally meant to never be supported by servers to + // trigger version negotiation when proposed by clients. + QUIC_VERSION_RESERVED_FOR_NEGOTIATION = 999, }; // The crypto handshake protocols that can be used with QUIC. @@ -161,6 +166,8 @@ QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion(); +QUIC_EXPORT_PRIVATE ParsedQuicVersion QuicVersionReservedForNegotiation(); + QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version);
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc index e94a3da..51a185f 100644 --- a/quic/core/quic_versions_test.cc +++ b/quic/core/quic_versions_test.cc
@@ -630,6 +630,17 @@ EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_99)); } +TEST_F(QuicVersionsTest, ReservedForNegotiation) { + EXPECT_EQ(QUIC_VERSION_RESERVED_FOR_NEGOTIATION, + QuicVersionReservedForNegotiation().transport_version); + // QUIC_VERSION_RESERVED_FOR_NEGOTIATION MUST NOT be added to + // kSupportedTransportVersions. + for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) { + EXPECT_NE(QUIC_VERSION_RESERVED_FOR_NEGOTIATION, + kSupportedTransportVersions[i]); + } +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/tools/quic_client_bin.cc b/quic/tools/quic_client_bin.cc index 8b30d96..2dec469 100644 --- a/quic/tools/quic_client_bin.cc +++ b/quic/tools/quic_client_bin.cc
@@ -179,6 +179,13 @@ DEFINE_QUIC_COMMAND_LINE_FLAG( bool, + force_version_negotiation, + false, + "If true, start by proposing a version that is reserved for version " + "negotiation."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + bool, redirect_is_success, true, "If true, an HTTP response code of 3xx is considered to be a " @@ -265,6 +272,11 @@ quic::QuicEnableVersion(parsed_quic_version); } + if (GetQuicFlag(FLAGS_force_version_negotiation)) { + versions.insert(versions.begin(), + quic::QuicVersionReservedForNegotiation()); + } + const int32_t num_requests(GetQuicFlag(FLAGS_num_requests)); std::unique_ptr<quic::ProofVerifier> proof_verifier; if (GetQuicFlag(FLAGS_disable_certificate_verification)) {