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