Make server drop IETF QUIC Version Negotiation packets

It is invalid for a client to send a Version Negotiation packet. Servers should drop them instead of trying to parse them as a packet for unsupported version 0.

gfe-relnote: drop version negotiation, protected by gfe2_restart_flag_quic_server_drop_version_negotiation
PiperOrigin-RevId: 247126958
Change-Id: Ia7272de96fa750ec5b4743b6596fc70c8ce5d128
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index a7ab4ff..dad38ab 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -7088,6 +7088,9 @@
   if (connection_.SupportsMultiplePacketNumberSpaces()) {
     return;
   }
+  if (connection_.transport_version() == QUIC_VERSION_99) {
+    return;
+  }
   // Start out with some unsupported version.
   QuicConnectionPeer::GetFramer(&connection_)
       ->set_version_for_tests(ParsedQuicVersion(
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 67e3447..c4eefb1 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -281,7 +281,7 @@
         break;
       default:
         QUIC_BUG << "Unreachable statement";
-        *long_header_type = VERSION_NEGOTIATION;
+        *long_header_type = INVALID_PACKET_TYPE;
         return false;
     }
     return true;
@@ -1430,7 +1430,7 @@
   QuicDataWriter writer(len, buffer.get());
 
   // TODO(fayang): Randomly select a value for the type.
-  uint8_t type = static_cast<uint8_t>(FLAGS_LONG_HEADER | VERSION_NEGOTIATION);
+  uint8_t type = static_cast<uint8_t>(FLAGS_LONG_HEADER);
   if (!writer.WriteUInt8(type)) {
     return nullptr;
   }
@@ -1497,8 +1497,19 @@
   }
 
   if (IsVersionNegotiation(header, packet_has_ietf_packet_header)) {
-    QUIC_DVLOG(1) << ENDPOINT << "Received version negotiation packet";
-    return ProcessVersionNegotiationPacket(&reader, header);
+    if (!GetQuicRestartFlag(quic_server_drop_version_negotiation)) {
+      QUIC_DVLOG(1) << ENDPOINT << "Received version negotiation packet";
+      return ProcessVersionNegotiationPacket(&reader, header);
+    }
+    QUIC_RESTART_FLAG_COUNT_N(quic_server_drop_version_negotiation, 1, 2);
+    if (perspective_ == Perspective::IS_CLIENT) {
+      QUIC_DVLOG(1) << "Client received version negotiation packet";
+      return ProcessVersionNegotiationPacket(&reader, header);
+    } else {
+      QUIC_DLOG(ERROR) << "Server received version negotiation packet";
+      set_detailed_error("Server received version negotiation packet.");
+      return RaiseError(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+    }
   }
 
   if (header.version_flag && header.version != version_) {
@@ -1745,6 +1756,10 @@
 
   if (header->form == IETF_QUIC_SHORT_HEADER_PACKET ||
       header->long_packet_type != VERSION_NEGOTIATION) {
+    DCHECK(header->form == IETF_QUIC_SHORT_HEADER_PACKET ||
+           header->long_packet_type == INITIAL ||
+           header->long_packet_type == HANDSHAKE ||
+           header->long_packet_type == ZERO_RTT_PROTECTED);
     // Process packet number.
     QuicPacketNumber base_packet_number;
     if (supports_multiple_packet_number_spaces_) {
@@ -5249,9 +5264,13 @@
     const QuicPacketHeader& header,
     bool packet_has_ietf_packet_header) const {
   if (perspective_ == Perspective::IS_SERVER) {
-    return false;
+    if (!GetQuicRestartFlag(quic_server_drop_version_negotiation)) {
+      return false;
+    }
+    QUIC_RESTART_FLAG_COUNT_N(quic_server_drop_version_negotiation, 2, 2);
   }
-  if (!packet_has_ietf_packet_header) {
+  if (!packet_has_ietf_packet_header &&
+      perspective_ == Perspective::IS_CLIENT) {
     return header.version_flag;
   }
   if (header.form == IETF_QUIC_SHORT_HEADER_PACKET) {
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index ddf8076..3082a94 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -5398,7 +5398,7 @@
   ASSERT_FALSE(visitor_.stateless_reset_packet_);
 }
 
-TEST_P(QuicFramerTest, VersionNegotiationPacket) {
+TEST_P(QuicFramerTest, VersionNegotiationPacketClient) {
   // clang-format off
   PacketFragments packet = {
       // public flags (version, 8 byte connection_id)
@@ -5453,6 +5453,39 @@
   CheckFramingBoundaries(fragments, QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
 }
 
+TEST_P(QuicFramerTest, VersionNegotiationPacketServer) {
+  if (!GetQuicRestartFlag(quic_server_drop_version_negotiation)) {
+    return;
+  }
+  if (framer_.transport_version() < QUIC_VERSION_44) {
+    return;
+  }
+
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+  // clang-format off
+  unsigned char packet[] = {
+      // public flags (long header with all ignored bits set)
+      0xFF,
+      // version
+      0x00, 0x00, 0x00, 0x00,
+      // connection ID lengths
+      0x50,
+      // destination connection ID
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+      // supported versions
+      QUIC_VERSION_BYTES,
+      'Q', '2', '.', '0',
+  };
+  // clang-format on
+
+  QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+  EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+  EXPECT_EQ(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, framer_.error());
+  EXPECT_EQ("Server received version negotiation packet.",
+            framer_.detailed_error());
+  EXPECT_FALSE(visitor_.version_negotiation_packet_.get());
+}
+
 TEST_P(QuicFramerTest, OldVersionNegotiationPacket) {
   // clang-format off
   PacketFragments packet = {
@@ -13323,7 +13356,7 @@
 
   // clang-format off
   PacketFragments packet = {
-    // public flags (long header with version present)
+    // public flags (Google QUIC header with version present)
     {"Unable to read public flags.",
      {0x09}},
     // connection_id