Skip to content

Commit

Permalink
One-Way Delay Feature Support (#3846)
Browse files Browse the repository at this point in the history
  • Loading branch information
nibanks authored Sep 12, 2023
1 parent 34d8371 commit 226138d
Show file tree
Hide file tree
Showing 54 changed files with 1,275 additions and 239 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ protocol. It is cross-platform, written in C and designed to be a general purpos
[![](https://img.shields.io/static/v1?label=Draft&message=Disable%20Encryption&color=blueviolet)](https://tools.ietf.org/html/draft-banks-quic-disable-encryption)
[![](https://img.shields.io/static/v1?label=Draft&message=Performance&color=blueviolet)](https://tools.ietf.org/html/draft-banks-quic-performance)
[![](https://img.shields.io/static/v1?label=Draft&message=CIBIR&color=blueviolet)](https://tools.ietf.org/html/draft-banks-quic-cibir)
[![](https://img.shields.io/static/v1?label=Draft&message=Timestamps&color=blueviolet)](https://tools.ietf.org/html/draft-huitema-quic-ts)

QUIC has many benefits when compared to existing "TLS over TCP" scenarios:

Expand Down
20 changes: 16 additions & 4 deletions src/core/ack_tracker.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,22 @@ QuicAckTrackerAckFrameEncode(
{
CXPLAT_DBG_ASSERT(QuicAckTrackerHasPacketsToAck(Tracker));

uint64_t AckDelay =
CxPlatTimeDiff64(Tracker->LargestPacketNumberRecvTime, CxPlatTimeUs64());

AckDelay >>= Builder->Connection->AckDelayExponent;
const uint64_t Timestamp = CxPlatTimeUs64();
const uint64_t AckDelay =
CxPlatTimeDiff64(Tracker->LargestPacketNumberRecvTime, Timestamp)
>> Builder->Connection->AckDelayExponent;

if (Builder->Connection->State.TimestampSendNegotiated &&
Builder->EncryptLevel == QUIC_ENCRYPT_LEVEL_1_RTT) {
QUIC_TIMESTAMP_EX Frame = { Timestamp - Builder->Connection->Stats.Timing.Start };
if (!QuicTimestampFrameEncode(
&Frame,
&Builder->DatagramLength,
(uint16_t)Builder->Datagram->Length - Builder->EncryptionOverhead,
Builder->Datagram->Buffer)) {
return FALSE;
}
}

if (!QuicAckFrameEncode(
&Tracker->PacketNumbersToAck,
Expand Down
7 changes: 3 additions & 4 deletions src/core/bbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,17 +320,16 @@ BbrCongestionControlLogOutFlowStatus(

QuicTraceEvent(
ConnOutFlowStatsV2,
"[conn][%p] OUT: BytesSent=%llu InFlight=%u InFlightMax=%u CWnd=%u SSThresh=%u ConnFC=%llu ISB=%llu PostedBytes=%llu SRtt=%llu",
"[conn][%p] OUT: BytesSent=%llu InFlight=%u CWnd=%u ConnFC=%llu ISB=%llu PostedBytes=%llu SRtt=%llu 1Way=%llu",
Connection,
Connection->Stats.Send.TotalBytes,
Bbr->BytesInFlight,
Bbr->BytesInFlightMax,
Bbr->CongestionWindow,
0,
Connection->Send.PeerMaxData - Connection->Send.OrderedStreamBytesSent,
Connection->SendBuffer.IdealBytes,
Connection->SendBuffer.PostedBytes,
Path->GotFirstRttSample ? Path->SmoothedRtt : 0);
Path->GotFirstRttSample ? Path->SmoothedRtt : 0,
Path->OneWayDelay);
}

//
Expand Down
1 change: 1 addition & 0 deletions src/core/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,7 @@ QuicBindingReceive(
Packet->PacketId =
PartitionShifted | InterlockedIncrement64((int64_t*)&QuicLibraryGetPerProc()->ReceivePacketId);
Packet->PacketNumber = 0;
Packet->SendTimestamp = UINT64_MAX;
Packet->AvailBuffer = Datagram->Buffer;
Packet->DestCid = NULL;
Packet->SourceCid = NULL;
Expand Down
6 changes: 6 additions & 0 deletions src/core/binding.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ typedef struct QUIC_RX_PACKET {
//
uint64_t PacketNumber;

//
// Represents the sender side's timestamp (in us) from the start of their
// epoch.
//
uint64_t SendTimestamp;

//
// The current packet buffer.
//
Expand Down
5 changes: 5 additions & 0 deletions src/core/congestion_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ typedef struct QUIC_ACK_EVENT {
//
uint64_t MinRtt;

//
// The smoothed one-way delay of the send path.
//
uint64_t OneWayDelay;

//
// Acked time minus ack delay.
//
Expand Down
146 changes: 114 additions & 32 deletions src/core/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -810,54 +810,67 @@ void
QuicConnUpdateRtt(
_In_ QUIC_CONNECTION* Connection,
_In_ QUIC_PATH* Path,
_In_ uint64_t LatestRtt
_In_ uint64_t LatestRtt,
_In_ uint64_t OurSendTimestamp,
_In_ uint64_t PeerSendTimestamp
)
{
BOOLEAN RttUpdated;
UNREFERENCED_PARAMETER(Connection);

if (LatestRtt == 0) {
//
// RTT cannot be zero or several loss recovery algorithms break down.
//
LatestRtt = 1;
}

BOOLEAN NewMinRtt = FALSE;
Path->LatestRttSample = LatestRtt;
if (LatestRtt < Path->MinRtt) {
Path->MinRtt = LatestRtt;
NewMinRtt = TRUE;
}
if (LatestRtt > Path->MaxRtt) {
Path->MaxRtt = LatestRtt;
}

if (!Path->GotFirstRttSample) {
Path->GotFirstRttSample = TRUE;

Path->SmoothedRtt = LatestRtt;
Path->RttVariance = LatestRtt / 2;
RttUpdated = TRUE;

} else {
uint64_t PrevRtt = Path->SmoothedRtt;
if (Path->SmoothedRtt > LatestRtt) {
Path->RttVariance = (3 * Path->RttVariance + Path->SmoothedRtt - LatestRtt) / 4;
} else {
Path->RttVariance = (3 * Path->RttVariance + LatestRtt - Path->SmoothedRtt) / 4;
}
Path->SmoothedRtt = (7 * Path->SmoothedRtt + LatestRtt) / 8;
RttUpdated = PrevRtt != Path->SmoothedRtt;
}

if (RttUpdated) {
CXPLAT_DBG_ASSERT(Path->SmoothedRtt != 0);
QuicTraceLogConnVerbose(
RttUpdatedMsg,
Connection,
"Updated Rtt=%u.%03u ms, Var=%u.%03u",
(uint32_t)(Path->SmoothedRtt / 1000), (uint32_t)(Path->SmoothedRtt % 1000),
(uint32_t)(Path->RttVariance / 1000), (uint32_t)(Path->RttVariance % 1000));
if (OurSendTimestamp != UINT64_MAX) {
if (Connection->Stats.Timing.PhaseShift == 0 || NewMinRtt) {
Connection->Stats.Timing.PhaseShift =
(int64_t)PeerSendTimestamp - (int64_t)OurSendTimestamp - (int64_t)LatestRtt / 2;
Path->OneWayDelayLatest = Path->OneWayDelay = LatestRtt / 2;
QuicTraceLogConnVerbose(
PhaseShiftUpdated,
Connection,
"New Phase Shift: %lld us",
Connection->Stats.Timing.PhaseShift);
} else {
Path->OneWayDelayLatest =
(uint64_t)((int64_t)PeerSendTimestamp - (int64_t)OurSendTimestamp - Connection->Stats.Timing.PhaseShift);
Path->OneWayDelay = (7 * Path->OneWayDelay + Path->OneWayDelayLatest) / 8;
}
}

CXPLAT_DBG_ASSERT(Path->SmoothedRtt != 0);
QuicTraceLogConnVerbose(
RttUpdatedV2,
Connection,
"Updated Rtt=%u.%03u ms, Var=%u.%03u 1Way=%u.%03u ms",
(uint32_t)(Path->SmoothedRtt / 1000), (uint32_t)(Path->SmoothedRtt % 1000),
(uint32_t)(Path->RttVariance / 1000), (uint32_t)(Path->RttVariance % 1000),
(uint32_t)(Path->OneWayDelay / 1000), (uint32_t)(Path->OneWayDelay % 1000));
}

_IRQL_requires_max_(PASSIVE_LEVEL)
Expand Down Expand Up @@ -2406,6 +2419,11 @@ QuicConnGenerateLocalTransportParameters(
LocalTP->Flags |= QUIC_TP_FLAG_RELIABLE_RESET_ENABLED;
}

if (Connection->Settings.OneWayDelayEnabled) {
LocalTP->Flags |= QUIC_TP_FLAG_TIMESTAMP_RECV_ENABLED |
QUIC_TP_FLAG_TIMESTAMP_SEND_ENABLED;
}

if (QuicConnIsServer(Connection)) {

if (Connection->Streams.Types[STREAM_ID_FLAG_IS_CLIENT | STREAM_ID_FLAG_IS_BI_DIR].MaxTotalStreamCount) {
Expand Down Expand Up @@ -3039,20 +3057,13 @@ QuicConnProcessPeerTransportParameters(
Connection->Stats.GreaseBitNegotiated = TRUE;
}

if (Connection->Settings.ReliableResetEnabled &&
(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED) > 0) {
//
// Reliable Reset is enabled on both sides.
//

Connection->State.ReliableResetStreamNegotiated = TRUE;
}

if (Connection->Settings.ReliableResetEnabled) {
Connection->State.ReliableResetStreamNegotiated =
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED);

//
// Send event to app to indicate result of negotiation if app cares.
//

QUIC_CONNECTION_EVENT Event;
Event.Type = QUIC_CONNECTION_EVENT_RELIABLE_RESET_NEGOTIATED;
Event.RELIABLE_RESET_NEGOTIATED.IsNegotiated = Connection->State.ReliableResetStreamNegotiated;
Expand All @@ -3065,6 +3076,29 @@ QuicConnProcessPeerTransportParameters(
QuicConnIndicateEvent(Connection, &Event);
}

if (Connection->Settings.OneWayDelayEnabled) {
Connection->State.TimestampSendNegotiated = // Peer wants to recv, so we can send
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_TIMESTAMP_RECV_ENABLED);
Connection->State.TimestampRecvNegotiated = // Peer wants to send, so we can recv
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_TIMESTAMP_SEND_ENABLED);

//
// Send event to app to indicate result of negotiation if app cares.
//
QUIC_CONNECTION_EVENT Event;
Event.Type = QUIC_CONNECTION_EVENT_ONE_WAY_DELAY_NEGOTIATED;
Event.ONE_WAY_DELAY_NEGOTIATED.SendNegotiated = Connection->State.TimestampSendNegotiated;
Event.ONE_WAY_DELAY_NEGOTIATED.ReceiveNegotiated = Connection->State.TimestampRecvNegotiated;

QuicTraceLogConnVerbose(
IndicateOneWayDelayNegotiated,
Connection,
"Indicating QUIC_CONNECTION_EVENT_ONE_WAY_DELAY_NEGOTIATED [Send=%hhu,Recv=%hhu]",
Event.ONE_WAY_DELAY_NEGOTIATED.SendNegotiated,
Event.ONE_WAY_DELAY_NEGOTIATED.ReceiveNegotiated);
QuicConnIndicateEvent(Connection, &Event);
}

//
// Fully validate all exchanged connection IDs.
//
Expand Down Expand Up @@ -4558,6 +4592,7 @@ QuicConnRecvFrames(
if (!QuicLossDetectionProcessAckFrame(
&Connection->LossDetection,
Path,
Packet,
EncryptLevel,
FrameType,
PayloadLength,
Expand Down Expand Up @@ -5282,6 +5317,33 @@ QuicConnRecvFrames(
AckImmediately = TRUE;
break;

case QUIC_FRAME_TIMESTAMP: { // Always accept the frame, because we always enable support.
if (!Connection->State.TimestampRecvNegotiated) {
QuicTraceEvent(
ConnError,
"[conn][%p] ERROR, %s.",
Connection,
"Received TIMESTAMP frame when not negotiated");
QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION);
return FALSE;

}
QUIC_TIMESTAMP_EX Frame;
if (!QuicTimestampFrameDecode(PayloadLength, Payload, &Offset, &Frame)) {
QuicTraceEvent(
ConnError,
"[conn][%p] ERROR, %s.",
Connection,
"Decoding TIMESTAMP frame");
QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
return FALSE;
}

Packet->HasNonProbingFrame = TRUE;
Packet->SendTimestamp = Frame.Timestamp;
break;
}

case QUIC_FRAME_RELIABLE_RESET_STREAM:
// TODO - Implement this frame.
default:
Expand Down Expand Up @@ -7288,13 +7350,10 @@ QuicConnApplyNewSettings(
Connection->Stats.GreaseBitNegotiated = TRUE;
}

if (QuicConnIsServer(Connection) &&
Connection->Settings.ReliableResetEnabled &&
(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED) > 0) {
Connection->State.ReliableResetStreamNegotiated = TRUE;
}

if (QuicConnIsServer(Connection) && Connection->Settings.ReliableResetEnabled) {
Connection->State.ReliableResetStreamNegotiated =
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED);

//
// Send event to app to indicate result of negotiation if app cares.
//
Expand All @@ -7310,6 +7369,29 @@ QuicConnApplyNewSettings(
QuicConnIndicateEvent(Connection, &Event);
}

if (QuicConnIsServer(Connection) && Connection->Settings.OneWayDelayEnabled) {
Connection->State.TimestampSendNegotiated = // Peer wants to recv, so we can send
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_TIMESTAMP_RECV_ENABLED);
Connection->State.TimestampRecvNegotiated = // Peer wants to send, so we can recv
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_TIMESTAMP_SEND_ENABLED);

//
// Send event to app to indicate result of negotiation if app cares.
//
QUIC_CONNECTION_EVENT Event;
Event.Type = QUIC_CONNECTION_EVENT_ONE_WAY_DELAY_NEGOTIATED;
Event.ONE_WAY_DELAY_NEGOTIATED.SendNegotiated = Connection->State.TimestampSendNegotiated;
Event.ONE_WAY_DELAY_NEGOTIATED.ReceiveNegotiated = Connection->State.TimestampRecvNegotiated;

QuicTraceLogConnVerbose(
IndicateOneWayDelayNegotiated,
Connection,
"Indicating QUIC_CONNECTION_EVENT_ONE_WAY_DELAY_NEGOTIATED [Send=%hhu,Recv=%hhu]",
Event.ONE_WAY_DELAY_NEGOTIATED.SendNegotiated,
Event.ONE_WAY_DELAY_NEGOTIATED.ReceiveNegotiated);
QuicConnIndicateEvent(Connection, &Event);
}

if (Connection->Settings.EcnEnabled) {
QUIC_PATH* Path = &Connection->Paths[0];
Path->EcnValidationState = ECN_VALIDATION_TESTING;
Expand Down
15 changes: 14 additions & 1 deletion src/core/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ typedef union QUIC_CONNECTION_STATE {
//
BOOLEAN ReliableResetStreamNegotiated : 1;

//
// Sending timestamps has been negotiated.
//
BOOLEAN TimestampSendNegotiated : 1;

//
// Receiving timestamps has been negotiated.
//
BOOLEAN TimestampRecvNegotiated : 1;

#ifdef CxPlatVerifierEnabledByAddr
//
// The calling app is being verified (app or driver verifier).
Expand Down Expand Up @@ -262,6 +272,7 @@ typedef struct QUIC_CONN_STATS {
uint64_t Start;
uint64_t InitialFlightEnd; // Processed all peer's Initial packets
uint64_t HandshakeFlightEnd; // Processed all peer's Handshake packets
int64_t PhaseShift; // Time between local and peer epochs
} Timing;

struct {
Expand Down Expand Up @@ -1292,7 +1303,9 @@ void
QuicConnUpdateRtt(
_In_ QUIC_CONNECTION* Connection,
_In_ QUIC_PATH* Path,
_In_ uint64_t LatestRtt
_In_ uint64_t LatestRtt,
_In_ uint64_t OurSendTimestamp,
_In_ uint64_t PeerSendTimestamp
);

//
Expand Down
Loading

0 comments on commit 226138d

Please sign in to comment.