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

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
This commit is contained in:
Joseph Beshay
2025-02-24 12:32:50 -08:00
committed by Facebook GitHub Bot
parent 466d304058
commit b45c82b884
24 changed files with 277 additions and 57 deletions

View File

@@ -262,13 +262,23 @@ enum class FrameType : uint64_t {
GROUP_STREAM_OFF_FIN = 0x37, GROUP_STREAM_OFF_FIN = 0x37,
GROUP_STREAM_OFF_LEN = 0x38, GROUP_STREAM_OFF_LEN = 0x38,
GROUP_STREAM_OFF_LEN_FIN = 0x39, GROUP_STREAM_OFF_LEN_FIN = 0x39,
ACK_RECEIVE_TIMESTAMPS = 0xB0 ACK_RECEIVE_TIMESTAMPS = 0xB0,
ACK_EXTENDED = 0xB1
}; };
inline constexpr uint16_t toFrameError(FrameType frame) { inline constexpr uint16_t toFrameError(FrameType frame) {
return 0x0100 | static_cast<uint8_t>(frame); return 0x0100 | static_cast<uint8_t>(frame);
} }
enum class ExtendedAckFeatureMask : uint8_t {
// These should use mutually exclusive bits.
ECN_COUNTS = 0x01,
RECEIVE_TIMESTAMPS = 0x02,
};
using ExtendedAckFeatureMaskType =
std::underlying_type<ExtendedAckFeatureMask>::type;
enum class TransportErrorCode : uint64_t { enum class TransportErrorCode : uint64_t {
NO_ERROR = 0x0000, NO_ERROR = 0x0000,
INTERNAL_ERROR = 0x0001, INTERNAL_ERROR = 0x0001,

View File

@@ -462,6 +462,21 @@ void QuicClientTransportLite::processUdpPacketData(
VLOG(10) << "Client received ack frame in packet=" << packetNum << " " VLOG(10) << "Client received ack frame in packet=" << packetNum << " "
<< *this; << *this;
ReadAckFrame& ackFrame = *quicFrame.asReadAckFrame(); ReadAckFrame& ackFrame = *quicFrame.asReadAckFrame();
if (ackFrame.frameType == FrameType::ACK_EXTENDED &&
!conn_->transportSettings.advertisedExtendedAckFeatures) {
throw QuicTransportException(
"Received unexpected ACK_EXTENDED frame",
TransportErrorCode::PROTOCOL_VIOLATION);
} else if (
ackFrame.frameType == FrameType::ACK_RECEIVE_TIMESTAMPS &&
!conn_->transportSettings
.maybeAckReceiveTimestampsConfigSentToPeer) {
throw QuicTransportException(
"Received unexpected ACK_RECEIVE_TIMESTAMPS frame",
TransportErrorCode::PROTOCOL_VIOLATION);
}
conn_->lastProcessedAckEvents.emplace_back(processAckFrame( conn_->lastProcessedAckEvents.emplace_back(processAckFrame(
*conn_, *conn_,
pnSpace, pnSpace,
@@ -754,7 +769,8 @@ void QuicClientTransportLite::processUdpPacketData(
? conn_->maybePeerAckReceiveTimestampsConfig ? conn_->maybePeerAckReceiveTimestampsConfig
->receiveTimestampsExponent ->receiveTimestampsExponent
: 3, : 3,
conn_->peerAdvertisedReliableStreamResetSupport); conn_->peerAdvertisedReliableStreamResetSupport,
conn_->peerAdvertisedExtendedAckFeatures);
if (clientConn_->zeroRttRejected.has_value() && if (clientConn_->zeroRttRejected.has_value() &&
*clientConn_->zeroRttRejected) { *clientConn_->zeroRttRejected) {

View File

@@ -24,6 +24,8 @@ struct CachedServerTransportParameters {
uint64_t initialMaxStreamsUni{0}; uint64_t initialMaxStreamsUni{0};
uint64_t maxReceiveTimestampsPerAck{0}; uint64_t maxReceiveTimestampsPerAck{0};
uint64_t receiveTimestampsExponent{0}; uint64_t receiveTimestampsExponent{0};
// Underlying type is currently uint8_t so this struct is still packed
ExtendedAckFeatureMaskType extendedAckFeatures{0};
bool knobFrameSupport{false}; bool knobFrameSupport{false};
bool ackReceiveTimestampsEnabled{false}; bool ackReceiveTimestampsEnabled{false};
bool reliableStreamResetSupport{false}; bool reliableStreamResetSupport{false};

View File

@@ -46,7 +46,8 @@ void ClientHandshake::connect(
cachedServerTransportParams->ackReceiveTimestampsEnabled, cachedServerTransportParams->ackReceiveTimestampsEnabled,
cachedServerTransportParams->maxReceiveTimestampsPerAck, cachedServerTransportParams->maxReceiveTimestampsPerAck,
cachedServerTransportParams->receiveTimestampsExponent, cachedServerTransportParams->receiveTimestampsExponent,
cachedServerTransportParams->reliableStreamResetSupport); cachedServerTransportParams->reliableStreamResetSupport,
cachedServerTransportParams->extendedAckFeatures);
updateTransportParamsFromCachedEarlyParams( updateTransportParamsFromCachedEarlyParams(
*conn_, *cachedServerTransportParams); *conn_, *cachedServerTransportParams);
} }

View File

@@ -151,6 +151,10 @@ void processServerInitialParams(
static_cast<TransportParameterId>( static_cast<TransportParameterId>(
TransportParameterId::knob_frames_supported), TransportParameterId::knob_frames_supported),
serverParams.parameters); serverParams.parameters);
auto extendedAckFeatures = getIntegerParameter(
static_cast<TransportParameterId>(
TransportParameterId::extended_ack_features),
serverParams.parameters);
auto reliableResetTpIter = findParameter( auto reliableResetTpIter = findParameter(
serverParams.parameters, serverParams.parameters,
@@ -286,6 +290,7 @@ void processServerInitialParams(
} }
conn.peerAdvertisedKnobFrameSupport = knobFrameSupported.value_or(0) > 0; conn.peerAdvertisedKnobFrameSupport = knobFrameSupported.value_or(0) > 0;
conn.peerAdvertisedExtendedAckFeatures = extendedAckFeatures.value_or(0);
} }
void cacheServerInitialParams( void cacheServerInitialParams(
@@ -300,7 +305,8 @@ void cacheServerInitialParams(
bool peerAdvertisedAckReceiveTimestampsEnabled, bool peerAdvertisedAckReceiveTimestampsEnabled,
uint64_t peerAdvertisedMaxReceiveTimestampsPerAck, uint64_t peerAdvertisedMaxReceiveTimestampsPerAck,
uint64_t peerAdvertisedReceiveTimestampsExponent, uint64_t peerAdvertisedReceiveTimestampsExponent,
bool peerAdvertisedReliableStreamResetSupport) { bool peerAdvertisedReliableStreamResetSupport,
ExtendedAckFeatureMaskType peerAdvertisedExtendedAckFeatures) {
conn.serverInitialParamsSet_ = true; conn.serverInitialParamsSet_ = true;
conn.peerAdvertisedInitialMaxData = peerAdvertisedInitialMaxData; conn.peerAdvertisedInitialMaxData = peerAdvertisedInitialMaxData;
conn.peerAdvertisedInitialMaxStreamDataBidiLocal = conn.peerAdvertisedInitialMaxStreamDataBidiLocal =
@@ -328,6 +334,7 @@ void cacheServerInitialParams(
} else { } else {
conn.maybePeerAckReceiveTimestampsConfig.clear(); conn.maybePeerAckReceiveTimestampsConfig.clear();
} }
conn.peerAdvertisedExtendedAckFeatures = peerAdvertisedExtendedAckFeatures;
} }
CachedServerTransportParameters getServerCachedTransportParameters( CachedServerTransportParameters getServerCachedTransportParameters(
@@ -360,6 +367,7 @@ CachedServerTransportParameters getServerCachedTransportParameters(
transportParams.receiveTimestampsExponent = transportParams.receiveTimestampsExponent =
conn.maybePeerAckReceiveTimestampsConfig->receiveTimestampsExponent; conn.maybePeerAckReceiveTimestampsConfig->receiveTimestampsExponent;
} }
transportParams.extendedAckFeatures = conn.peerAdvertisedExtendedAckFeatures;
return transportParams; return transportParams;
} }
@@ -395,5 +403,6 @@ void updateTransportParamsFromCachedEarlyParams(
} else { } else {
conn.maybePeerAckReceiveTimestampsConfig.clear(); conn.maybePeerAckReceiveTimestampsConfig.clear();
} }
conn.peerAdvertisedExtendedAckFeatures = transportParams.extendedAckFeatures;
} }
} // namespace quic } // namespace quic

View File

@@ -158,7 +158,8 @@ void cacheServerInitialParams(
bool peerAdvertisedAckReceiveTimestampsEnabled, bool peerAdvertisedAckReceiveTimestampsEnabled,
uint64_t peerAdvertisedMaxRecvTimestampsPerAck, uint64_t peerAdvertisedMaxRecvTimestampsPerAck,
uint64_t peerAdvertisedReceiveTimestampsExponent, uint64_t peerAdvertisedReceiveTimestampsExponent,
bool peerAdvertisedReliableStreamResetSupport); bool peerAdvertisedReliableStreamResetSupport,
ExtendedAckFeatureMaskType peerAdvertisedExtendedAckSupport);
CachedServerTransportParameters getServerCachedTransportParameters( CachedServerTransportParameters getServerCachedTransportParameters(
const QuicClientConnectionState& conn); const QuicClientConnectionState& conn);

View File

@@ -32,6 +32,7 @@ constexpr auto initialMaxStreamDataUni = kDefaultStreamFlowControlWindow + 5;
constexpr auto initialMaxStreamsBidi = kDefaultMaxStreamsBidirectional + 6; constexpr auto initialMaxStreamsBidi = kDefaultMaxStreamsBidirectional + 6;
constexpr auto initialMaxStreamsUni = kDefaultMaxStreamsUnidirectional + 7; constexpr auto initialMaxStreamsUni = kDefaultMaxStreamsUnidirectional + 7;
constexpr auto knobFrameSupport = true; constexpr auto knobFrameSupport = true;
constexpr auto extendedAckSupport = 3;
constexpr auto ackReceiveTimestampsEnabled = true; constexpr auto ackReceiveTimestampsEnabled = true;
constexpr auto maxReceiveTimestampsPerAck = 10; constexpr auto maxReceiveTimestampsPerAck = 10;
constexpr auto ackReceiveTimestampsExponent = 0; constexpr auto ackReceiveTimestampsExponent = 0;
@@ -46,6 +47,7 @@ const CachedServerTransportParameters kParams{
initialMaxStreamsUni, initialMaxStreamsUni,
maxReceiveTimestampsPerAck, maxReceiveTimestampsPerAck,
ackReceiveTimestampsExponent, ackReceiveTimestampsExponent,
extendedAckSupport,
knobFrameSupport, knobFrameSupport,
ackReceiveTimestampsEnabled}; ackReceiveTimestampsEnabled};
} // namespace } // namespace
@@ -78,6 +80,7 @@ TEST_F(ClientStateMachineTest, TestUpdateTransportParamsNotIgnorePathMTU) {
TEST_F(ClientStateMachineTest, TestUpdateTransportParamsFromCachedEarlyParams) { TEST_F(ClientStateMachineTest, TestUpdateTransportParamsFromCachedEarlyParams) {
client_->transportSettings.canIgnorePathMTU = true; client_->transportSettings.canIgnorePathMTU = true;
client_->peerAdvertisedKnobFrameSupport = false; client_->peerAdvertisedKnobFrameSupport = false;
client_->peerAdvertisedExtendedAckFeatures = 0;
client_->maybePeerAckReceiveTimestampsConfig.assign( client_->maybePeerAckReceiveTimestampsConfig.assign(
{.maxReceiveTimestampsPerAck = 10, .receiveTimestampsExponent = 0}); {.maxReceiveTimestampsPerAck = 10, .receiveTimestampsExponent = 0});
@@ -95,6 +98,7 @@ TEST_F(ClientStateMachineTest, TestUpdateTransportParamsFromCachedEarlyParams) {
client_->flowControlState.peerAdvertisedInitialMaxStreamOffsetUni, client_->flowControlState.peerAdvertisedInitialMaxStreamOffsetUni,
initialMaxStreamDataUni); initialMaxStreamDataUni);
EXPECT_EQ(client_->peerAdvertisedKnobFrameSupport, knobFrameSupport); EXPECT_EQ(client_->peerAdvertisedKnobFrameSupport, knobFrameSupport);
EXPECT_EQ(client_->peerAdvertisedExtendedAckFeatures, extendedAckSupport);
ASSERT_TRUE(client_->maybePeerAckReceiveTimestampsConfig.has_value()); ASSERT_TRUE(client_->maybePeerAckReceiveTimestampsConfig.has_value());
EXPECT_EQ( EXPECT_EQ(
client_->maybePeerAckReceiveTimestampsConfig.value() client_->maybePeerAckReceiveTimestampsConfig.value()
@@ -234,6 +238,28 @@ TEST_F(ClientStateMachineTest, TestProcessKnobFramesSupportedParamDisabled) {
EXPECT_FALSE(clientConn.peerAdvertisedKnobFrameSupport); EXPECT_FALSE(clientConn.peerAdvertisedKnobFrameSupport);
} }
TEST_F(ClientStateMachineTest, TestProcessExtendedAckSupportedParam) {
QuicClientConnectionState clientConn(
FizzClientQuicHandshakeContext::Builder().build());
std::vector<TransportParameter> transportParams;
transportParams.push_back(
encodeIntegerParameter(TransportParameterId::extended_ack_features, 3));
ServerTransportParameters serverTransportParams = {
std::move(transportParams)};
processServerInitialParams(clientConn, serverTransportParams, 0);
EXPECT_EQ(clientConn.peerAdvertisedExtendedAckFeatures, 3);
}
TEST_F(ClientStateMachineTest, TestProcessExtendedAckSupportedParamDefault) {
QuicClientConnectionState clientConn(
FizzClientQuicHandshakeContext::Builder().build());
std::vector<TransportParameter> transportParams;
ServerTransportParameters serverTransportParams = {
std::move(transportParams)};
processServerInitialParams(clientConn, serverTransportParams, 0);
EXPECT_EQ(clientConn.peerAdvertisedExtendedAckFeatures, 0);
}
TEST_F( TEST_F(
ClientStateMachineTest, ClientStateMachineTest,
TestProcessReliableStreamResetSupportedParamEnabled) { TestProcessReliableStreamResetSupportedParamEnabled) {

View File

@@ -865,11 +865,17 @@ QuicFrame parseFrame(
return QuicFrame(decodeAckFrequencyFrame(cursor)); return QuicFrame(decodeAckFrequencyFrame(cursor));
case FrameType::IMMEDIATE_ACK: case FrameType::IMMEDIATE_ACK:
return QuicFrame(decodeImmediateAckFrame(cursor)); return QuicFrame(decodeImmediateAckFrame(cursor));
case FrameType::ACK_RECEIVE_TIMESTAMPS: case FrameType::ACK_RECEIVE_TIMESTAMPS: {
auto frame = QuicFrame(decodeAckFrameWithReceivedTimestamps( auto frame = QuicFrame(decodeAckFrameWithReceivedTimestamps(
cursor, header, params, FrameType::ACK_RECEIVE_TIMESTAMPS)); cursor, header, params, FrameType::ACK_RECEIVE_TIMESTAMPS));
return frame; return frame;
} }
case FrameType::ACK_EXTENDED:
throw QuicTransportException(
folly::to<std::string>(
"ACK_EXTENDED not yet supported, type=", frameTypeInt->first),
TransportErrorCode::FRAME_ENCODING_ERROR);
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
error = true; error = true;
throw QuicTransportException( throw QuicTransportException(

View File

@@ -457,6 +457,8 @@ std::string_view toString(FrameType frame) {
return "GROUP_STREAM"; return "GROUP_STREAM";
case FrameType::ACK_RECEIVE_TIMESTAMPS: case FrameType::ACK_RECEIVE_TIMESTAMPS:
return "ACK_RECEIVE_TIMESTAMPS"; return "ACK_RECEIVE_TIMESTAMPS";
case quic::FrameType::ACK_EXTENDED:
return "ACK_EXTENDED";
} }
LOG(WARNING) << "toString has unhandled frame type"; LOG(WARNING) << "toString has unhandled frame type";
return "UNKNOWN"; return "UNKNOWN";

View File

@@ -183,7 +183,8 @@ class AcceptingTicketCipher : public fizz::server::TicketCipher {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional, kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional); kDefaultMaxStreamsUnidirectional,
0 /*extendedAckSupport*/);
appToken.version = QuicVersion::MVFST; appToken.version = QuicVersion::MVFST;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
return resState; return resState;

View File

@@ -129,6 +129,11 @@ std::vector<TransportParameter> getSupportedExtTransportParams(
customTps.push_back(encodeEmptyParameter(TpId::reliable_stream_reset)); customTps.push_back(encodeEmptyParameter(TpId::reliable_stream_reset));
} }
if (ts.advertisedExtendedAckFeatures) {
customTps.push_back(encodeIntegerParameter(
TpId::extended_ack_features, ts.advertisedExtendedAckFeatures));
}
return customTps; return customTps;
} }

View File

@@ -39,6 +39,7 @@ enum class TransportParameterId : uint64_t {
ack_receive_timestamps_enabled = 0xff0a001, ack_receive_timestamps_enabled = 0xff0a001,
max_receive_timestamps_per_ack = 0xff0a002, max_receive_timestamps_per_ack = 0xff0a002,
receive_timestamps_exponent = 0xff0a003, receive_timestamps_exponent = 0xff0a003,
extended_ack_features = 0xff0a004,
stream_groups_enabled = 0x0000ff99, stream_groups_enabled = 0x0000ff99,
knob_frames_supported = 0x00005178, knob_frames_supported = 0x00005178,
cwnd_hint_bytes = 0x00007492, cwnd_hint_bytes = 0x00007492,

View File

@@ -94,6 +94,8 @@ folly::StringPiece toQlogString(FrameType frame) {
return "group_stream"; return "group_stream";
case FrameType::ACK_RECEIVE_TIMESTAMPS: case FrameType::ACK_RECEIVE_TIMESTAMPS:
return "ack_receive_timestamps"; return "ack_receive_timestamps";
case FrameType::ACK_EXTENDED:
return "ack_extended";
} }
folly::assume_unreachable(); folly::assume_unreachable();
} }

View File

@@ -224,11 +224,13 @@ folly::dynamic ReadAckFrameLog::toDynamic() const {
} }
d["acked_ranges"] = ackRangeDynamic; d["acked_ranges"] = ackRangeDynamic;
d["frame_type"] = toQlogString(frameType); d["frame_type"] = toQlogString(frameType);
if (frameType == FrameType::ACK_ECN) { if (frameType == FrameType::ACK_EXTENDED || frameType == FrameType::ACK_ECN) {
d["ecn_ect0"] = ecnECT0Count; d["ecn_ect0"] = ecnECT0Count;
d["ecn_ect1"] = ecnECT1Count; d["ecn_ect1"] = ecnECT1Count;
d["ecn_ce"] = ecnCECount; d["ecn_ce"] = ecnCECount;
} else if (frameType == FrameType::ACK_RECEIVE_TIMESTAMPS) { }
if (frameType == FrameType::ACK_EXTENDED ||
frameType == FrameType::ACK_RECEIVE_TIMESTAMPS) {
if (maybeLatestRecvdPacketTime.has_value()) { if (maybeLatestRecvdPacketTime.has_value()) {
d["latest_recvd_packet_time"] = d["latest_recvd_packet_time"] =
maybeLatestRecvdPacketTime.value().count(); maybeLatestRecvdPacketTime.value().count();

View File

@@ -564,6 +564,7 @@ void QuicServerTransport::maybeWriteNewSessionTicket() {
conn_->transportSettings.advertisedInitialUniStreamFlowControlWindow, conn_->transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn_->transportSettings.advertisedInitialMaxStreamsBidi, conn_->transportSettings.advertisedInitialMaxStreamsBidi,
conn_->transportSettings.advertisedInitialMaxStreamsUni, conn_->transportSettings.advertisedInitialMaxStreamsUni,
conn_->transportSettings.advertisedExtendedAckFeatures,
cwndHint); cwndHint);
appToken.sourceAddresses = serverConn_->tokenSourceAddresses; appToken.sourceAddresses = serverConn_->tokenSourceAddresses;
appToken.version = conn_->version.value(); appToken.version = conn_->version.value();

View File

@@ -18,6 +18,7 @@ TicketTransportParameters createTicketTransportParameters(
uint64_t initialMaxStreamDataUni, uint64_t initialMaxStreamDataUni,
uint64_t initialMaxStreamsBidi, uint64_t initialMaxStreamsBidi,
uint64_t initialMaxStreamsUni, uint64_t initialMaxStreamsUni,
ExtendedAckFeatureMaskType extendedAckFeatures,
Optional<uint64_t> cwndHintBytes) { Optional<uint64_t> cwndHintBytes) {
TicketTransportParameters params; TicketTransportParameters params;
params.parameters.push_back( params.parameters.push_back(
@@ -39,6 +40,8 @@ TicketTransportParameters createTicketTransportParameters(
TransportParameterId::initial_max_streams_bidi, initialMaxStreamsBidi)); TransportParameterId::initial_max_streams_bidi, initialMaxStreamsBidi));
params.parameters.push_back(encodeIntegerParameter( params.parameters.push_back(encodeIntegerParameter(
TransportParameterId::initial_max_streams_uni, initialMaxStreamsUni)); TransportParameterId::initial_max_streams_uni, initialMaxStreamsUni));
params.parameters.push_back(encodeIntegerParameter(
TransportParameterId::extended_ack_features, extendedAckFeatures));
if (cwndHintBytes) { if (cwndHintBytes) {
params.parameters.push_back((encodeIntegerParameter( params.parameters.push_back((encodeIntegerParameter(
TransportParameterId::cwnd_hint_bytes, *cwndHintBytes))); TransportParameterId::cwnd_hint_bytes, *cwndHintBytes)));

View File

@@ -36,6 +36,7 @@ TicketTransportParameters createTicketTransportParameters(
uint64_t initialMaxStreamDataUni, uint64_t initialMaxStreamDataUni,
uint64_t initialMaxStreamsBidi, uint64_t initialMaxStreamsBidi,
uint64_t initialMaxStreamsUni, uint64_t initialMaxStreamsUni,
ExtendedAckFeatureMaskType extendedAckSupport,
Optional<uint64_t> cwndHintBytes = none); Optional<uint64_t> cwndHintBytes = none);
} // namespace quic } // namespace quic

View File

@@ -129,6 +129,16 @@ bool DefaultAppTokenValidator::validate(
VLOG(10) << "Decreased max streams"; VLOG(10) << "Decreased max streams";
return validated = false; return validated = false;
} }
auto ticketExtendedAckFeatures =
getIntegerParameter(TransportParameterId::extended_ack_features, params)
.value_or(0);
if (conn_->transportSettings.advertisedExtendedAckFeatures !=
ticketExtendedAckFeatures) {
VLOG(10) << "Extended ack support changed";
return validated = false;
}
conn_->transportParamsMatching = true; conn_->transportParamsMatching = true;
if (!validateAndUpdateSourceToken( if (!validateAndUpdateSourceToken(

View File

@@ -78,6 +78,14 @@ void expectAppTokenEqual(
appToken.transportParams.parameters); appToken.transportParams.parameters);
EXPECT_EQ(cwndHintBytes, expectedCwndHintBytes); EXPECT_EQ(cwndHintBytes, expectedCwndHintBytes);
auto extendedAckSupport = getIntegerParameter(
TransportParameterId::extended_ack_features,
decodedAppToken->transportParams.parameters);
auto expectedExtendedAckSupport = getIntegerParameter(
TransportParameterId::extended_ack_features,
appToken.transportParams.parameters);
EXPECT_EQ(extendedAckSupport, expectedExtendedAckSupport);
EXPECT_EQ( EXPECT_EQ(
decodedAppToken->sourceAddresses.size(), appToken.sourceAddresses.size()); decodedAppToken->sourceAddresses.size(), appToken.sourceAddresses.size());
for (size_t ii = 0; ii < appToken.sourceAddresses.size(); ++ii) { for (size_t ii = 0; ii < appToken.sourceAddresses.size(); ++ii) {
@@ -104,7 +112,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeNoSourceAddresses) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.version = QuicVersion::MVFST; appToken.version = QuicVersion::MVFST;
Buf buf = encodeAppToken(appToken); Buf buf = encodeAppToken(appToken);
@@ -121,7 +130,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeSingleIPv6Address) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.sourceAddresses = { appToken.sourceAddresses = {
folly::IPAddress("2401:db00:2111:7283:face::46:0")}; folly::IPAddress("2401:db00:2111:7283:face::46:0")};
appToken.version = QuicVersion::MVFST; appToken.version = QuicVersion::MVFST;
@@ -140,7 +150,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeThreeIPv6Addresses) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.sourceAddresses = { appToken.sourceAddresses = {
folly::IPAddress("2401:db00:2111:7283:face::46:0"), folly::IPAddress("2401:db00:2111:7283:face::46:0"),
folly::IPAddress("2401:db00:2111:7283:face::46:1"), folly::IPAddress("2401:db00:2111:7283:face::46:1"),
@@ -161,7 +172,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeSingleIPv4Address) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.sourceAddresses = {folly::IPAddress("1.2.3.4")}; appToken.sourceAddresses = {folly::IPAddress("1.2.3.4")};
appToken.version = QuicVersion::MVFST; appToken.version = QuicVersion::MVFST;
Buf buf = encodeAppToken(appToken); Buf buf = encodeAppToken(appToken);
@@ -179,7 +191,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeThreeIPv4Addresses) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.sourceAddresses = { appToken.sourceAddresses = {
folly::IPAddress("1.2.3.4"), folly::IPAddress("1.2.3.4"),
folly::IPAddress("1.2.3.5"), folly::IPAddress("1.2.3.5"),
@@ -200,7 +213,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeIPv6AndIPv4Addresses) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.sourceAddresses = { appToken.sourceAddresses = {
folly::IPAddress("2401:db00:2111:7283:face::46:0"), folly::IPAddress("2401:db00:2111:7283:face::46:0"),
folly::IPAddress("1.2.3.4"), folly::IPAddress("1.2.3.4"),
@@ -221,7 +235,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeWithAppToken) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.appParams = folly::IOBuf::copyBuffer("QPACK Params"); appToken.appParams = folly::IOBuf::copyBuffer("QPACK Params");
appToken.version = QuicVersion::MVFST; appToken.version = QuicVersion::MVFST;
Buf buf = encodeAppToken(appToken); Buf buf = encodeAppToken(appToken);
@@ -239,7 +254,8 @@ TEST(AppTokenTest, TestEncodeAndDecodeIPv6AndIPv4AddressesWithAppToken) {
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow, kDefaultStreamFlowControlWindow,
std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max()); std::numeric_limits<uint32_t>::max(),
2 /* extendedAckSupport */);
appToken.sourceAddresses = { appToken.sourceAddresses = {
folly::IPAddress("2401:db00:2111:7283:face::46:0"), folly::IPAddress("2401:db00:2111:7283:face::46:0"),
folly::IPAddress("1.2.3.4"), folly::IPAddress("1.2.3.4"),

View File

@@ -44,7 +44,8 @@ TEST(DefaultAppTokenValidatorTest, TestValidParams) {
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi, conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures);
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -75,7 +76,8 @@ TEST(DefaultAppTokenValidatorTest, TestValidOptionalParameter) {
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi, conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures);
appToken.transportParams.parameters.push_back( appToken.transportParams.parameters.push_back(
encodeIntegerParameter(TransportParameterId::disable_migration, 1)); encodeIntegerParameter(TransportParameterId::disable_migration, 1));
ResumptionState resState; ResumptionState resState;
@@ -112,7 +114,8 @@ TEST(
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi, conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures);
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -197,7 +200,7 @@ TEST(DefaultAppTokenValidatorTest, TestInvalidMissingParams) {
params.parameters.push_back(encodeIntegerParameter( params.parameters.push_back(encodeIntegerParameter(
TransportParameterId::max_packet_size, TransportParameterId::max_packet_size,
conn.transportSettings.maxRecvPacketSize)); conn.transportSettings.maxRecvPacketSize));
appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -212,39 +215,45 @@ TEST(DefaultAppTokenValidatorTest, TestInvalidMissingParams) {
EXPECT_FALSE(validator.validate(resState)); EXPECT_FALSE(validator.validate(resState));
} }
TEST(DefaultAppTokenValidatorTest, TestInvalidRedundantParameter) { // This test was not actually testing for redundant parameters. It was passing
QuicServerConnectionState conn( // because the check on the source address was invalidating the token.
FizzServerQuicHandshakeContext::Builder().build()); // The validator currently allows redundant parameters.
conn.peerAddress = folly::SocketAddress("1.2.3.4", 443); // TODO: Update the validator to reject redundant parameters?
conn.version = QuicVersion::MVFST; // TEST(DefaultAppTokenValidatorTest, TestInvalidRedundantParameter) {
auto quicStats = std::make_shared<MockQuicStats>(); // QuicServerConnectionState conn(
conn.statsCallback = quicStats.get(); // FizzServerQuicHandshakeContext::Builder().build());
// conn.peerAddress = folly::SocketAddress("1.2.3.4", 443);
// conn.version = QuicVersion::MVFST;
// auto quicStats = std::make_shared<MockQuicStats>();
// conn.statsCallback = quicStats.get();
AppToken appToken; // AppToken appToken;
appToken.transportParams = createTicketTransportParameters( // appToken.transportParams = createTicketTransportParameters(
conn.transportSettings.idleTimeout.count(), // conn.transportSettings.idleTimeout.count(),
conn.transportSettings.maxRecvPacketSize, // conn.transportSettings.maxRecvPacketSize,
conn.transportSettings.advertisedInitialConnectionFlowControlWindow, // conn.transportSettings.advertisedInitialConnectionFlowControlWindow,
conn.transportSettings.advertisedInitialBidiLocalStreamFlowControlWindow, // conn.transportSettings.advertisedInitialBidiLocalStreamFlowControlWindow,
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, // conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, // conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi, // conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); // conn.transportSettings.advertisedInitialMaxStreamsUni,
appToken.transportParams.parameters.push_back( // conn.transportSettings.advertisedExtendedAckFeatures);
encodeIntegerParameter(TransportParameterId::idle_timeout, 100)); // appToken.transportParams.parameters.push_back(
ResumptionState resState; // encodeIntegerParameter(TransportParameterId::idle_timeout, 100));
resState.appToken = encodeAppToken(appToken); // appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
// ResumptionState resState;
// resState.appToken = encodeAppToken(appToken);
conn.earlyDataAppParamsValidator = [](const Optional<std::string>&, // conn.earlyDataAppParamsValidator = [](const Optional<std::string>&,
const Buf&) { // const Buf&) {
EXPECT_TRUE(false); // EXPECT_TRUE(false);
return true; // return true;
}; // };
DefaultAppTokenValidator validator(&conn); // DefaultAppTokenValidator validator(&conn);
EXPECT_CALL(*quicStats, onZeroRttAccepted()).Times(0); // EXPECT_CALL(*quicStats, onZeroRttAccepted()).Times(0);
EXPECT_CALL(*quicStats, onZeroRttRejected()); // EXPECT_CALL(*quicStats, onZeroRttRejected());
EXPECT_FALSE(validator.validate(resState)); // EXPECT_FALSE(validator.validate(resState));
} // }
TEST(DefaultAppTokenValidatorTest, TestInvalidDecreasedInitialMaxStreamData) { TEST(DefaultAppTokenValidatorTest, TestInvalidDecreasedInitialMaxStreamData) {
QuicServerConnectionState conn( QuicServerConnectionState conn(
@@ -266,7 +275,9 @@ TEST(DefaultAppTokenValidatorTest, TestInvalidDecreasedInitialMaxStreamData) {
1, 1,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow + 1, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow + 1,
conn.transportSettings.advertisedInitialMaxStreamsBidi, conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures);
appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -296,7 +307,9 @@ TEST(DefaultAppTokenValidatorTest, TestChangedIdleTimeout) {
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi, conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures);
appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -328,7 +341,9 @@ TEST(DefaultAppTokenValidatorTest, TestDecreasedInitialMaxStreams) {
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi + 1, conn.transportSettings.advertisedInitialMaxStreamsBidi + 1,
conn.transportSettings.advertisedInitialMaxStreamsUni + 1); conn.transportSettings.advertisedInitialMaxStreamsUni + 1,
conn.transportSettings.advertisedExtendedAckFeatures);
appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -343,6 +358,38 @@ TEST(DefaultAppTokenValidatorTest, TestDecreasedInitialMaxStreams) {
EXPECT_FALSE(validator.validate(resState)); EXPECT_FALSE(validator.validate(resState));
} }
TEST(DefaultAppTokenValidatorTest, TestInvalidExtendedAckSupportChanged) {
QuicServerConnectionState conn(
FizzServerQuicHandshakeContext::Builder().build());
conn.peerAddress = folly::SocketAddress("1.2.3.4", 443);
conn.version = QuicVersion::MVFST;
auto quicStats = std::make_shared<MockQuicStats>();
conn.statsCallback = quicStats.get();
AppToken appToken;
appToken.transportParams = createTicketTransportParameters(
conn.transportSettings.idleTimeout.count(),
conn.transportSettings.maxRecvPacketSize,
conn.transportSettings.advertisedInitialConnectionFlowControlWindow,
conn.transportSettings.advertisedInitialBidiLocalStreamFlowControlWindow,
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures + 1);
appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
ResumptionState resState;
resState.appToken = encodeAppToken(appToken);
conn.earlyDataAppParamsValidator = [](const Optional<std::string>&,
const Buf&) {
EXPECT_TRUE(false);
return true;
};
DefaultAppTokenValidator validator(&conn);
EXPECT_FALSE(validator.validate(resState));
}
TEST(DefaultAppTokenValidatorTest, TestInvalidAppParams) { TEST(DefaultAppTokenValidatorTest, TestInvalidAppParams) {
QuicServerConnectionState conn( QuicServerConnectionState conn(
FizzServerQuicHandshakeContext::Builder().build()); FizzServerQuicHandshakeContext::Builder().build());
@@ -363,7 +410,9 @@ TEST(DefaultAppTokenValidatorTest, TestInvalidAppParams) {
conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow, conn.transportSettings.advertisedInitialBidiRemoteStreamFlowControlWindow,
conn.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn.transportSettings.advertisedInitialMaxStreamsBidi, conn.transportSettings.advertisedInitialMaxStreamsBidi,
conn.transportSettings.advertisedInitialMaxStreamsUni); conn.transportSettings.advertisedInitialMaxStreamsUni,
conn.transportSettings.advertisedExtendedAckFeatures);
appToken.sourceAddresses = {conn.peerAddress.getIPAddress()};
ResumptionState resState; ResumptionState resState;
resState.appToken = encodeAppToken(appToken); resState.appToken = encodeAppToken(appToken);
@@ -392,7 +441,8 @@ class SourceAddressTokenTest : public Test {
.advertisedInitialBidiRemoteStreamFlowControlWindow, .advertisedInitialBidiRemoteStreamFlowControlWindow,
conn_.transportSettings.advertisedInitialUniStreamFlowControlWindow, conn_.transportSettings.advertisedInitialUniStreamFlowControlWindow,
conn_.transportSettings.advertisedInitialMaxStreamsBidi, conn_.transportSettings.advertisedInitialMaxStreamsBidi,
conn_.transportSettings.advertisedInitialMaxStreamsUni); conn_.transportSettings.advertisedInitialMaxStreamsUni,
conn_.transportSettings.advertisedExtendedAckFeatures);
} }
void encodeAndValidate(bool acceptZeroRtt = true) { void encodeAndValidate(bool acceptZeroRtt = true) {

View File

@@ -250,6 +250,11 @@ void processClientInitialParams(
TransportParameterId::knob_frames_supported), TransportParameterId::knob_frames_supported),
clientParams.parameters); clientParams.parameters);
auto extendedAckFeatures = getIntegerParameter(
static_cast<TransportParameterId>(
TransportParameterId::extended_ack_features),
clientParams.parameters);
auto reliableResetTpIter = findParameter( auto reliableResetTpIter = findParameter(
clientParams.parameters, clientParams.parameters,
static_cast<TransportParameterId>( static_cast<TransportParameterId>(
@@ -381,6 +386,7 @@ void processClientInitialParams(
} }
conn.peerAdvertisedKnobFrameSupport = knobFrameSupported.value_or(0) > 0; conn.peerAdvertisedKnobFrameSupport = knobFrameSupported.value_or(0) > 0;
conn.peerAdvertisedExtendedAckFeatures = extendedAckFeatures.value_or(0);
} }
void updateHandshakeState(QuicServerConnectionState& conn) { void updateHandshakeState(QuicServerConnectionState& conn) {
@@ -1113,6 +1119,21 @@ void onServerReadDataFromOpen(
<< conn; << conn;
isNonProbingPacket = true; isNonProbingPacket = true;
ReadAckFrame& ackFrame = *quicFrame.asReadAckFrame(); ReadAckFrame& ackFrame = *quicFrame.asReadAckFrame();
if (ackFrame.frameType == FrameType::ACK_EXTENDED &&
!conn.transportSettings.advertisedExtendedAckFeatures) {
throw QuicTransportException(
"Received unexpected ACK_EXTENDED frame",
TransportErrorCode::PROTOCOL_VIOLATION);
} else if (
ackFrame.frameType == FrameType::ACK_RECEIVE_TIMESTAMPS &&
!conn.transportSettings
.maybeAckReceiveTimestampsConfigSentToPeer) {
throw QuicTransportException(
"Received unexpected ACK_RECEIVE_TIMESTAMPS frame",
TransportErrorCode::PROTOCOL_VIOLATION);
}
conn.lastProcessedAckEvents.emplace_back(processAckFrame( conn.lastProcessedAckEvents.emplace_back(processAckFrame(
conn, conn,
packetNumberSpace, packetNumberSpace,

View File

@@ -336,6 +336,28 @@ TEST(ServerStateMachineTest, TestEncodeKnobFrameSupportedParamDisabled) {
testing::Eq(TransportParameterId::knob_frames_supported))))); testing::Eq(TransportParameterId::knob_frames_supported)))));
} }
TEST(ServerStateMachineTest, TestProcessExtendedAckSupportParam) {
QuicServerConnectionState serverConn(
FizzServerQuicHandshakeContext::Builder().build());
std::vector<TransportParameter> transportParams;
transportParams.push_back(
encodeIntegerParameter(TransportParameterId::extended_ack_features, 7));
ClientTransportParameters clientTransportParams = {
std::move(transportParams)};
processClientInitialParams(serverConn, clientTransportParams);
EXPECT_EQ(serverConn.peerAdvertisedExtendedAckFeatures, 7);
}
TEST(ServerStateMachineTest, TestProcessExtendedAckSupportParamNotSent) {
QuicServerConnectionState serverConn(
FizzServerQuicHandshakeContext::Builder().build());
std::vector<TransportParameter> transportParams;
ClientTransportParameters clientTransportParams = {
std::move(transportParams)};
processClientInitialParams(serverConn, clientTransportParams);
EXPECT_EQ(serverConn.peerAdvertisedExtendedAckFeatures, 0);
}
TEST( TEST(
ServerStateMachineTest, ServerStateMachineTest,
TestProcessReliableStreamResetSupportedParamEnabled) { TestProcessReliableStreamResetSupportedParamEnabled) {

View File

@@ -703,6 +703,8 @@ struct QuicConnectionStateBase : public folly::DelayedDestruction {
bool peerAdvertisedReliableStreamResetSupport{false}; bool peerAdvertisedReliableStreamResetSupport{false};
bool peerAdvertisedKnobFrameSupport{false}; bool peerAdvertisedKnobFrameSupport{false};
ExtendedAckFeatureMaskType peerAdvertisedExtendedAckFeatures{0};
// Retransmission policies map. // Retransmission policies map.
folly::F14FastMap<StreamGroupId, QuicStreamGroupRetransmissionPolicy> folly::F14FastMap<StreamGroupId, QuicStreamGroupRetransmissionPolicy>
retransmissionPolicies; retransmissionPolicies;

View File

@@ -335,6 +335,16 @@ struct TransportSettings {
bool dropIngressOnStopSending{false}; bool dropIngressOnStopSending{false};
bool advertisedReliableResetStreamSupport{false}; bool advertisedReliableResetStreamSupport{false};
bool advertisedKnobFrameSupport{true}; bool advertisedKnobFrameSupport{true};
// Extended ACK support to advertise to the peer. This is what we expect to
// receive from the peer inside ACK_EXTENDED frames.
// 0 means no support.
// Otherwise the bits of the integer indicate the following:
// - Bit 0: support for ACK_EXTENDED frame with ECN fields
// - Bit 1: support for ACK_EXTENDED frame with receive timestamp fields
// The list of features corresponds to ExtendedAckFeatures in QuicConstants
ExtendedAckFeatureMaskType advertisedExtendedAckFeatures{0};
bool removeStreamAfterEomCallbackUnset{false}; bool removeStreamAfterEomCallbackUnset{false};
// Whether to include cwnd hint in new session tickets for 0-rtt // Whether to include cwnd hint in new session tickets for 0-rtt
bool includeCwndHintsInSessionTicket{false}; bool includeCwndHintsInSessionTicket{false};