1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-09 10:00:57 +03:00

Codec changes to support ACK_RECEIVE_TIMESTAMPS

Summary: Create a new ACK_RECEIVE_TIMESTAMPS frame, as outlined in https://www.ietf.org/archive/id/draft-smith-quic-receive-ts-00.html#name-ack_receive_timestamps-fram

Reviewed By: mjoras

Differential Revision: D37799050

fbshipit-source-id: 0157c7fa7c4e489bb310f7c9cd6c0c1877e4967f
This commit is contained in:
Sharad Jaiswal (Eng)
2022-11-16 13:02:27 -08:00
committed by Facebook GitHub Bot
parent 9ef63d6b46
commit 96abc8160d
23 changed files with 1468 additions and 185 deletions

View File

@@ -12,6 +12,8 @@
#include <quic/QuicConstants.h>
#include <quic/QuicException.h>
#include <quic/codec/QuicInteger.h>
#include <cstdint>
#include <sstream>
namespace {
@@ -265,22 +267,131 @@ static size_t fillFrameWithAckBlocks(
return numAdditionalAckBlocks;
}
folly::Optional<AckFrameWriteResult> writeAckFrame(
size_t computeSizeUsedByRecvdTimestamps(WriteAckFrame& ackFrame) {
size_t usedSize = 0;
for (auto& recvdPacketsTimestampRanges :
ackFrame.recvdPacketsTimestampRanges) {
usedSize += getQuicIntegerSizeThrows(recvdPacketsTimestampRanges.gap);
usedSize += getQuicIntegerSizeThrows(
recvdPacketsTimestampRanges.timestamp_delta_count);
for (auto& timestampDelta : recvdPacketsTimestampRanges.deltas) {
usedSize += getQuicIntegerSizeThrows(timestampDelta);
}
}
return usedSize;
}
static size_t fillPacketReceiveTimestamps(
const quic::AckFrameMetaData& ackFrameMetaData,
PacketBuilderInterface& builder) {
if (ackFrameMetaData.ackBlocks.empty()) {
WriteAckFrame& ackFrame,
uint64_t largestAckedPacketNum,
uint64_t spaceLeft,
uint64_t receiveTimestampsExponent) {
if (ackFrameMetaData.ackState.recvdPacketInfos.size() == 0) {
return 0;
}
auto recvdPacketInfos = ackFrameMetaData.ackState.recvdPacketInfos;
// Insert all received packet timestamps into an interval set, to identify
// continguous ranges
DCHECK(ackFrameMetaData.maxAckReceiveTimestampsToSend.has_value());
auto maxRecvTimestampsToSend =
ackFrameMetaData.maxAckReceiveTimestampsToSend.value();
uint64_t pktsAdded = 0;
IntervalSet<PacketNum> receivedPktNumsIntervalSet;
for (auto& recvdPkt : recvdPacketInfos) {
// Add up to the peer requested max ack receive timestamps;
if (pktsAdded == maxRecvTimestampsToSend) {
break;
}
receivedPktNumsIntervalSet.insert(recvdPkt.pktNum);
pktsAdded++;
}
auto prevPktNum = largestAckedPacketNum;
auto timestampIt = recvdPacketInfos.crbegin();
size_t cumUsedSpace = 0;
// We start from the latest timestamp intervals
bool outOfSpace = false;
for (auto timestampIntervalsIt = receivedPktNumsIntervalSet.crbegin();
timestampIntervalsIt != receivedPktNumsIntervalSet.crend();
timestampIntervalsIt++) {
RecvdPacketsTimestampsRange nextTimestampRange;
size_t nextTimestampRangeUsedSpace = 0;
// Compute pktNum gap for each time-stamp range
if (ackFrame.recvdPacketsTimestampRanges.empty()) {
nextTimestampRange.gap = prevPktNum - timestampIntervalsIt->end;
} else {
nextTimestampRange.gap = prevPktNum - 2 - timestampIntervalsIt->end;
}
// Intialize spaced used by the next candidate time-stamp range
nextTimestampRangeUsedSpace +=
getQuicIntegerSizeThrows(nextTimestampRange.gap);
while (timestampIt != recvdPacketInfos.crend() &&
timestampIt->pktNum >= timestampIntervalsIt->start &&
timestampIt->pktNum <= timestampIntervalsIt->end) {
std::chrono::microseconds deltaDuration;
if (timestampIt == recvdPacketInfos.crbegin()) {
deltaDuration = (timestampIt->timeStamp > ackFrameMetaData.connTime)
? std::chrono::duration_cast<std::chrono::microseconds>(
timestampIt->timeStamp - ackFrameMetaData.connTime)
: 0us;
} else {
deltaDuration = std::chrono::duration_cast<std::chrono::microseconds>(
(timestampIt - 1)->timeStamp - timestampIt->timeStamp);
}
auto delta = deltaDuration.count() >> receiveTimestampsExponent;
// Check if adding a new time-stamp delta from the current time-stamp
// interval Will allow us to run out of space. Since adding a new delta
// impacts cumulative counts of deltas these are not already incorporated
// into nextTimestampRangeUsedSpace.
if (spaceLeft <
(cumUsedSpace + nextTimestampRangeUsedSpace +
getQuicIntegerSizeThrows(delta) +
getQuicIntegerSizeThrows(nextTimestampRange.deltas.size() + 1) +
getQuicIntegerSizeThrows(
ackFrame.recvdPacketsTimestampRanges.size() + 1))) {
outOfSpace = true;
break;
}
nextTimestampRange.deltas.push_back(delta);
nextTimestampRangeUsedSpace += getQuicIntegerSizeThrows(delta);
timestampIt++;
}
if (nextTimestampRange.deltas.size() > 0) {
nextTimestampRange.timestamp_delta_count =
nextTimestampRange.deltas.size();
cumUsedSpace += nextTimestampRangeUsedSpace +
getQuicIntegerSizeThrows(nextTimestampRange.deltas.size());
ackFrame.recvdPacketsTimestampRanges.push_back(nextTimestampRange);
prevPktNum = timestampIntervalsIt->start;
DCHECK(cumUsedSpace <= spaceLeft);
}
if (outOfSpace) {
break;
}
}
DCHECK(cumUsedSpace == computeSizeUsedByRecvdTimestamps(ackFrame));
return ackFrame.recvdPacketsTimestampRanges.size();
}
folly::Optional<WriteAckFrame> writeAckFrameToPacketBuilder(
const quic::AckFrameMetaData& ackFrameMetaData,
PacketBuilderInterface& builder,
FrameType frameType) {
if (ackFrameMetaData.ackState.acks.empty()) {
return folly::none;
}
const WriteAckState& ackState = ackFrameMetaData.ackState;
// The last block must be the largest block.
auto largestAckedPacket = ackFrameMetaData.ackBlocks.back().end;
auto largestAckedPacket = ackState.acks.back().end;
// ackBlocks are already an interval set so each value is naturally
// non-overlapping.
auto firstAckBlockLength =
largestAckedPacket - ackFrameMetaData.ackBlocks.back().start;
auto firstAckBlockLength = largestAckedPacket - ackState.acks.back().start;
WriteAckFrame ackFrame;
ackFrame.frameType = frameType;
uint64_t spaceLeft = builder.remainingSpaceInPkt();
uint64_t beginningSpace = spaceLeft;
ackFrame.ackBlocks.reserve(spaceLeft / 4);
// We could technically split the range if the size of the representation of
@@ -297,18 +408,42 @@ folly::Optional<AckFrameWriteResult> writeAckFrame(
// Required fields are Type, LargestAcked, AckDelay, AckBlockCount,
// firstAckBlockLength
QuicInteger encodedintFrameType(static_cast<uint8_t>(FrameType::ACK));
QuicInteger encodedintFrameType(static_cast<uint8_t>(frameType));
auto headerSize = encodedintFrameType.getSize() +
largestAckedPacketInt.getSize() + ackDelayInt.getSize() +
minAdditionalAckBlockCount.getSize() + firstAckBlockLengthInt.getSize();
if (spaceLeft < headerSize) {
size_t minAdditionalAckReceiveTimestampsFieldsSize = 0;
if (frameType == FrameType::ACK_RECEIVE_TIMESTAMPS) {
// Compute minimum size requirements for 3 fields that must be sent
// in every ACK_RECEIVE_TIMESTAMPS frame
uint64_t countTimestampRanges = 0;
uint64_t maybeLastPktNum = 0;
std::chrono::microseconds maybeLastPktTsDelta = 0us;
if (ackState.lastRecvdPacketInfo)
maybeLastPktNum = ackState.lastRecvdPacketInfo.value().pktNum;
maybeLastPktTsDelta =
(ackState.lastRecvdPacketInfo.value().timeStamp >
ackFrameMetaData.connTime
? std::chrono::duration_cast<std::chrono::microseconds>(
ackState.lastRecvdPacketInfo.value().timeStamp -
ackFrameMetaData.connTime)
: 0us);
minAdditionalAckReceiveTimestampsFieldsSize =
getQuicIntegerSize(countTimestampRanges).value_or(0) +
getQuicIntegerSize(maybeLastPktNum).value_or(0) +
getQuicIntegerSize(maybeLastPktTsDelta.count()).value_or(0);
}
if (spaceLeft < (headerSize + minAdditionalAckReceiveTimestampsFieldsSize)) {
return folly::none;
}
spaceLeft -= headerSize;
spaceLeft -= (headerSize + minAdditionalAckReceiveTimestampsFieldsSize);
ackFrame.ackBlocks.push_back(ackFrameMetaData.ackBlocks.back());
ackFrame.ackBlocks.push_back(ackState.acks.back());
auto numAdditionalAckBlocks =
fillFrameWithAckBlocks(ackFrameMetaData.ackBlocks, ackFrame, spaceLeft);
fillFrameWithAckBlocks(ackState.acks, ackFrame, spaceLeft);
QuicInteger numAdditionalAckBlocksInt(numAdditionalAckBlocks);
builder.write(encodedintFrameType);
@@ -317,7 +452,7 @@ folly::Optional<AckFrameWriteResult> writeAckFrame(
builder.write(numAdditionalAckBlocksInt);
builder.write(firstAckBlockLengthInt);
PacketNum currentSeqNum = ackFrameMetaData.ackBlocks.back().start;
PacketNum currentSeqNum = ackState.acks.back().start;
for (auto it = ackFrame.ackBlocks.cbegin() + 1;
it != ackFrame.ackBlocks.cend();
++it) {
@@ -331,10 +466,108 @@ folly::Optional<AckFrameWriteResult> writeAckFrame(
currentSeqNum = it->start;
}
ackFrame.ackDelay = ackFrameMetaData.ackDelay;
builder.appendFrame(std::move(ackFrame));
return AckFrameWriteResult(
return ackFrame;
}
folly::Optional<AckFrameWriteResult> writeAckFrame(
const quic::AckFrameMetaData& ackFrameMetaData,
PacketBuilderInterface& builder,
FrameType frameType) {
uint64_t beginningSpace = builder.remainingSpaceInPkt();
auto maybeWriteAckFrame =
writeAckFrameToPacketBuilder(ackFrameMetaData, builder, frameType);
if (maybeWriteAckFrame.has_value()) {
builder.appendFrame(std::move(maybeWriteAckFrame.value()));
return AckFrameWriteResult(
beginningSpace - builder.remainingSpaceInPkt(),
maybeWriteAckFrame.value(),
maybeWriteAckFrame.value().ackBlocks.size());
} else {
return folly::none;
}
}
folly::Optional<AckFrameWriteResult> writeAckFrameWithReceivedTimestamps(
const quic::AckFrameMetaData& ackFrameMetaData,
PacketBuilderInterface& builder,
FrameType frameType) {
auto beginningSpace = builder.remainingSpaceInPkt();
auto maybeAckFrame =
writeAckFrameToPacketBuilder(ackFrameMetaData, builder, frameType);
if (!maybeAckFrame.has_value()) {
return folly::none;
}
auto ackFrame = maybeAckFrame.value();
const WriteAckState& ackState = ackFrameMetaData.ackState;
uint64_t spaceLeft = builder.remainingSpaceInPkt();
uint64_t lastPktNum = 0;
std::chrono::microseconds lastPktTsDelta = 0us;
if (ackState.lastRecvdPacketInfo) {
lastPktNum = ackState.lastRecvdPacketInfo.value().pktNum;
lastPktTsDelta =
(ackState.lastRecvdPacketInfo.value().timeStamp >
ackFrameMetaData.connTime
? std::chrono::duration_cast<std::chrono::microseconds>(
ackState.lastRecvdPacketInfo.value().timeStamp -
ackFrameMetaData.connTime)
: 0us);
}
QuicInteger lastRecvdPacketNumInt(lastPktNum);
builder.write(lastRecvdPacketNumInt);
ackFrame.maybeLatestRecvdPacketNum = lastRecvdPacketNumInt.getValue();
QuicInteger lastRecvdPacketTimeInt(lastPktTsDelta.count());
builder.write(lastRecvdPacketTimeInt);
ackFrame.maybeLatestRecvdPacketTime =
std::chrono::microseconds(lastRecvdPacketTimeInt.getValue());
size_t countTimestampRanges = 0;
size_t countTimestamps = 0;
spaceLeft = builder.remainingSpaceInPkt();
if (spaceLeft > 0) {
auto largestAckedPacket = ackState.acks.back().end;
uint8_t receiveTimestampsExponentToUse =
ackFrameMetaData.recvTimestampsConfig.has_value()
? ackFrameMetaData.recvTimestampsConfig.value()
.receive_timestamps_exponent
: kDefaultReceiveTimestampsExponent;
countTimestampRanges = fillPacketReceiveTimestamps(
ackFrameMetaData,
ackFrame,
largestAckedPacket,
spaceLeft,
receiveTimestampsExponentToUse);
if (countTimestampRanges > 0) {
QuicInteger timeStampRangeCountInt(
ackFrame.recvdPacketsTimestampRanges.size());
builder.write(timeStampRangeCountInt);
for (auto& recvdPacketsTimestampRanges :
ackFrame.recvdPacketsTimestampRanges) {
QuicInteger gapInt(recvdPacketsTimestampRanges.gap);
QuicInteger timestampDeltaCountInt(
recvdPacketsTimestampRanges.timestamp_delta_count);
builder.write(gapInt);
builder.write(timestampDeltaCountInt);
for (auto& timestamp : recvdPacketsTimestampRanges.deltas) {
QuicInteger deltaInt(timestamp);
builder.write(deltaInt);
countTimestamps++;
}
}
} else {
QuicInteger timeStampRangeCountInt(0);
builder.write(timeStampRangeCountInt);
}
}
auto ackFrameWriteResult = AckFrameWriteResult(
beginningSpace - builder.remainingSpaceInPkt(),
1 + numAdditionalAckBlocks);
ackFrame,
ackFrame.ackBlocks.size(),
countTimestampRanges,
countTimestamps);
builder.appendFrame(std::move(ackFrame));
return ackFrameWriteResult;
}
size_t writeSimpleFrame(