1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-07-29 03:41:11 +03:00

Add padding to QUIC short header packets

Summary:
Adds paddingModulo to QuicPacketBuilder, which pads up short header packets so that the length remaining of a packet is a multiple of paddingModulo

ie a message of length 25 and max packet size of 40 with a paddingModulo of 8 will get 7 padding frames to a total length of 32. (and a message of length 26 will get 6 padding frames to also go up to a total length of 32)

Reviewed By: mjoras

Differential Revision: D32858254

fbshipit-source-id: 99ada01108429db9f9bba1e342daf99e80385179
This commit is contained in:
Kyle Mirzakhanian
2022-01-07 15:00:39 -08:00
committed by Facebook GitHub Bot
parent 6b7d667e51
commit 29ed688b58
10 changed files with 238 additions and 24 deletions

View File

@ -6,10 +6,9 @@
*
*/
#include <quic/api/QuicPacketScheduler.h>
#include <folly/portability/GTest.h>
#include <quic/api/QuicPacketScheduler.h>
#include <quic/api/QuicTransportFunctions.h>
#include <quic/api/test/Mocks.h>
#include <quic/client/state/ClientStateMachine.h>
@ -517,7 +516,7 @@ TEST_F(QuicPacketSchedulerTest, StreamFrameSchedulerStreamNotExists) {
TEST_F(QuicPacketSchedulerTest, NoCloningForDSR) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "Juice WRLD", 0);
EXPECT_FALSE(cloningScheduler.hasData());
@ -543,7 +542,7 @@ TEST_F(QuicPacketSchedulerTest, NoCloningForDSR) {
TEST_F(QuicPacketSchedulerTest, CloningSchedulerTest) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
EXPECT_FALSE(cloningScheduler.hasData());
@ -610,7 +609,7 @@ TEST_P(QuicPacketSchedulerTest, NoCloningWithOnlyD6DProbes) {
false /* isDSRPacket */);
ASSERT_EQ(1, conn.outstandings.packets.size());
EXPECT_TRUE(conn.outstandings.packets.back().metadata.isD6DProbe);
FrameScheduler noopScheduler("noopScheduler");
FrameScheduler noopScheduler("noopScheduler", conn);
CloningScheduler cloningScheduler(
noopScheduler, conn, "MarShall Mathers", cipherOverhead);
EXPECT_FALSE(cloningScheduler.hasData());
@ -619,7 +618,7 @@ TEST_P(QuicPacketSchedulerTest, NoCloningWithOnlyD6DProbes) {
TEST_F(QuicPacketSchedulerTest, WriteOnlyOutstandingPacketsTest) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
EXPECT_FALSE(cloningScheduler.hasData());
@ -688,7 +687,7 @@ TEST_F(QuicPacketSchedulerTest, WriteOnlyOutstandingPacketsTest) {
TEST_F(QuicPacketSchedulerTest, DoNotCloneProcessedClonedPacket) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
// Add two outstanding packets, but then mark the second one processed by
// adding a PacketEvent that's missing from the outstandings.packetEvents set
@ -720,7 +719,7 @@ TEST_F(QuicPacketSchedulerTest, DoNotCloneProcessedClonedPacket) {
TEST_F(QuicPacketSchedulerTest, CloneSchedulerHasHandshakeData) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
EXPECT_FALSE(cloningScheduler.hasData());
@ -731,7 +730,7 @@ TEST_F(QuicPacketSchedulerTest, CloneSchedulerHasHandshakeData) {
TEST_F(QuicPacketSchedulerTest, CloneSchedulerHasInitialData) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
EXPECT_FALSE(cloningScheduler.hasData());
@ -742,7 +741,7 @@ TEST_F(QuicPacketSchedulerTest, CloneSchedulerHasInitialData) {
TEST_F(QuicPacketSchedulerTest, CloneSchedulerHasAppDataData) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
EXPECT_FALSE(cloningScheduler.hasData());
@ -753,7 +752,7 @@ TEST_F(QuicPacketSchedulerTest, CloneSchedulerHasAppDataData) {
TEST_F(QuicPacketSchedulerTest, DoNotCloneHandshake) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
CloningScheduler cloningScheduler(noopScheduler, conn, "CopyCat", 0);
// Add two outstanding packets, with second one being handshake
auto expected = addOutstandingPacket(conn);
@ -781,7 +780,7 @@ TEST_F(QuicPacketSchedulerTest, DoNotCloneHandshake) {
TEST_F(QuicPacketSchedulerTest, CloneSchedulerUseNormalSchedulerFirst) {
QuicClientConnectionState conn(
FizzClientQuicHandshakeContext::Builder().build());
NiceMock<MockFrameScheduler> mockScheduler;
NiceMock<MockFrameScheduler> mockScheduler(&conn);
CloningScheduler cloningScheduler(mockScheduler, conn, "Mocker", 0);
ShortHeader header(
ProtectionType::KeyPhaseOne,
@ -831,7 +830,7 @@ TEST_F(QuicPacketSchedulerTest, CloneWillGenerateNewWindowUpdate) {
FizzClientQuicHandshakeContext::Builder().build());
conn.streamManager->setMaxLocalBidirectionalStreams(10);
auto stream = conn.streamManager->createNextBidirectionalStream().value();
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
CloningScheduler cloningScheduler(noopScheduler, conn, "GiantsShoulder", 0);
PacketEvent expectedPacketEvent(
PacketNumberSpace::AppData, addOutstandingPacket(conn));
@ -917,7 +916,7 @@ TEST_F(QuicPacketSchedulerTest, CloningSchedulerWithInplaceBuilder) {
bufAccessor.release(std::move(buf));
conn.bufAccessor = &bufAccessor;
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "93MillionMiles", 0);
auto packetNum = addOutstandingPacket(conn);
@ -999,7 +998,7 @@ TEST_F(QuicPacketSchedulerTest, CloningSchedulerWithInplaceBuilderFullPacket) {
buf->clear();
bufAccessor.release(std::move(buf));
FrameScheduler noopScheduler("noopScheduler");
FrameScheduler noopScheduler("noopScheduler", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "93MillionMiles", 0);
EXPECT_TRUE(cloningScheduler.hasData());
@ -1077,7 +1076,7 @@ TEST_F(QuicPacketSchedulerTest, CloneLargerThanOriginalPacket) {
conn.udpSendPacketLen,
std::move(cloneHeader),
conn.ackStates.appDataAckState.largestAckedByPeer.value_or(0));
FrameScheduler noopScheduler("noopScheduler");
FrameScheduler noopScheduler("noopScheduler", conn);
CloningScheduler cloningScheduler(
noopScheduler, conn, "CopyCat", cipherOverhead);
auto cloneResult = cloningScheduler.scheduleFramesForPacket(
@ -1547,7 +1546,7 @@ TEST_F(
bufAccessor.release(std::move(buf));
conn.bufAccessor = &bufAccessor;
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "Little Hurry", 0);
addOutstandingPacket(conn);
@ -1591,7 +1590,7 @@ TEST_F(
bufAccessor.release(std::move(buf));
conn.bufAccessor = &bufAccessor;
FrameScheduler noopScheduler("frame");
FrameScheduler noopScheduler("frame", conn);
ASSERT_FALSE(noopScheduler.hasData());
CloningScheduler cloningScheduler(noopScheduler, conn, "HotPot", 0);
addOutstandingPacket(conn);
@ -2038,6 +2037,179 @@ TEST_F(QuicPacketSchedulerTest, DatagramFrameWriteWhenRoomAvailable) {
ASSERT_EQ(frames.size(), 1);
}
TEST_F(QuicPacketSchedulerTest, ShortHeaderPaddingWithSpaceForPadding) {
QuicServerConnectionState conn(
FizzServerQuicHandshakeContext::Builder().build());
size_t paddingModulo = 16;
conn.transportSettings.paddingModulo = paddingModulo;
// create enough input data to partially fill packet
size_t inputDataLength1 = conn.udpSendPacketLen / 2;
size_t inputDataLength2 = inputDataLength1 + 1;
auto inputData1 = buildRandomInputData(inputDataLength1);
auto inputData2 = buildRandomInputData(inputDataLength2);
FrameScheduler scheduler = std::move(FrameScheduler::Builder(
conn,
EncryptionLevel::AppData,
PacketNumberSpace::AppData,
"streamScheduler")
.streamFrames())
.build();
ShortHeader shortHeader1(
ProtectionType::KeyPhaseOne,
conn.clientConnectionId.value_or(getTestConnectionId()),
getNextPacketNum(conn, PacketNumberSpace::AppData));
ShortHeader shortHeader2(
ProtectionType::KeyPhaseOne,
conn.clientConnectionId.value_or(getTestConnectionId()),
getNextPacketNum(conn, PacketNumberSpace::AppData));
RegularQuicPacketBuilder builder1(
conn.udpSendPacketLen,
std::move(shortHeader1),
conn.ackStates.appDataAckState.largestAckedByPeer.value_or(0));
RegularQuicPacketBuilder builder2(
conn.udpSendPacketLen,
std::move(shortHeader2),
conn.ackStates.appDataAckState.largestAckedByPeer.value_or(0));
DatagramFrame frame1(inputDataLength1, std::move(inputData1));
DatagramFrame frame2(inputDataLength2, std::move(inputData2));
writeFrame(frame1, builder1);
writeFrame(frame2, builder2);
NiceMock<MockQuicStats> quicStats;
conn.statsCallback = &quicStats;
EXPECT_CALL(quicStats, onShortHeaderPadding(_)).Times(1);
auto result1 = scheduler.scheduleFramesForPacket(
std::move(builder1), conn.udpSendPacketLen);
EXPECT_CALL(quicStats, onShortHeaderPadding(_)).Times(1);
auto result2 = scheduler.scheduleFramesForPacket(
std::move(builder2), conn.udpSendPacketLen);
auto headerLength1 = result1.packet->header->computeChainDataLength();
auto bodyLength1 = result1.packet->body->computeChainDataLength();
auto packetLength1 = headerLength1 + bodyLength1;
auto expectedPadding1 =
(conn.udpSendPacketLen - (inputDataLength1 + headerLength1)) %
paddingModulo;
auto headerLength2 = result2.packet->header->computeChainDataLength();
auto bodyLength2 = result2.packet->body->computeChainDataLength();
auto packetLength2 = headerLength2 + bodyLength2;
auto expectedPadding2 =
(conn.udpSendPacketLen - (inputDataLength2 + headerLength2)) %
paddingModulo;
EXPECT_EQ(packetLength1, headerLength1 + inputDataLength1 + expectedPadding1);
EXPECT_EQ(packetLength2, headerLength2 + inputDataLength2 + expectedPadding2);
// ensure two similar size input data get padded up to same length
EXPECT_EQ(packetLength1, packetLength2);
}
TEST_F(QuicPacketSchedulerTest, ShortHeaderPaddingNearMaxPacketLength) {
QuicServerConnectionState conn(
FizzServerQuicHandshakeContext::Builder().build());
conn.udpSendPacketLen = 1000;
size_t paddingModulo = 50;
conn.transportSettings.paddingModulo = paddingModulo;
// create enough input data to almost fill packet
size_t inputDataLength = conn.udpSendPacketLen - 20;
auto inputData = buildRandomInputData(inputDataLength);
FrameScheduler scheduler = std::move(FrameScheduler::Builder(
conn,
EncryptionLevel::AppData,
PacketNumberSpace::AppData,
"streamScheduler")
.streamFrames())
.build();
ShortHeader shortHeader(
ProtectionType::KeyPhaseOne,
conn.clientConnectionId.value_or(getTestConnectionId()),
getNextPacketNum(conn, PacketNumberSpace::AppData));
RegularQuicPacketBuilder builder(
conn.udpSendPacketLen,
std::move(shortHeader),
conn.ackStates.appDataAckState.largestAckedByPeer.value_or(0));
DatagramFrame frame(inputDataLength, std::move(inputData));
writeFrame(frame, builder);
NiceMock<MockQuicStats> quicStats;
conn.statsCallback = &quicStats;
EXPECT_CALL(quicStats, onShortHeaderPadding(_)).Times(1);
auto result = scheduler.scheduleFramesForPacket(
std::move(builder), conn.udpSendPacketLen);
auto headerLength = result.packet->header->computeChainDataLength();
auto bodyLength = result.packet->body->computeChainDataLength();
auto packetLength = headerLength + bodyLength;
auto expectedPadding =
(conn.udpSendPacketLen - (inputDataLength + headerLength)) %
paddingModulo;
EXPECT_EQ(packetLength, headerLength + inputDataLength + expectedPadding);
EXPECT_EQ(packetLength, conn.udpSendPacketLen);
}
TEST_F(QuicPacketSchedulerTest, ShortHeaderPaddingMaxPacketLength) {
QuicServerConnectionState conn(
FizzServerQuicHandshakeContext::Builder().build());
conn.udpSendPacketLen = 1000;
size_t paddingModulo = 50;
conn.transportSettings.paddingModulo = paddingModulo;
FrameScheduler scheduler = std::move(FrameScheduler::Builder(
conn,
EncryptionLevel::AppData,
PacketNumberSpace::AppData,
"streamScheduler")
.streamFrames())
.build();
ShortHeader shortHeader(
ProtectionType::KeyPhaseOne,
conn.clientConnectionId.value_or(getTestConnectionId()),
getNextPacketNum(conn, PacketNumberSpace::AppData));
size_t largestAckedPacketNum =
conn.ackStates.appDataAckState.largestAckedByPeer.value_or(0);
auto packetNumberEncoding = encodePacketNumber(
shortHeader.getPacketSequenceNum(), largestAckedPacketNum);
auto connectionIdSize = shortHeader.getConnectionId().size();
RegularQuicPacketBuilder builder(
conn.udpSendPacketLen, std::move(shortHeader), largestAckedPacketNum);
// create enough input data to fully fill packet
while (builder.remainingSpaceInPkt() >
connectionIdSize + packetNumberEncoding.length + 1) {
writeFrame(PaddingFrame(), builder);
}
NiceMock<MockQuicStats> quicStats;
conn.statsCallback = &quicStats;
EXPECT_CALL(quicStats, onShortHeaderPadding(_)).Times(1);
auto result = scheduler.scheduleFramesForPacket(
std::move(builder), conn.udpSendPacketLen);
auto headerLength = result.packet->header->computeChainDataLength();
auto bodyLength = result.packet->body->computeChainDataLength();
auto packetLength = headerLength + bodyLength;
EXPECT_EQ(packetLength, conn.udpSendPacketLen);
}
INSTANTIATE_TEST_CASE_P(
QuicPacketSchedulerTests,
QuicPacketSchedulerTest,