diff --git a/quic/QuicConstants.h b/quic/QuicConstants.h index 5025469c8..b9ec808c2 100644 --- a/quic/QuicConstants.h +++ b/quic/QuicConstants.h @@ -22,6 +22,7 @@ using DurationRep = std::chrono::microseconds::rep; using namespace std::chrono_literals; // Default QUIC packet size for both read and write. +// TODO(xtt): make them configurable constexpr uint64_t kDefaultV4UDPSendPacketLen = 1252; constexpr uint64_t kDefaultV6UDPSendPacketLen = 1232; // With Android NDK r15c for some apps we use gnu-libstdc++ instead of diff --git a/quic/api/QuicTransportFunctions.cpp b/quic/api/QuicTransportFunctions.cpp index 8516c936c..44a161239 100644 --- a/quic/api/QuicTransportFunctions.cpp +++ b/quic/api/QuicTransportFunctions.cpp @@ -1341,6 +1341,49 @@ uint64_t writeProbingDataToSocket( return written; } +uint64_t writeD6DProbeToSocket( + folly::AsyncUDPSocket& sock, + QuicConnectionStateBase& connection, + const ConnectionId& srcConnId, + const ConnectionId& dstConnId, + const Aead& aead, + const PacketNumberCipher& headerCipher, + QuicVersion version) { + if (!connection.pendingEvents.sendD6DProbePacket) { + return 0; + } + auto builder = ShortHeaderBuilder(); + // D6D probe is always in AppData pnSpace + auto pnSpace = PacketNumberSpace::AppData; + // Skip a packet number for probing packets to elicit acks + increaseNextPacketNum(connection, pnSpace); + D6DProbeScheduler d6dProbeScheduler( + connection, + "D6DProbeScheduler", + aead.getCipherOverhead(), + connection.d6d.currentProbeSize); + auto written = writeConnectionDataToSocket( + sock, + connection, + srcConnId, + dstConnId, + builder, + pnSpace, + d6dProbeScheduler, + unlimitedWritableBytes, + 1, + aead, + headerCipher, + version); + VLOG_IF(10, written > 0) << nodeToString(connection.nodeType) + << " writing d6d probes using scheduler=D6DScheduler" + << connection; + if (written > 0) { + connection.pendingEvents.sendD6DProbePacket = false; + } + return written; +} + WriteDataReason shouldWriteData(const QuicConnectionStateBase& conn) { if (conn.pendingEvents.numProbePackets) { VLOG(10) << nodeToString(conn.nodeType) << " needs write because of PTO" diff --git a/quic/api/QuicTransportFunctions.h b/quic/api/QuicTransportFunctions.h index c2ac1529d..a254d9dbf 100644 --- a/quic/api/QuicTransportFunctions.h +++ b/quic/api/QuicTransportFunctions.h @@ -282,6 +282,15 @@ uint64_t writeProbingDataToSocket( QuicVersion version, const std::string& token = std::string()); +uint64_t writeD6DProbeToSocket( + folly::AsyncUDPSocket& sock, + QuicConnectionStateBase& connection, + const ConnectionId& srcConnId, + const ConnectionId& dstConnId, + const Aead& aead, + const PacketNumberCipher& headerCipher, + QuicVersion version); + HeaderBuilder LongHeaderBuilder(LongHeader::Types packetType); HeaderBuilder ShortHeaderBuilder(); diff --git a/quic/api/test/QuicTransportFunctionsTest.cpp b/quic/api/test/QuicTransportFunctionsTest.cpp index 1ea90d5d1..304a1fe6f 100644 --- a/quic/api/test/QuicTransportFunctionsTest.cpp +++ b/quic/api/test/QuicTransportFunctionsTest.cpp @@ -2519,5 +2519,42 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) { EXPECT_EQ(conn->outstandings.packets.size(), outstandingPacketsCount + 3); } +TEST_F(QuicTransportFunctionsTest, WriteD6DProbesWithInplaceBuilder) { + auto conn = createConn(); + conn->transportSettings.dataPathType = DataPathType::ContinuousMemory; + conn->d6d.currentProbeSize = 1450; + conn->pendingEvents.sendD6DProbePacket = true; + auto simpleBufAccessor = + std::make_unique(kDefaultMaxUDPPayload * 16); + auto outputBuf = simpleBufAccessor->obtain(); + auto bufPtr = outputBuf.get(); + simpleBufAccessor->release(std::move(outputBuf)); + conn->bufAccessor = simpleBufAccessor.get(); + conn->transportSettings.batchingMode = QuicBatchingMode::BATCHING_MODE_GSO; + EventBase evb; + folly::test::MockAsyncUDPSocket mockSock(&evb); + EXPECT_CALL(mockSock, getGSO()).WillRepeatedly(Return(true)); + EXPECT_CALL(mockSock, write(_, _)) + .Times(1) + .WillOnce(Invoke([&](const SocketAddress&, + const std::unique_ptr& sockBuf) { + EXPECT_EQ(sockBuf->length(), conn->d6d.currentProbeSize); + EXPECT_EQ(sockBuf.get(), bufPtr); + EXPECT_TRUE(folly::IOBufEqualTo()(*sockBuf, *bufPtr)); + EXPECT_FALSE(sockBuf->isChained()); + return sockBuf->computeChainDataLength(); + })); + writeD6DProbeToSocket( + mockSock, + *conn, + *conn->clientConnectionId, + *conn->serverConnectionId, + *aead, + *headerCipher, + getVersion(*conn)); + EXPECT_EQ(0, bufPtr->length()); + EXPECT_EQ(0, bufPtr->headroom()); +} + } // namespace test } // namespace quic diff --git a/quic/state/StateData.h b/quic/state/StateData.h index 890694739..b4918578e 100644 --- a/quic/state/StateData.h +++ b/quic/state/StateData.h @@ -626,6 +626,9 @@ struct QuicConnectionStateBase : public folly::DelayedDestruction { // To send a ping frame bool sendPing{false}; + // To send a d6d probe packet + bool sendD6DProbePacket{false}; + // Do we need to send data blocked frame when connection is blocked. bool sendDataBlocked{false}; @@ -765,6 +768,9 @@ struct QuicConnectionStateBase : public folly::DelayedDestruction { // The base PMTU to start probing with uint16_t basePMTU{kDefaultD6DBasePMTU}; + // Current probe size + uint32_t currentProbeSize{kDefaultD6DBasePMTU}; + // The raise timeout std::chrono::seconds raiseTimeout{kDefaultD6DRaiseTimeout}; };