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