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