Move connectivity probing related code from QuicFramer to QuicPacketCreator. QuicPacketCreator has access to debug_delegate_, which helps log on path challenge frames. gfe-relnote: protected by disabled v99 flag. PiperOrigin-RevId: 273405309 Change-Id: I5f7805e0254fcaa417cababc0af0681bf8901e26
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index f792273..17898a4 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -1188,93 +1188,6 @@ return writer->length(); } -size_t QuicFramer::BuildConnectivityProbingPacket( - const QuicPacketHeader& header, - char* buffer, - size_t packet_length, - EncryptionLevel level) { - QuicFrames frames; - - // Write a PING frame, which has no data payload. - QuicPingFrame ping_frame; - frames.push_back(QuicFrame(ping_frame)); - - // Add padding to the rest of the packet. - QuicPaddingFrame padding_frame; - frames.push_back(QuicFrame(padding_frame)); - - return BuildDataPacket(header, frames, buffer, packet_length, level); -} - -size_t QuicFramer::BuildPaddedPathChallengePacket( - const QuicPacketHeader& header, - char* buffer, - size_t packet_length, - QuicPathFrameBuffer* payload, - QuicRandom* randomizer, - EncryptionLevel level) { - if (!VersionHasIetfQuicFrames(version_.transport_version)) { - QUIC_BUG << "Attempt to build a PATH_CHALLENGE Connectivity Probing " - "packet and not doing IETF QUIC"; - return 0; - } - QuicFrames frames; - - // Write a PATH_CHALLENGE frame, which has a random 8-byte payload - randomizer->RandBytes(payload->data(), payload->size()); - - QuicPathChallengeFrame path_challenge_frame(0, *payload); - frames.push_back(QuicFrame(&path_challenge_frame)); - - // Add padding to the rest of the packet in order to assess Path MTU - // characteristics. - QuicPaddingFrame padding_frame; - frames.push_back(QuicFrame(padding_frame)); - - return BuildDataPacket(header, frames, buffer, packet_length, level); -} - -size_t QuicFramer::BuildPathResponsePacket( - const QuicPacketHeader& header, - char* buffer, - size_t packet_length, - const QuicDeque<QuicPathFrameBuffer>& payloads, - const bool is_padded, - EncryptionLevel level) { - if (payloads.empty()) { - QUIC_BUG - << "Attempt to generate connectivity response with no request payloads"; - return 0; - } - if (!VersionHasIetfQuicFrames(version_.transport_version)) { - QUIC_BUG << "Attempt to build a PATH_RESPONSE Connectivity Probing " - "packet and not doing IETF QUIC"; - return 0; - } - - std::vector<std::unique_ptr<QuicPathResponseFrame>> path_response_frames; - for (const QuicPathFrameBuffer& payload : payloads) { - // Note that the control frame ID can be 0 since this is not retransmitted. - path_response_frames.push_back( - std::make_unique<QuicPathResponseFrame>(0, payload)); - } - - QuicFrames frames; - for (const std::unique_ptr<QuicPathResponseFrame>& path_response_frame : - path_response_frames) { - frames.push_back(QuicFrame(path_response_frame.get())); - } - - if (is_padded) { - // Add padding to the rest of the packet in order to assess Path MTU - // characteristics. - QuicPaddingFrame padding_frame; - frames.push_back(QuicFrame(padding_frame)); - } - - return BuildDataPacket(header, frames, buffer, packet_length, level); -} - // static std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildPublicResetPacket( const QuicPublicResetPacket& packet) {
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h index 8421c10..65a0b3b 100644 --- a/quic/core/quic_framer.h +++ b/quic/core/quic_framer.h
@@ -427,33 +427,6 @@ size_t packet_length, EncryptionLevel level); - // Serializes a probing packet, which is a padded PING packet. Returns the - // length of the packet. Returns 0 if it fails to serialize. - size_t BuildConnectivityProbingPacket(const QuicPacketHeader& header, - char* buffer, - size_t packet_length, - EncryptionLevel level); - - // Serialize a probing packet that uses IETF QUIC's PATH CHALLENGE frame. Also - // fills the packet with padding. - size_t BuildPaddedPathChallengePacket(const QuicPacketHeader& header, - char* buffer, - size_t packet_length, - QuicPathFrameBuffer* payload, - QuicRandom* randomizer, - EncryptionLevel level); - - // Serialize a probing response packet that uses IETF QUIC's PATH RESPONSE - // frame. Also fills the packet with padding if |is_padded| is - // true. |payloads| is always emptied, even if the packet can not be - // successfully built. - size_t BuildPathResponsePacket(const QuicPacketHeader& header, - char* buffer, - size_t packet_length, - const QuicDeque<QuicPathFrameBuffer>& payloads, - const bool is_padded, - EncryptionLevel level); - // Returns a new public reset packet. static std::unique_ptr<QuicEncryptedPacket> BuildPublicResetPacket( const QuicPublicResetPacket& packet);
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index 646e9ee..c7e67fc 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -8511,341 +8511,6 @@ QUIC_ARRAYSIZE(packet46)); } -// Test that the connectivity probing packet is serialized correctly as a -// padded PING packet. -TEST_P(QuicFramerTest, BuildConnectivityProbingPacket) { - QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); - QuicPacketHeader header; - header.destination_connection_id = FramerTestConnectionId(); - header.reset_flag = false; - header.version_flag = false; - header.packet_number = kPacketNumber; - - // clang-format off - unsigned char packet[] = { - // public flags (8 byte connection_id) - 0x2C, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (ping frame) - 0x07, - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - - unsigned char packet46[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type - 0x07, - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - - unsigned char packet99[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // frame type (IETF_PING frame) - 0x01, - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - // clang-format on - - unsigned char* p = packet; - size_t packet_size = QUIC_ARRAYSIZE(packet); - if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - packet_size = QUIC_ARRAYSIZE(packet99); - } else if (framer_.transport_version() >= QUIC_VERSION_46) { - p = packet46; - packet_size = QUIC_ARRAYSIZE(packet46); - } - - std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - - size_t length = framer_.BuildConnectivityProbingPacket( - header, buffer.get(), packet_size, ENCRYPTION_INITIAL); - - EXPECT_NE(0u, length); - QuicPacket data(framer_.transport_version(), buffer.release(), length, true, - header); - - test::CompareCharArraysWithHexError("constructed packet", data.data(), - data.length(), AsChars(p), packet_size); -} - -// Test that the path challenge connectivity probing packet is serialized -// correctly as a padded PATH CHALLENGE packet. -TEST_P(QuicFramerTest, BuildPaddedPathChallengePacket) { - if (!VersionHasIetfQuicFrames(framer_.transport_version())) { - // This frame is only for IETF QUIC. - return; - } - - QuicPacketHeader header; - header.destination_connection_id = FramerTestConnectionId(); - header.reset_flag = false; - header.version_flag = false; - header.packet_number = kPacketNumber; - QuicPathFrameBuffer payload; - - // clang-format off - unsigned char packet[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // Path Challenge Frame type (IETF_PATH_CHALLENGE) - 0x1a, - // 8 "random" bytes, MockRandom makes lots of r's - 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r', - // frame type (padding frame) - 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - // clang-format on - - std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - MockRandom randomizer; - - size_t length = framer_.BuildPaddedPathChallengePacket( - header, buffer.get(), QUIC_ARRAYSIZE(packet), &payload, &randomizer, - ENCRYPTION_INITIAL); - EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); - - // Payload has the random bytes that were generated. Copy them into packet, - // above, before checking that the generated packet is correct. - EXPECT_EQ(kQuicPathFrameBufferSize, payload.size()); - - QuicPacket data(framer_.transport_version(), buffer.release(), length, true, - header); - - test::CompareCharArraysWithHexError("constructed packet", data.data(), - data.length(), AsChars(packet), - QUIC_ARRAYSIZE(packet)); -} - -// Several tests that the path response connectivity probing packet is -// serialized correctly as either a padded and unpadded PATH RESPONSE -// packet. Also generates packets with 1 and 3 PATH_RESPONSES in them to -// exercised the single- and multiple- payload cases. -TEST_P(QuicFramerTest, BuildPathResponsePacket1ResponseUnpadded) { - if (!VersionHasIetfQuicFrames(framer_.transport_version())) { - // This frame is only for IETF QUIC. - return; - } - - QuicPacketHeader header; - header.destination_connection_id = FramerTestConnectionId(); - header.reset_flag = false; - header.version_flag = false; - header.packet_number = kPacketNumber; - QuicPathFrameBuffer payload0 = { - {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; - - // Build 1 PATH RESPONSE, not padded - // clang-format off - unsigned char packet[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // Path Response Frame type (IETF_PATH_RESPONSE) - 0x1b, - // 8 "random" bytes - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - }; - // clang-format on - std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - size_t length = framer_.BuildPathResponsePacket( - header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, - /*is_padded=*/false, ENCRYPTION_INITIAL); - EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); - QuicPacket data(framer_.transport_version(), buffer.release(), length, true, - header); - - test::CompareCharArraysWithHexError("constructed packet", data.data(), - data.length(), AsChars(packet), - QUIC_ARRAYSIZE(packet)); -} - -TEST_P(QuicFramerTest, BuildPathResponsePacket1ResponsePadded) { - if (!VersionHasIetfQuicFrames(framer_.transport_version())) { - // This frame is only for IETF QUIC. - return; - } - - QuicPacketHeader header; - header.destination_connection_id = FramerTestConnectionId(); - header.reset_flag = false; - header.version_flag = false; - header.packet_number = kPacketNumber; - QuicPathFrameBuffer payload0 = { - {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; - - // Build 1 PATH RESPONSE, padded - // clang-format off - unsigned char packet[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // Path Response Frame type (IETF_PATH_RESPONSE) - 0x1b, - // 8 "random" bytes - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - // Padding type and pad - 0x00, 0x00, 0x00, 0x00, 0x00 - }; - // clang-format on - std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - size_t length = framer_.BuildPathResponsePacket( - header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, - /*is_padded=*/true, ENCRYPTION_INITIAL); - EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); - QuicPacket data(framer_.transport_version(), buffer.release(), length, true, - header); - - test::CompareCharArraysWithHexError("constructed packet", data.data(), - data.length(), AsChars(packet), - QUIC_ARRAYSIZE(packet)); -} - -TEST_P(QuicFramerTest, BuildPathResponsePacket3ResponsesUnpadded) { - if (!VersionHasIetfQuicFrames(framer_.transport_version())) { - // This frame is only for IETF QUIC. - return; - } - - QuicPacketHeader header; - header.destination_connection_id = FramerTestConnectionId(); - header.reset_flag = false; - header.version_flag = false; - header.packet_number = kPacketNumber; - QuicPathFrameBuffer payload0 = { - {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; - QuicPathFrameBuffer payload1 = { - {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; - QuicPathFrameBuffer payload2 = { - {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; - - // Build one packet with 3 PATH RESPONSES, no padding - // clang-format off - unsigned char packet[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // 3 path response frames (IETF_PATH_RESPONSE type byte and payload) - 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - }; - // clang-format on - - std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - payloads.push_back(payload1); - payloads.push_back(payload2); - size_t length = framer_.BuildPathResponsePacket( - header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, - /*is_padded=*/false, ENCRYPTION_INITIAL); - EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); - QuicPacket data(framer_.transport_version(), buffer.release(), length, true, - header); - - test::CompareCharArraysWithHexError("constructed packet", data.data(), - data.length(), AsChars(packet), - QUIC_ARRAYSIZE(packet)); -} - -TEST_P(QuicFramerTest, BuildPathResponsePacket3ResponsesPadded) { - if (!VersionHasIetfQuicFrames(framer_.transport_version())) { - // This frame is only for IETF QUIC. - return; - } - - QuicPacketHeader header; - header.destination_connection_id = FramerTestConnectionId(); - header.reset_flag = false; - header.version_flag = false; - header.packet_number = kPacketNumber; - QuicPathFrameBuffer payload0 = { - {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; - QuicPathFrameBuffer payload1 = { - {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; - QuicPathFrameBuffer payload2 = { - {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; - - // Build one packet with 3 PATH RESPONSES, with padding - // clang-format off - unsigned char packet[] = { - // type (short header, 4 byte packet number) - 0x43, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - - // 3 path response frames (IETF_PATH_RESPONSE byte and payload) - 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, - 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - // Padding - 0x00, 0x00, 0x00, 0x00, 0x00 - }; - // clang-format on - - std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - payloads.push_back(payload1); - payloads.push_back(payload2); - size_t length = framer_.BuildPathResponsePacket( - header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, - /*is_padded=*/true, ENCRYPTION_INITIAL); - EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); - QuicPacket data(framer_.transport_version(), buffer.release(), length, true, - header); - - test::CompareCharArraysWithHexError("constructed packet", data.data(), - data.length(), AsChars(packet), - QUIC_ARRAYSIZE(packet)); -} // Test that the MTU discovery packet is serialized correctly as a PING packet. TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) {
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc index e7f5185..1a2ca6f 100644 --- a/quic/core/quic_packet_creator.cc +++ b/quic/core/quic_packet_creator.cc
@@ -5,10 +5,12 @@ #include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" #include <algorithm> +#include <cstddef> #include <cstdint> #include <string> #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" @@ -700,7 +702,7 @@ << header; std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - size_t length = framer_->BuildConnectivityProbingPacket( + size_t length = BuildConnectivityProbingPacket( header, buffer.get(), max_plaintext_size_, packet_.encryption_level); DCHECK(length); @@ -734,7 +736,7 @@ QUIC_DVLOG(2) << ENDPOINT << "Serializing path challenge packet " << header; std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - size_t length = framer_->BuildPaddedPathChallengePacket( + size_t length = BuildPaddedPathChallengePacket( header, buffer.get(), max_plaintext_size_, payload, random_, packet_.encryption_level); DCHECK(length); @@ -770,9 +772,9 @@ QUIC_DVLOG(2) << ENDPOINT << "Serializing path response packet " << header; std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - size_t length = framer_->BuildPathResponsePacket( - header, buffer.get(), max_plaintext_size_, payloads, is_padded, - packet_.encryption_level); + size_t length = + BuildPathResponsePacket(header, buffer.get(), max_plaintext_size_, + payloads, is_padded, packet_.encryption_level); DCHECK(length); const size_t encrypted_length = framer_->EncryptInPlace( @@ -791,6 +793,92 @@ return serialize_packet; } +size_t QuicPacketCreator::BuildPaddedPathChallengePacket( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + QuicPathFrameBuffer* payload, + QuicRandom* randomizer, + EncryptionLevel level) { + DCHECK(VersionHasIetfQuicFrames(framer_->transport_version())); + QuicFrames frames; + + // Write a PATH_CHALLENGE frame, which has a random 8-byte payload + randomizer->RandBytes(payload->data(), payload->size()); + QuicPathChallengeFrame path_challenge_frame(0, *payload); + frames.push_back(QuicFrame(&path_challenge_frame)); + + if (debug_delegate_ != nullptr) { + debug_delegate_->OnFrameAddedToPacket(QuicFrame(&path_challenge_frame)); + } + + // Add padding to the rest of the packet in order to assess Path MTU + // characteristics. + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(padding_frame)); + + return framer_->BuildDataPacket(header, frames, buffer, packet_length, level); +} + +size_t QuicPacketCreator::BuildPathResponsePacket( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded, + EncryptionLevel level) { + if (payloads.empty()) { + QUIC_BUG + << "Attempt to generate connectivity response with no request payloads"; + return 0; + } + DCHECK(VersionHasIetfQuicFrames(framer_->transport_version())); + + std::vector<std::unique_ptr<QuicPathResponseFrame>> path_response_frames; + for (const QuicPathFrameBuffer& payload : payloads) { + // Note that the control frame ID can be 0 since this is not retransmitted. + path_response_frames.push_back( + std::make_unique<QuicPathResponseFrame>(0, payload)); + } + + QuicFrames frames; + for (const std::unique_ptr<QuicPathResponseFrame>& path_response_frame : + path_response_frames) { + frames.push_back(QuicFrame(path_response_frame.get())); + if (debug_delegate_ != nullptr) { + debug_delegate_->OnFrameAddedToPacket( + QuicFrame(path_response_frame.get())); + } + } + + if (is_padded) { + // Add padding to the rest of the packet in order to assess Path MTU + // characteristics. + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(padding_frame)); + } + + return framer_->BuildDataPacket(header, frames, buffer, packet_length, level); +} + +size_t QuicPacketCreator::BuildConnectivityProbingPacket( + const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + EncryptionLevel level) { + QuicFrames frames; + + // Write a PING frame, which has no data payload. + QuicPingFrame ping_frame; + frames.push_back(QuicFrame(ping_frame)); + + // Add padding to the rest of the packet. + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(padding_frame)); + + return framer_->BuildDataPacket(header, frames, buffer, packet_length, level); +} + // TODO(b/74062209): Make this a public method of framer? SerializedPacket QuicPacketCreator::NoPacket() { return SerializedPacket(QuicPacketNumber(), PACKET_1BYTE_PACKET_NUMBER,
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h index a654423..f202af0 100644 --- a/quic/core/quic_packet_creator.h +++ b/quic/core/quic_packet_creator.h
@@ -395,6 +395,33 @@ return combine_generator_and_creator_; } + // Serialize a probing packet that uses IETF QUIC's PATH CHALLENGE frame. Also + // fills the packet with padding. + size_t BuildPaddedPathChallengePacket(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + QuicPathFrameBuffer* payload, + QuicRandom* randomizer, + EncryptionLevel level); + + // Serialize a probing response packet that uses IETF QUIC's PATH RESPONSE + // frame. Also fills the packet with padding if |is_padded| is + // true. |payloads| is always emptied, even if the packet can not be + // successfully built. + size_t BuildPathResponsePacket(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + const QuicDeque<QuicPathFrameBuffer>& payloads, + const bool is_padded, + EncryptionLevel level); + + // Serializes a probing packet, which is a padded PING packet. Returns the + // length of the packet. Returns 0 if it fails to serialize. + size_t BuildConnectivityProbingPacket(const QuicPacketHeader& header, + char* buffer, + size_t packet_length, + EncryptionLevel level); + private: friend class test::QuicPacketCreatorPeer;
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc index 1e66667..75ea7f5 100644 --- a/quic/core/quic_packet_creator_test.cc +++ b/quic/core/quic_packet_creator_test.cc
@@ -18,6 +18,7 @@ #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" @@ -39,6 +40,12 @@ namespace test { namespace { +const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678)); +// Use fields in which each byte is distinct to ensure that every byte is +// framed correctly. The values are otherwise arbitrary. +const QuicConnectionId kTestConnectionId = + TestConnectionId(UINT64_C(0xFEDCBA9876543210)); + // Run tests with combinations of {ParsedQuicVersion, // ToggleVersionSerialization}. struct TestParams { @@ -585,8 +592,8 @@ } TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) { - QuicConnectionCloseFrame frame(GetParam().version.transport_version, - QUIC_NO_ERROR, "error", + QuicConnectionCloseFrame frame(creator_.transport_version(), QUIC_NO_ERROR, + "error", /*transport_close_frame_type=*/0); QuicFrames frames; @@ -823,7 +830,7 @@ ParsedQuicVersionVector versions; versions.push_back(test::QuicVersionMax()); const bool ietf_quic = - VersionHasIetfInvariantHeader(GetParam().version.transport_version); + VersionHasIetfInvariantHeader(creator_.transport_version()); const bool has_length_prefix = GetParam().version.HasLengthPrefixedConnectionIds(); std::unique_ptr<QuicEncryptedPacket> encrypted( @@ -840,6 +847,340 @@ client_framer_.ProcessPacket(*encrypted); } +// Test that the path challenge connectivity probing packet is serialized +// correctly as a padded PATH CHALLENGE packet. +TEST_P(QuicPacketCreatorTest, BuildPathChallengePacket) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { + // This frame is only for IETF QUIC. + return; + } + + QuicPacketHeader header; + header.destination_connection_id = kTestConnectionId; + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload; + + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // Path Challenge Frame type (IETF_PATH_CHALLENGE) + 0x1a, + // 8 "random" bytes, MockRandom makes lots of r's + 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r', + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); + MockRandom randomizer; + + size_t length = creator_.BuildPaddedPathChallengePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), &payload, &randomizer, + ENCRYPTION_INITIAL); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + + // Payload has the random bytes that were generated. Copy them into packet, + // above, before checking that the generated packet is correct. + EXPECT_EQ(kQuicPathFrameBufferSize, payload.size()); + + QuicPacket data(creator_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError( + "constructed packet", data.data(), data.length(), + reinterpret_cast<char*>(packet), QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicPacketCreatorTest, BuildConnectivityProbingPacket) { + QuicPacketHeader header; + header.destination_connection_id = kTestConnectionId; + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x2C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (ping frame) + 0x07, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet46[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type + 0x07, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char packet99[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (IETF_PING frame) + 0x01, + // frame type (padding frame) + 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + unsigned char* p = packet; + size_t packet_size = QUIC_ARRAYSIZE(packet); + if (VersionHasIetfQuicFrames(creator_.transport_version())) { + p = packet99; + packet_size = QUIC_ARRAYSIZE(packet99); + } else if (creator_.transport_version() >= QUIC_VERSION_46) { + p = packet46; + packet_size = QUIC_ARRAYSIZE(packet46); + } + + std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); + + size_t length = creator_.BuildConnectivityProbingPacket( + header, buffer.get(), packet_size, ENCRYPTION_INITIAL); + + EXPECT_NE(0u, length); + QuicPacket data(creator_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError("constructed packet", data.data(), + data.length(), reinterpret_cast<char*>(p), + packet_size); +} + +// Several tests that the path response connectivity probing packet is +// serialized correctly as either a padded and unpadded PATH RESPONSE +// packet. Also generates packets with 1 and 3 PATH_RESPONSES in them to +// exercised the single- and multiple- payload cases. +TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket1ResponseUnpadded) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { + // This frame is only for IETF QUIC. + return; + } + + QuicPacketHeader header; + header.destination_connection_id = kTestConnectionId; + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + + // Build 1 PATH RESPONSE, not padded + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // Path Response Frame type (IETF_PATH_RESPONSE) + 0x1b, + // 8 "random" bytes + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }; + // clang-format on + std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + size_t length = creator_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/false, ENCRYPTION_INITIAL); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(creator_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError( + "constructed packet", data.data(), data.length(), + reinterpret_cast<char*>(packet), QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket1ResponsePadded) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { + // This frame is only for IETF QUIC. + return; + } + + QuicPacketHeader header; + header.destination_connection_id = kTestConnectionId; + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + + // Build 1 PATH RESPONSE, padded + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // Path Response Frame type (IETF_PATH_RESPONSE) + 0x1b, + // 8 "random" bytes + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + // Padding type and pad + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + size_t length = creator_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/true, ENCRYPTION_INITIAL); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(creator_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError( + "constructed packet", data.data(), data.length(), + reinterpret_cast<char*>(packet), QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesUnpadded) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { + // This frame is only for IETF QUIC. + return; + } + + QuicPacketHeader header; + header.destination_connection_id = kTestConnectionId; + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + QuicPathFrameBuffer payload1 = { + {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; + QuicPathFrameBuffer payload2 = { + {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; + + // Build one packet with 3 PATH RESPONSES, no padding + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // 3 path response frames (IETF_PATH_RESPONSE type byte and payload) + 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + }; + // clang-format on + + std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); + size_t length = creator_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/false, ENCRYPTION_INITIAL); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(creator_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError( + "constructed packet", data.data(), data.length(), + reinterpret_cast<char*>(packet), QUIC_ARRAYSIZE(packet)); +} + +TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesPadded) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { + // This frame is only for IETF QUIC. + return; + } + + QuicPacketHeader header; + header.destination_connection_id = kTestConnectionId; + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + QuicPathFrameBuffer payload0 = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + QuicPathFrameBuffer payload1 = { + {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}}; + QuicPathFrameBuffer payload2 = { + {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}}; + + // Build one packet with 3 PATH RESPONSES, with padding + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // 3 path response frames (IETF_PATH_RESPONSE byte and payload) + 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + // Padding + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + // clang-format on + + std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); + QuicDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); + size_t length = creator_.BuildPathResponsePacket( + header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads, + /*is_padded=*/true, ENCRYPTION_INITIAL); + EXPECT_EQ(length, QUIC_ARRAYSIZE(packet)); + QuicPacket data(creator_.transport_version(), buffer.release(), length, true, + header); + + test::CompareCharArraysWithHexError( + "constructed packet", data.data(), data.length(), + reinterpret_cast<char*>(packet), QUIC_ARRAYSIZE(packet)); +} + TEST_P(QuicPacketCreatorTest, SerializeConnectivityProbingPacket) { for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { EncryptionLevel level = static_cast<EncryptionLevel>(i); @@ -847,7 +1188,7 @@ creator_.set_encryption_level(level); OwningSerializedPacketPointer encrypted; - if (VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (VersionHasIetfQuicFrames(creator_.transport_version())) { QuicPathFrameBuffer payload = { {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; encrypted = @@ -862,7 +1203,7 @@ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - if (VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (VersionHasIetfQuicFrames(creator_.transport_version())) { EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); } else { @@ -878,7 +1219,7 @@ } TEST_P(QuicPacketCreatorTest, SerializePathChallengeProbePacket) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload = { @@ -909,7 +1250,7 @@ } TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload0 = { @@ -943,7 +1284,7 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadUnPadded) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload0 = { @@ -975,7 +1316,7 @@ } TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload0 = { @@ -1012,7 +1353,7 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsUnPadded) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload0 = { @@ -1047,7 +1388,7 @@ } TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload0 = { @@ -1087,7 +1428,7 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsUnpadded) { - if (!VersionHasIetfQuicFrames(GetParam().version.transport_version)) { + if (!VersionHasIetfQuicFrames(creator_.transport_version())) { return; } QuicPathFrameBuffer payload0 = { @@ -1124,7 +1465,7 @@ } TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { - if (VersionHasIetfInvariantHeader(GetParam().version.transport_version) && + if (VersionHasIetfInvariantHeader(creator_.transport_version()) && !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) { EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); @@ -1162,7 +1503,7 @@ TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) { QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1); - if (VersionHasIetfInvariantHeader(GetParam().version.transport_version) && + if (VersionHasIetfInvariantHeader(creator_.transport_version()) && !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) { EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); @@ -1196,7 +1537,7 @@ TEST_P(QuicPacketCreatorTest, SkipNPacketNumbers) { QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1); - if (VersionHasIetfInvariantHeader(GetParam().version.transport_version) && + if (VersionHasIetfInvariantHeader(creator_.transport_version()) && !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) { EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); @@ -1862,7 +2203,7 @@ // Regression test for bugfix of GetPacketHeaderSize. TEST_P(QuicPacketCreatorTest, GetGuaranteedLargestMessagePayload) { - QuicTransportVersion version = GetParam().version.transport_version; + QuicTransportVersion version = creator_.transport_version(); if (!VersionSupportsMessageFrames(version)) { return; }