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)) {