From 622d9d793f4e71ae5352534d281def1280fcb4f7 Mon Sep 17 00:00:00 2001 From: Joseph Beshay Date: Mon, 24 Feb 2025 12:32:50 -0800 Subject: [PATCH] ACK_EXTENDED frame support Summary: This introduces a new frame type for acks (ACK_EXTENDED) that can carry optional fields depending on the features supported by the peer. The currently supported features set will include ECN count fields, and Receive Timstamp fields. This enables a quic connection to report both ECN counts and receive timestamps, which is not possible otherwise because they use different frame types. Support for the extended ack as well as the set of features that can be included in it is negotiated through a new transport parameter (extended_ack_supported = 0xff0a004). Its value indicates which features are supported by the local transport. The value is an integer which is evaluated against the following bitmasks: ``` ECN_COUNTS = 0x01, RECEIVE_TIMESTAMPS = 0x02, ``` This diff introduces the transport parameter and negotiates the supported features between the peers of the connection. The parameter is cached in the psk cache so the client can remember the server config. It is also encoded inside the 0-rtt ticket so the server can reject it if its local config has changed. The following diffs add reading and writing the frame itself. The ACK_EXTENDED frame itself will have the following format ``` ACK_EXTENDED Frame { Type (i) = 0xB1 // Fields of the existing ACK (type=0x02) frame: Largest Acknowledged (i), ACK Delay (i), ACK Range Count (i), First ACK Range (i), ACK Range (..) ..., Extended Ack Features (i), // Optional ECN counts (if bit 0 is set in Features) [ECN Counts (..)], // Optional Receive Timestamps (if bit 1 is set in Features) [Receive Timestamps (..)] } // Fields from the existing ACK_ECN frame ECN Counts { ECT0 Count (i), ECT1 Count (i), ECN-CE Count (i), } // Fields from the existing ACK_RECEIVE_TIMESTAMPS frame Receive Timestamps { Timestamp Range Count (i), Timestamp Ranges (..) ..., } Timestamp Range { Gap (i), Timestamp Delta Count (i), Timestamp Delta (i) ..., } ``` Reviewed By: sharmafb Differential Revision: D68931151 fbshipit-source-id: 44c8c83d2f434abca97c4e85f0fa7502736cddc1 --- proxygen/httpserver/samples/hq/HQCommandLine.cpp | 11 +++++++++++ proxygen/lib/transport/PersistentQuicPskCache.cpp | 4 ++++ .../lib/transport/test/PersistentQuicPskCacheTest.cpp | 9 ++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/proxygen/httpserver/samples/hq/HQCommandLine.cpp b/proxygen/httpserver/samples/hq/HQCommandLine.cpp index 06441d72d..50e54a58c 100644 --- a/proxygen/httpserver/samples/hq/HQCommandLine.cpp +++ b/proxygen/httpserver/samples/hq/HQCommandLine.cpp @@ -121,6 +121,14 @@ DEFINE_uint32( max_ack_receive_timestamps_to_send, quic::kMaxReceivedPktsTimestampsStored, "Controls how many packet receieve timestamps the peer should send"); +DEFINE_uint32( + extended_ack_features, + 0, + "Replace the ACK frame with ACK_EXTENDED with the following features." + "The bitwise values can be ORed together." + "bit 1 - ECN support" + "bit 2 - Receive timestamps support" + "Example: 3 means both ECN and receive timestamps are supported"); DEFINE_bool(initiate_key_updates, false, "Whether to initiate periodic key updates"); @@ -306,6 +314,9 @@ void initializeTransportSettings(HQToolParams& hqUberParams) { hqParams.transportSettings.dscpValue = FLAGS_dscp; hqParams.transportSettings.disableMigration = false; + + hqParams.transportSettings.advertisedExtendedAckFeatures = + FLAGS_extended_ack_features; } // initializeTransportSettings void initializeHttpServerSettings(HQToolServerParams& hqParams) { diff --git a/proxygen/lib/transport/PersistentQuicPskCache.cpp b/proxygen/lib/transport/PersistentQuicPskCache.cpp index 299294969..b11b44853 100644 --- a/proxygen/lib/transport/PersistentQuicPskCache.cpp +++ b/proxygen/lib/transport/PersistentQuicPskCache.cpp @@ -76,6 +76,8 @@ folly::Optional PersistentQuicPskCache::getPsk( cursor); fizz::detail::read(quicCachedPsk.transportParams.receiveTimestampsExponent, cursor); + fizz::detail::read(quicCachedPsk.transportParams.extendedAckFeatures, + cursor); std::unique_ptr appParams; fizz::detail::readBuf(appParams, cursor); @@ -126,6 +128,8 @@ void PersistentQuicPskCache::putPsk(const std::string& identity, appender); fizz::detail::write(quicCachedPsk.transportParams.receiveTimestampsExponent, appender); + fizz::detail::write(quicCachedPsk.transportParams.extendedAckFeatures, + appender); fizz::detail::writeBuf( folly::IOBuf::wrapBuffer(folly::StringPiece(quicCachedPsk.appParams)), diff --git a/proxygen/lib/transport/test/PersistentQuicPskCacheTest.cpp b/proxygen/lib/transport/test/PersistentQuicPskCacheTest.cpp index 1fd13e24d..759cdc2e0 100644 --- a/proxygen/lib/transport/test/PersistentQuicPskCacheTest.cpp +++ b/proxygen/lib/transport/test/PersistentQuicPskCacheTest.cpp @@ -60,6 +60,7 @@ class PersistentQuicPskCacheTest : public Test { quicPsk1_.transportParams.ackReceiveTimestampsEnabled = true; quicPsk1_.transportParams.maxReceiveTimestampsPerAck = 30; quicPsk1_.transportParams.receiveTimestampsExponent = 0; + quicPsk1_.transportParams.extendedAckFeatures = 3; quicPsk1_.appParams = "QPACK param"; auto& fizzPsk2 = quicPsk2_.cachedPsk; @@ -88,9 +89,10 @@ class PersistentQuicPskCacheTest : public Test { quicPsk2_.transportParams.initialMaxStreamsBidi = 2345; quicPsk2_.transportParams.initialMaxStreamsUni = 1233; quicPsk2_.transportParams.knobFrameSupport = false; - quicPsk1_.transportParams.ackReceiveTimestampsEnabled = false; - quicPsk1_.transportParams.maxReceiveTimestampsPerAck = 10; - quicPsk1_.transportParams.receiveTimestampsExponent = 0; + quicPsk2_.transportParams.ackReceiveTimestampsEnabled = false; + quicPsk2_.transportParams.maxReceiveTimestampsPerAck = 10; + quicPsk2_.transportParams.receiveTimestampsExponent = 0; + quicPsk2_.transportParams.extendedAckFeatures = 0; } void TearDown() override { @@ -154,6 +156,7 @@ static void expectMatch(const QuicCachedPsk& a, const QuicCachedPsk& b) { paramsB.maxReceiveTimestampsPerAck); EXPECT_EQ(paramsA.receiveTimestampsExponent, paramsB.receiveTimestampsExponent); + EXPECT_EQ(paramsA.extendedAckFeatures, paramsB.extendedAckFeatures); EXPECT_EQ(a.appParams, b.appParams); }