Add ECN support: ECN validation (#3168)

This PR implements 13.4.2. ECN Validation in RFC9000. The ECN validation algorithm implemented in this PR is very similar to the sample algorithm given in RFC9000 A.4. The details can be found at the top of path.h.

This PR also adds a new global QUIC setting, EcnEnabled and a flag, EcnCapable, in connection stats.

This PR does not implement CC ECN reaction, which will be done in a separate PR.
This commit is contained in:
Yi Huang 2022-10-24 15:38:52 -07:00 коммит произвёл GitHub
Родитель 52144efbc6
Коммит 22e14863c4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
45 изменённых файлов: 980 добавлений и 37 удалений

Просмотреть файл

@ -60,6 +60,7 @@ The following settings are available via registry as well as via [QUIC_SETTINGS]
| Max Binding Stateless Operations | uint16_t | MaxBindingStatelessOperations | 100 | The maximum number of stateless operations that may be queued on a binding at any one time. |
| Stateless Operation Expiration | uint16_t | StatelessOperationExpirationMs | 100 | The time limit between operations for the same endpoint, in milliseconds. |
| Congestion Control Algorithm | uint16_t | CongestionControlAlgorithm | 0 (Cubic) | The congestion control algorithm used for the connection. |
| ECN | uint8_t | EcnEnabled | 0 (FALSE) | Enable sender-side ECN support. |
The types map to registry types as follows:
- `uint64_t` is a `REG_QWORD`.

Просмотреть файл

@ -44,7 +44,8 @@ typedef struct QUIC_SETTINGS {
uint64_t MtuDiscoveryMissingProbeCount : 1;
uint64_t DestCidUpdateIdleTimeoutMs : 1;
uint64_t GreaseQuicBitEnabled : 1;
uint64_t RESERVED : 31;
uint64_t EcnEnabled : 1;
uint64_t RESERVED : 30;
} IsSet;
};
@ -78,7 +79,7 @@ typedef struct QUIC_SETTINGS {
uint8_t DatagramReceiveEnabled : 1;
uint8_t ServerResumptionLevel : 2; // QUIC_SERVER_RESUMPTION_LEVEL
uint8_t GreaseQuicBitEnabled : 1;
uint8_t RESERVED : 1;
uint8_t EcnEnabled : 1;
uint8_t MaxOperationsPerDrain;
uint8_t MtuDiscoveryMissingProbeCount;
uint32_t DestCidUpdateIdleTimeoutMs;
@ -296,6 +297,12 @@ Advertise support for QUIC Grease Bit Extension. Both sides of a connection need
**Default value:** 0 (`FALSE`)
`EcnEnabled`
Enable sender-side ECN support. The connection will validate and react to ECN feedback from peer.
**Default value:** 0 (`FALSE`)
# Remarks
When setting new values for the settings, the app must set the corresponding `.IsSet.*` parameter for each actual parameter that is being set or updated. For example:

Просмотреть файл

@ -634,6 +634,11 @@ QuicConnTraceRundownOper(
"[conn][%p] Assigned worker: %p",
Connection,
Connection->Worker);
QuicTraceEvent(
ConnEcnCapable,
"[conn][%p] Ecn: IsCapable=%hu",
Connection,
Connection->Paths[0].EcnValidationState == ECN_VALIDATION_CAPABLE);
CXPLAT_DBG_ASSERT(Connection->Registration);
QuicTraceEvent(
ConnRegistered,
@ -5942,6 +5947,7 @@ QuicConnResetIdleTimeout(
)
{
uint64_t IdleTimeoutMs;
QUIC_PATH* Path = &Connection->Paths[0];
if (Connection->State.Connected) {
//
// Use the (non-zero) min value between local and peer's configuration.
@ -5964,7 +5970,7 @@ QuicConnResetIdleTimeout(
uint32_t MinIdleTimeoutMs =
US_TO_MS(QuicLossDetectionComputeProbeTimeout(
&Connection->LossDetection,
&Connection->Paths[0],
Path,
QUIC_CLOSE_PTO_COUNT));
if (IdleTimeoutMs < MinIdleTimeoutMs) {
IdleTimeoutMs = MinIdleTimeoutMs;
@ -6667,6 +6673,7 @@ QuicConnGetV2Statistics(
Stats->ResumptionAttempted = Connection->Stats.ResumptionAttempted;
Stats->ResumptionSucceeded = Connection->Stats.ResumptionSucceeded;
Stats->GreaseBitNegotiated = Connection->Stats.GreaseBitNegotiated;
Stats->EcnCapable = Path->EcnValidationState == ECN_VALIDATION_CAPABLE;
Stats->Rtt = Path->SmoothedRtt;
Stats->MinRtt = Path->MinRtt;
Stats->MaxRtt = Path->MaxRtt;
@ -7170,6 +7177,11 @@ QuicConnApplyNewSettings(
Connection->State.FixedBit = (RandomValue % 2);
Connection->Stats.GreaseBitNegotiated = TRUE;
}
if (Connection->Settings.EcnEnabled) {
QUIC_PATH* Path = &Connection->Paths[0];
Path->EcnValidationState = ECN_VALIDATION_TESTING;
}
}
uint8_t PeerStreamType =

Просмотреть файл

@ -1299,7 +1299,8 @@ QuicLossDetectionProcessAckBlocks(
_In_ QUIC_ENCRYPT_LEVEL EncryptLevel,
_In_ uint64_t AckDelay,
_In_ QUIC_RANGE* AckBlocks,
_Out_ BOOLEAN* InvalidAckBlock
_Out_ BOOLEAN* InvalidAckBlock,
_In_opt_ QUIC_ACK_ECN_EX* Ecn
)
{
QUIC_SENT_PACKET_METADATA* AckedPackets = NULL;
@ -1432,7 +1433,7 @@ QuicLossDetectionProcessAckBlocks(
uint64_t LargestAckedPacketNum = 0;
BOOLEAN IsLargestAckedPacketAppLimited = FALSE;
int64_t EcnEctCounter = 0;
QUIC_SENT_PACKET_METADATA* AckedPacketsIterator = AckedPackets;
while (AckedPacketsIterator != NULL) {
@ -1475,6 +1476,7 @@ QuicLossDetectionProcessAckBlocks(
IsLargestAckedPacketAppLimited = Packet->Flags.IsAppLimited;
}
EcnEctCounter += Packet->Flags.EcnEctSet;
QuicLossDetectionOnPacketAcknowledged(LossDetection, EncryptLevel, Packet, FALSE, (uint32_t)TimeNow, AckDelay);
}
@ -1496,6 +1498,67 @@ QuicLossDetectionProcessAckBlocks(
}
if (NewLargestAck) {
if (Path->EcnValidationState != ECN_VALIDATION_FAILED) {
//
// Per RFC 9000, we validate ECN counts from received ACK frames
// when the largest acked packet number increases.
//
QUIC_PACKET_SPACE* Packets = Connection->Packets[EncryptLevel];
BOOLEAN EcnValidated = TRUE;
int64_t EctCeDeltaSum = 0;
if (Ecn != NULL) {
EctCeDeltaSum += Ecn->CE_Count - Packets->EcnCeCounter;
EctCeDeltaSum += Ecn->ECT_0_Count - Packets->EcnEctCounter;
//
// Conditions where ECN validation fails:
// 1. Reneging ECN counts from the peer.
// 2. ECN counts do not match the marks that were applied to the packets sent.
//
if (EctCeDeltaSum < 0 ||
EctCeDeltaSum < EcnEctCounter ||
Ecn->ECT_1_Count != 0 ||
Connection->Send.NumPacketsSentWithEct < Ecn->ECT_0_Count) {
EcnValidated = FALSE;
} else {
//
// TODO: Notify CC of the ECN signal before we update the CE counts.
//
Packets->EcnCeCounter = Ecn->CE_Count;
Packets->EcnEctCounter = Ecn->ECT_0_Count;
if (Path->EcnValidationState <= ECN_VALIDATION_UNKNOWN) {
Path->EcnValidationState = ECN_VALIDATION_CAPABLE;
QuicTraceEvent(
ConnEcnCapable,
"[conn][%p] Ecn: IsCapable=%hu",
Connection,
TRUE);
}
}
} else {
//
// If an ACK frame newly acknowledges a packet that the endpoint sent
// with either the ECT(0) or ECT(1) codepoint set, ECN validation fails
// if the corresponding ECN counts are not present in the ACK frame.
//
if (EcnEctCounter != 0) {
EcnValidated = FALSE;
}
}
if (!EcnValidated) {
QuicTraceEvent(
ConnEcnFailed,
"[conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu",
Connection,
EncryptLevel,
Packets->EcnEctCounter, Packets->EcnCeCounter,
Connection->Send.NumPacketsSentWithEct,
EctCeDeltaSum,
Path->EcnValidationState);
Path->EcnValidationState = ECN_VALIDATION_FAILED;
}
}
//
// Handle packet loss (and any possible congestion events) before
// data acknowledgement so that we have an accurate bytes in flight
@ -1595,7 +1658,6 @@ QuicLossDetectionProcessAckFrame(
} else {
// TODO - Use ECN information.
AckDelay <<= Connection->PeerTransportParams.AckDelayExponent;
QuicLossDetectionProcessAckBlocks(
@ -1604,7 +1666,8 @@ QuicLossDetectionProcessAckFrame(
EncryptLevel,
AckDelay,
&Connection->DecodedAckRanges,
InvalidFrame);
InvalidFrame,
FrameType == QUIC_FRAME_ACK_1 ? &Ecn : NULL);
}
}

Просмотреть файл

@ -258,7 +258,7 @@ QuicPacketBuilderPrepare(
Builder->SendData =
CxPlatSendDataAlloc(
Builder->Path->Binding->Socket,
CXPLAT_ECN_NON_ECT,
Builder->EcnEctSet ? CXPLAT_ECN_ECT_0 : CXPLAT_ECN_NON_ECT,
IsPathMtuDiscovery ?
0 :
MaxUdpPayloadSizeForFamily(
@ -927,7 +927,7 @@ QuicPacketBuilderFinalize(
Builder->Metadata->SentTime = CxPlatTimeUs32();
Builder->Metadata->PacketLength =
Builder->HeaderLength + PayloadLength;
Builder->Metadata->Flags.EcnEctSet = Builder->EcnEctSet;
QuicTraceEvent(
ConnPacketSent,
"[conn][%p][TX][%llu] %hhu (%hu bytes)",
@ -963,6 +963,9 @@ Exit:
if (FinalQuicPacket) {
if (Builder->Datagram != NULL) {
if (Builder->Metadata->Flags.EcnEctSet) {
++Connection->Send.NumPacketsSentWithEct;
}
Builder->Datagram->Length = Builder->DatagramLength;
Builder->Datagram = NULL;
++Builder->TotalCountDatagrams;

Просмотреть файл

@ -73,6 +73,11 @@ typedef struct QUIC_PACKET_BUILDER {
//
uint8_t BatchCount : 4;
//
// Indicates whether ECN ECT bit is set on the packets to be sent.
//
uint8_t EcnEctSet : 1;
//
// The total number of datagrams that have been created.
//

Просмотреть файл

@ -62,6 +62,12 @@ typedef struct QUIC_PACKET_SPACE {
//
uint64_t NextRecvPacketNumber;
//
// ECT and CE counters.
//
uint64_t EcnEctCounter;
uint64_t EcnCeCounter; // maps to ecn_ce_counters in RFC 9002.
//
// Owning connection of this packet space.
//

Просмотреть файл

@ -31,7 +31,8 @@ QuicPathInitialize(
Path->Mtu = Connection->Settings.MinimumMtu;
Path->SmoothedRtt = MS_TO_US(Connection->Settings.InitialRttMs);
Path->RttVariance = Path->SmoothedRtt / 2;
Path->EcnValidationState =
Connection->Settings.EcnEnabled ? ECN_VALIDATION_TESTING : ECN_VALIDATION_FAILED;
QuicTraceLogConnInfo(
PathInitialized,
Connection,

Просмотреть файл

@ -5,6 +5,40 @@
--*/
//
// ECN validation state transition:
//
// ECN_VALIDATION_TESTING: when a new path is created AND ECN is enabled.
//
// ECN_VALIDATION_TESTING -> ECN_VALIDATION_UNKNOWN: after sending packets with ECT bit set for 3 PTOs.
//
// {ECN_VALIDATION_TESTING | ECN_VALIDATION_UNKNOWN} -> ECN_VALIDATION_CAPABLE:
// when ECN validation passes.
//
// {ANY} -> ECN_VALIDATION_FAILED: when ECN validation fails.
//
// In ECN_VALIDATION_TESTING or ECN_VALIDATION_CAPABLE state, packets sent are marked with ECT bit.
//
// This algorithm is a slightly simplified and relaxed version of the sample ECN validation in
// RFC9000 A.4. The main differences are:
//
// 1. Our algorithm can transition into capable state right from testing state if ECN validation passes.
//
// 2. The sample algorithm fails ECN validation when all packets sent in testing are considered lost.
// Our algorithm does not do that. However, in that case, our algorithm stays in unknown state, where
// we send packets without ECT mark, which is effectively the same as failing the validation.
//
//
// Different state of ECN validation for the network path.
//
typedef enum ECN_VALIDATION_STATE {
ECN_VALIDATION_TESTING,
ECN_VALIDATION_UNKNOWN,
ECN_VALIDATION_CAPABLE,
ECN_VALIDATION_FAILED, // or not enabled by the app.
} ECN_VALIDATION_STATE;
//
// Represents all the per-path information of a connection.
//
@ -67,6 +101,16 @@ typedef struct QUIC_PATH {
//
uint8_t PartitionUpdated : 1;
//
// ECN validation state.
//
uint8_t EcnValidationState : 2;
//
// The ending time of ECN validation testing state in microseconds.
//
uint64_t EcnTestingEndingTime;
//
// The currently calculated path MTU.
//

Просмотреть файл

@ -506,6 +506,11 @@ CXPLAT_STATIC_ASSERT(
//
#define QUIC_DEFAULT_GREASE_QUIC_BIT_ENABLED FALSE
//
// The default value for enabling sender-side ECN support.
//
#define QUIC_DEFAULT_ECN_ENABLED FALSE
/*************************************************************
TRANSPORT PARAMETERS
*************************************************************/
@ -575,6 +580,7 @@ CXPLAT_STATIC_ASSERT(
#define QUIC_SETTING_MIGRATION_ENABLED "MigrationEnabled"
#define QUIC_SETTING_DATAGRAM_RECEIVE_ENABLED "DatagramReceiveEnabled"
#define QUIC_SETTING_GREASE_QUIC_BIT_ENABLED "GreaseQuicBitEnabled"
#define QUIC_SETTING_ECN_ENABLED "EcnEnabled"
#define QUIC_SETTING_INITIAL_WINDOW_PACKETS "InitialWindowPackets"
#define QUIC_SETTING_SEND_IDLE_TIMEOUT_MS "SendIdleTimeoutMs"

Просмотреть файл

@ -1196,6 +1196,28 @@ QuicSendFlush(
}
_Analysis_assume_(Builder.Metadata != NULL);
if (Builder.Path->EcnValidationState == ECN_VALIDATION_CAPABLE) {
Builder.EcnEctSet = TRUE;
} else if (Builder.Path->EcnValidationState == ECN_VALIDATION_TESTING) {
if (Builder.Path->EcnTestingEndingTime != 0) {
if (!CxPlatTimeAtOrBefore64(TimeNow, Builder.Path->EcnTestingEndingTime)) {
Builder.Path->EcnValidationState = ECN_VALIDATION_UNKNOWN;
QuicTraceLogConnInfo(
EcnValidationUnknown,
Connection,
"ECN unknown.");
}
} else {
uint32_t ThreePtosInUs =
QuicLossDetectionComputeProbeTimeout(
&Connection->LossDetection,
&Connection->Paths[0],
QUIC_CLOSE_PTO_COUNT);
Builder.Path->EcnTestingEndingTime = TimeNow + ThreePtosInUs;
}
Builder.EcnEctSet = TRUE;
}
QuicTraceEvent(
ConnFlushSend,
"[conn][%p] Flushing Send. Allowance=%u bytes",

Просмотреть файл

@ -255,6 +255,12 @@ typedef struct QUIC_SEND {
//
uint64_t LastFlushTime;
//
// The total number of packets sent with each corresponding ECT codepoint in all encryption
// level.
//
uint64_t NumPacketsSentWithEct;
//
// The value we send in MAX_DATA frames.
//

Просмотреть файл

@ -94,6 +94,7 @@ typedef struct QUIC_SEND_PACKET_FLAGS {
//
BOOLEAN IsAppLimited : 1;
BOOLEAN HasLastAckedPacketInfo : 1;
BOOLEAN EcnEctSet : 1;
#if DEBUG
BOOLEAN Freed : 1;
#endif

Просмотреть файл

@ -135,6 +135,9 @@ QuicSettingsSetDefault(
if (!Settings->IsSet.GreaseQuicBitEnabled) {
Settings->GreaseQuicBitEnabled = QUIC_DEFAULT_GREASE_QUIC_BIT_ENABLED;
}
if (!Settings->IsSet.EcnEnabled) {
Settings->EcnEnabled = QUIC_DEFAULT_ECN_ENABLED;
}
}
_IRQL_requires_max_(PASSIVE_LEVEL)
@ -270,6 +273,9 @@ QuicSettingsCopy(
if (!Destination->IsSet.GreaseQuicBitEnabled) {
Destination->GreaseQuicBitEnabled = Source->GreaseQuicBitEnabled;
}
if (!Destination->IsSet.EcnEnabled) {
Destination->EcnEnabled = Source->EcnEnabled;
}
}
_IRQL_requires_max_(PASSIVE_LEVEL)
@ -343,7 +349,7 @@ BOOLEAN
QuicSettingApply(
_Inout_ QUIC_SETTINGS_INTERNAL* Destination,
_In_ BOOLEAN OverWrite,
_In_ BOOLEAN AllowMtuChanges,
_In_ BOOLEAN AllowMtuAndEcnChanges,
_In_reads_bytes_(sizeof(QUIC_SETTINGS_INTERNAL))
const QUIC_SETTINGS_INTERNAL* Source
)
@ -499,7 +505,7 @@ QuicSettingApply(
}
}
if (AllowMtuChanges) {
if (AllowMtuAndEcnChanges) {
uint16_t MinimumMtu =
Destination->IsSet.MinimumMtu ? Destination->MinimumMtu : QUIC_DPLPMTUD_MIN_MTU;
uint16_t MaximumMtu =
@ -568,6 +574,15 @@ QuicSettingApply(
Destination->IsSet.GreaseQuicBitEnabled = TRUE;
}
if (AllowMtuAndEcnChanges) {
if (Source->IsSet.EcnEnabled && (!Destination->IsSet.EcnEnabled || OverWrite)) {
Destination->EcnEnabled = Source->EcnEnabled;
Destination->IsSet.EcnEnabled = TRUE;
}
} else if (Source->IsSet.EcnEnabled) {
return FALSE;
}
return TRUE;
}
@ -1129,6 +1144,16 @@ VersionSettingsFail:
&ValueLen);
Settings->GreaseQuicBitEnabled = !!Value;
}
if (!Settings->IsSet.EcnEnabled) {
Value = QUIC_DEFAULT_ECN_ENABLED;
ValueLen = sizeof(Value);
CxPlatStorageReadValue(
Storage,
QUIC_SETTING_ECN_ENABLED,
(uint8_t*)&Value,
&ValueLen);
Settings->EcnEnabled = !!Value;
}
}
_IRQL_requires_max_(PASSIVE_LEVEL)
@ -1188,6 +1213,7 @@ QuicSettingsDump(
QuicTraceLogVerbose(SettingCongestionControlAlgorithm, "[sett] CongestionControlAlgorithm = %hu", Settings->CongestionControlAlgorithm);
QuicTraceLogVerbose(SettingDestCidUpdateIdleTimeoutMs, "[sett] DestCidUpdateIdleTimeoutMs = %u", Settings->DestCidUpdateIdleTimeoutMs);
QuicTraceLogVerbose(SettingGreaseQuicBitEnabled, "[sett] GreaseQuicBitEnabled = %hhu", Settings->GreaseQuicBitEnabled);
QuicTraceLogVerbose(SettingEcnEnabled, "[sett] EcnEnabled = %hhu", Settings->EcnEnabled);
}
_IRQL_requires_max_(PASSIVE_LEVEL)
@ -1319,6 +1345,9 @@ QuicSettingsDumpNew(
if (Settings->IsSet.GreaseQuicBitEnabled) {
QuicTraceLogVerbose(SettingGreaseQuicBitEnabled, "[sett] GreaseQuicBitEnabled = %hhu", Settings->GreaseQuicBitEnabled);
}
if (Settings->IsSet.EcnEnabled) {
QuicTraceLogVerbose(SettingEcnEnabled, "[sett] EcnEnabled = %hhu", Settings->EcnEnabled);
}
}
#define SETTINGS_SIZE_THRU_FIELD(SettingsType, Field) \
@ -1478,7 +1507,8 @@ QuicSettingsSettingsToInternal(
SETTING_COPY_TO_INTERNAL(MigrationEnabled, Settings, InternalSettings);
SETTING_COPY_TO_INTERNAL(DatagramReceiveEnabled, Settings, InternalSettings);
SETTING_COPY_TO_INTERNAL(ServerResumptionLevel, Settings, InternalSettings);
SETTING_COPY_TO_INTERNAL(GreaseQuicBitEnabled, Settings, InternalSettings); // We can't copy it via sized version due to bit field operation not allowed on it.
SETTING_COPY_TO_INTERNAL(GreaseQuicBitEnabled, Settings, InternalSettings);
SETTING_COPY_TO_INTERNAL(EcnEnabled, Settings, InternalSettings);
//
// N.B. Anything after this needs to be size checked
@ -1571,7 +1601,8 @@ QuicSettingsGetSettings(
SETTING_COPY_FROM_INTERNAL(MigrationEnabled, Settings, InternalSettings);
SETTING_COPY_FROM_INTERNAL(DatagramReceiveEnabled, Settings, InternalSettings);
SETTING_COPY_FROM_INTERNAL(ServerResumptionLevel, Settings, InternalSettings);
SETTING_COPY_FROM_INTERNAL(GreaseQuicBitEnabled, Settings, InternalSettings); // We can't copy it via sized version due to bit field operation not allowed on it.
SETTING_COPY_FROM_INTERNAL(GreaseQuicBitEnabled, Settings, InternalSettings);
SETTING_COPY_FROM_INTERNAL(EcnEnabled, Settings, InternalSettings);
//
// N.B. Anything after this needs to be size checked

Просмотреть файл

@ -51,7 +51,8 @@ typedef struct QUIC_SETTINGS_INTERNAL {
uint64_t CongestionControlAlgorithm : 1;
uint64_t DestCidUpdateIdleTimeoutMs : 1;
uint64_t GreaseQuicBitEnabled : 1;
uint64_t RESERVED : 27;
uint64_t EcnEnabled : 1;
uint64_t RESERVED : 26;
} IsSet;
};
@ -84,6 +85,7 @@ typedef struct QUIC_SETTINGS_INTERNAL {
uint8_t ServerResumptionLevel : 2; // QUIC_SERVER_RESUMPTION_LEVEL
uint8_t VersionNegotiationExtEnabled : 1;
uint8_t GreaseQuicBitEnabled : 1;
uint8_t EcnEnabled : 1;
QUIC_VERSION_SETTINGS* VersionSettings;
uint16_t MinimumMtu;
uint16_t MaximumMtu;
@ -123,7 +125,7 @@ BOOLEAN
QuicSettingApply(
_Inout_ QUIC_SETTINGS_INTERNAL* Destination,
_In_ BOOLEAN OverWrite,
_In_ BOOLEAN AllowMtuChanges,
_In_ BOOLEAN AllowMtuAndEcnChanges,
_In_reads_bytes_(sizeof(QUIC_SETTINGS_INTERNAL))
const QUIC_SETTINGS_INTERNAL* Source
);

Просмотреть файл

@ -115,6 +115,7 @@ TEST(SettingsTest, TestAllSettingsFieldsSet)
SETTINGS_FEATURE_SET_TEST(ServerResumptionLevel, QuicSettingsSettingsToInternal);
SETTINGS_FEATURE_SET_TEST(DestCidUpdateIdleTimeoutMs, QuicSettingsSettingsToInternal);
SETTINGS_FEATURE_SET_TEST(GreaseQuicBitEnabled, QuicSettingsSettingsToInternal);
SETTINGS_FEATURE_SET_TEST(EcnEnabled, QuicSettingsSettingsToInternal);
Settings.IsSetFlags = 0;
Settings.IsSet.RESERVED = ~Settings.IsSet.RESERVED;
@ -190,6 +191,7 @@ TEST(SettingsTest, TestAllSettingsFieldsGet)
SETTINGS_FEATURE_GET_TEST(ServerResumptionLevel, QuicSettingsGetSettings);
SETTINGS_FEATURE_GET_TEST(DestCidUpdateIdleTimeoutMs, QuicSettingsGetSettings);
SETTINGS_FEATURE_GET_TEST(GreaseQuicBitEnabled, QuicSettingsGetSettings);
SETTINGS_FEATURE_GET_TEST(EcnEnabled, QuicSettingsGetSettings);
Settings.IsSetFlags = 0;
Settings.IsSet.RESERVED = ~Settings.IsSet.RESERVED;

Просмотреть файл

@ -723,17 +723,31 @@ namespace Microsoft.Quic
}
}
[NativeTypeName("uint32_t : 27")]
internal uint RESERVED
[NativeTypeName("uint32_t : 1")]
internal uint EcnCapable
{
get
{
return (_bitfield >> 5) & 0x7FFFFFFu;
return (_bitfield >> 5) & 0x1u;
}
set
{
_bitfield = (_bitfield & ~(0x7FFFFFFu << 5)) | ((value & 0x7FFFFFFu) << 5);
_bitfield = (_bitfield & ~(0x1u << 5)) | ((value & 0x1u) << 5);
}
}
[NativeTypeName("uint32_t : 26")]
internal uint RESERVED
{
get
{
return (_bitfield >> 6) & 0x3FFFFFFu;
}
set
{
_bitfield = (_bitfield & ~(0x3FFFFFFu << 6)) | ((value & 0x3FFFFFFu) << 6);
}
}
@ -1145,7 +1159,7 @@ namespace Microsoft.Quic
}
[NativeTypeName("uint8_t : 1")]
internal byte RESERVED
internal byte EcnEnabled
{
get
{
@ -1660,17 +1674,31 @@ namespace Microsoft.Quic
}
}
[NativeTypeName("uint64_t : 31")]
internal ulong RESERVED
[NativeTypeName("uint64_t : 1")]
internal ulong EcnEnabled
{
get
{
return (_bitfield >> 33) & 0x7FFFFFFFUL;
return (_bitfield >> 33) & 0x1UL;
}
set
{
_bitfield = (_bitfield & ~(0x7FFFFFFFUL << 33)) | ((value & 0x7FFFFFFFUL) << 33);
_bitfield = (_bitfield & ~(0x1UL << 33)) | ((value & 0x1UL) << 33);
}
}
[NativeTypeName("uint64_t : 30")]
internal ulong RESERVED
{
get
{
return (_bitfield >> 34) & 0x3FFFFFFFUL;
}
set
{
_bitfield = (_bitfield & ~(0x3FFFFFFFUL << 34)) | ((value & 0x3FFFFFFFUL) << 34);
}
}
}

Просмотреть файл

@ -1751,6 +1751,26 @@ tracepoint(CLOG_CONNECTION_C, ConnAssignWorker , arg2, arg3);\
/*----------------------------------------------------------
// Decoder Ring for ConnEcnCapable
// [conn][%p] Ecn: IsCapable=%hu
// QuicTraceEvent(
ConnEcnCapable,
"[conn][%p] Ecn: IsCapable=%hu",
Connection,
Connection->Paths[0].EcnValidationState == ECN_VALIDATION_CAPABLE);
// arg2 = arg2 = Connection = arg2
// arg3 = arg3 = Connection->Paths[0].EcnValidationState == ECN_VALIDATION_CAPABLE = arg3
----------------------------------------------------------*/
#ifndef _clog_4_ARGS_TRACE_ConnEcnCapable
#define _clog_4_ARGS_TRACE_ConnEcnCapable(uniqueId, encoded_arg_string, arg2, arg3)\
tracepoint(CLOG_CONNECTION_C, ConnEcnCapable , arg2, arg3);\
#endif
/*----------------------------------------------------------
// Decoder Ring for ConnVersionSet
// [conn][%p] QUIC Version: %u

Просмотреть файл

@ -1952,6 +1952,29 @@ TRACEPOINT_EVENT(CLOG_CONNECTION_C, ConnAssignWorker,
/*----------------------------------------------------------
// Decoder Ring for ConnEcnCapable
// [conn][%p] Ecn: IsCapable=%hu
// QuicTraceEvent(
ConnEcnCapable,
"[conn][%p] Ecn: IsCapable=%hu",
Connection,
Connection->Paths[0].EcnValidationState == ECN_VALIDATION_CAPABLE);
// arg2 = arg2 = Connection = arg2
// arg3 = arg3 = Connection->Paths[0].EcnValidationState == ECN_VALIDATION_CAPABLE = arg3
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_CONNECTION_C, ConnEcnCapable,
TP_ARGS(
const void *, arg2,
unsigned short, arg3),
TP_FIELDS(
ctf_integer_hex(uint64_t, arg2, arg2)
ctf_integer(unsigned short, arg3, arg3)
)
)
/*----------------------------------------------------------
// Decoder Ring for ConnVersionSet
// [conn][%p] QUIC Version: %u

Просмотреть файл

@ -447,6 +447,55 @@ tracepoint(CLOG_LOSS_DETECTION_C, ConnError , arg2, arg3);\
/*----------------------------------------------------------
// Decoder Ring for ConnEcnCapable
// [conn][%p] Ecn: IsCapable=%hu
// QuicTraceEvent(
ConnEcnCapable,
"[conn][%p] Ecn: IsCapable=%hu",
Connection,
TRUE);
// arg2 = arg2 = Connection = arg2
// arg3 = arg3 = TRUE = arg3
----------------------------------------------------------*/
#ifndef _clog_4_ARGS_TRACE_ConnEcnCapable
#define _clog_4_ARGS_TRACE_ConnEcnCapable(uniqueId, encoded_arg_string, arg2, arg3)\
tracepoint(CLOG_LOSS_DETECTION_C, ConnEcnCapable , arg2, arg3);\
#endif
/*----------------------------------------------------------
// Decoder Ring for ConnEcnFailed
// [conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu
// QuicTraceEvent(
ConnEcnFailed,
"[conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu",
Connection,
EncryptLevel,
Packets->EcnEctCounter, Packets->EcnCeCounter,
Connection->Send.NumPacketsSentWithEct,
EctCeDeltaSum,
Path->EcnValidationState);
// arg2 = arg2 = Connection = arg2
// arg3 = arg3 = EncryptLevel = arg3
// arg4 = arg4 = Packets->EcnEctCounter = arg4
// arg5 = arg5 = Packets->EcnCeCounter = arg5
// arg6 = arg6 = Connection->Send.NumPacketsSentWithEct = arg6
// arg7 = arg7 = EctCeDeltaSum = arg7
// arg8 = arg8 = Path->EcnValidationState = arg8
----------------------------------------------------------*/
#ifndef _clog_9_ARGS_TRACE_ConnEcnFailed
#define _clog_9_ARGS_TRACE_ConnEcnFailed(uniqueId, encoded_arg_string, arg2, arg3, arg4, arg5, arg6, arg7, arg8)\
tracepoint(CLOG_LOSS_DETECTION_C, ConnEcnFailed , arg2, arg3, arg4, arg5, arg6, arg7, arg8);\
#endif
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -486,3 +486,68 @@ TRACEPOINT_EVENT(CLOG_LOSS_DETECTION_C, ConnError,
ctf_string(arg3, arg3)
)
)
/*----------------------------------------------------------
// Decoder Ring for ConnEcnCapable
// [conn][%p] Ecn: IsCapable=%hu
// QuicTraceEvent(
ConnEcnCapable,
"[conn][%p] Ecn: IsCapable=%hu",
Connection,
TRUE);
// arg2 = arg2 = Connection = arg2
// arg3 = arg3 = TRUE = arg3
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_LOSS_DETECTION_C, ConnEcnCapable,
TP_ARGS(
const void *, arg2,
unsigned short, arg3),
TP_FIELDS(
ctf_integer_hex(uint64_t, arg2, arg2)
ctf_integer(unsigned short, arg3, arg3)
)
)
/*----------------------------------------------------------
// Decoder Ring for ConnEcnFailed
// [conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu
// QuicTraceEvent(
ConnEcnFailed,
"[conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu",
Connection,
EncryptLevel,
Packets->EcnEctCounter, Packets->EcnCeCounter,
Connection->Send.NumPacketsSentWithEct,
EctCeDeltaSum,
Path->EcnValidationState);
// arg2 = arg2 = Connection = arg2
// arg3 = arg3 = EncryptLevel = arg3
// arg4 = arg4 = Packets->EcnEctCounter = arg4
// arg5 = arg5 = Packets->EcnCeCounter = arg5
// arg6 = arg6 = Connection->Send.NumPacketsSentWithEct = arg6
// arg7 = arg7 = EctCeDeltaSum = arg7
// arg8 = arg8 = Path->EcnValidationState = arg8
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_LOSS_DETECTION_C, ConnEcnFailed,
TP_ARGS(
const void *, arg2,
int, arg3,
unsigned long long, arg4,
unsigned long long, arg5,
unsigned long long, arg6,
long long, arg7,
unsigned short, arg8),
TP_FIELDS(
ctf_integer_hex(uint64_t, arg2, arg2)
ctf_integer(int, arg3, arg3)
ctf_integer(uint64_t, arg4, arg4)
ctf_integer(uint64_t, arg5, arg5)
ctf_integer(uint64_t, arg6, arg6)
ctf_integer(int64_t, arg7, arg7)
ctf_integer(unsigned short, arg8, arg8)
)
)

Просмотреть файл

@ -18,6 +18,10 @@
#define _clog_MACRO_QuicTraceLogStreamVerbose 1
#define QuicTraceLogStreamVerbose(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
#endif
#ifndef _clog_MACRO_QuicTraceLogConnInfo
#define _clog_MACRO_QuicTraceLogConnInfo 1
#define QuicTraceLogConnInfo(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
#endif
#ifndef _clog_MACRO_QuicTraceLogConnVerbose
#define _clog_MACRO_QuicTraceLogConnVerbose 1
#define QuicTraceLogConnVerbose(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
@ -71,6 +75,24 @@ tracepoint(CLOG_SEND_C, ClearSendFlags , arg1, arg3);\
/*----------------------------------------------------------
// Decoder Ring for EcnValidationUnknown
// [conn][%p] ECN unknown.
// QuicTraceLogConnInfo(
EcnValidationUnknown,
Connection,
"ECN unknown.");
// arg1 = arg1 = Connection = arg1
----------------------------------------------------------*/
#ifndef _clog_3_ARGS_TRACE_EcnValidationUnknown
#define _clog_3_ARGS_TRACE_EcnValidationUnknown(uniqueId, arg1, encoded_arg_string)\
tracepoint(CLOG_SEND_C, EcnValidationUnknown , arg1);\
#endif
/*----------------------------------------------------------
// Decoder Ring for ScheduleSendFlags
// [conn][%p] Scheduling flags 0x%x to 0x%x

Просмотреть файл

@ -51,6 +51,25 @@ TRACEPOINT_EVENT(CLOG_SEND_C, ClearSendFlags,
/*----------------------------------------------------------
// Decoder Ring for EcnValidationUnknown
// [conn][%p] ECN unknown.
// QuicTraceLogConnInfo(
EcnValidationUnknown,
Connection,
"ECN unknown.");
// arg1 = arg1 = Connection = arg1
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_SEND_C, EcnValidationUnknown,
TP_ARGS(
const void *, arg1),
TP_FIELDS(
ctf_integer_hex(uint64_t, arg1, arg1)
)
)
/*----------------------------------------------------------
// Decoder Ring for ScheduleSendFlags
// [conn][%p] Scheduling flags 0x%x to 0x%x

Просмотреть файл

@ -647,6 +647,21 @@ tracepoint(CLOG_SETTINGS_C, SettingGreaseQuicBitEnabled , arg2);\
/*----------------------------------------------------------
// Decoder Ring for SettingEcnEnabled
// [sett] EcnEnabled = %hhu
// QuicTraceLogVerbose(SettingEcnEnabled, "[sett] EcnEnabled = %hhu", Settings->EcnEnabled);
// arg2 = arg2 = Settings->EcnEnabled = arg2
----------------------------------------------------------*/
#ifndef _clog_3_ARGS_TRACE_SettingEcnEnabled
#define _clog_3_ARGS_TRACE_SettingEcnEnabled(uniqueId, encoded_arg_string, arg2)\
tracepoint(CLOG_SETTINGS_C, SettingEcnEnabled , arg2);\
#endif
/*----------------------------------------------------------
// Decoder Ring for SettingsLoadInvalidAcceptableVersion
// Invalid AcceptableVersion loaded from storage! 0x%x at position %d

Просмотреть файл

@ -666,6 +666,22 @@ TRACEPOINT_EVENT(CLOG_SETTINGS_C, SettingGreaseQuicBitEnabled,
/*----------------------------------------------------------
// Decoder Ring for SettingEcnEnabled
// [sett] EcnEnabled = %hhu
// QuicTraceLogVerbose(SettingEcnEnabled, "[sett] EcnEnabled = %hhu", Settings->EcnEnabled);
// arg2 = arg2 = Settings->EcnEnabled = arg2
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_SETTINGS_C, SettingEcnEnabled,
TP_ARGS(
unsigned char, arg2),
TP_FIELDS(
ctf_integer(unsigned char, arg2, arg2)
)
)
/*----------------------------------------------------------
// Decoder Ring for SettingsLoadInvalidAcceptableVersion
// Invalid AcceptableVersion loaded from storage! 0x%x at position %d

Просмотреть файл

@ -473,7 +473,8 @@ typedef struct QUIC_STATISTICS_V2 {
uint32_t ResumptionAttempted : 1;
uint32_t ResumptionSucceeded : 1;
uint32_t GreaseBitNegotiated : 1; // Set if we negotiated the GREASE bit.
uint32_t RESERVED : 27;
uint32_t EcnCapable : 1;
uint32_t RESERVED : 26;
uint32_t Rtt; // In microseconds
uint32_t MinRtt; // In microseconds
uint32_t MaxRtt; // In microseconds
@ -629,7 +630,8 @@ typedef struct QUIC_SETTINGS {
uint64_t MtuDiscoveryMissingProbeCount : 1;
uint64_t DestCidUpdateIdleTimeoutMs : 1;
uint64_t GreaseQuicBitEnabled : 1;
uint64_t RESERVED : 31;
uint64_t EcnEnabled : 1;
uint64_t RESERVED : 30;
} IsSet;
};
@ -663,7 +665,7 @@ typedef struct QUIC_SETTINGS {
uint8_t DatagramReceiveEnabled : 1;
uint8_t ServerResumptionLevel : 2; // QUIC_SERVER_RESUMPTION_LEVEL
uint8_t GreaseQuicBitEnabled : 1;
uint8_t RESERVED : 1;
uint8_t EcnEnabled : 1;
uint8_t MaxOperationsPerDrain;
uint8_t MtuDiscoveryMissingProbeCount;
uint32_t DestCidUpdateIdleTimeoutMs;

Просмотреть файл

@ -438,6 +438,7 @@ public:
MsQuicSettings& SetCongestionControlAlgorithm(QUIC_CONGESTION_CONTROL_ALGORITHM Cc) { CongestionControlAlgorithm = (uint8_t)Cc; IsSet.CongestionControlAlgorithm = TRUE; return *this; }
MsQuicSettings& SetDestCidUpdateIdleTimeoutMs(uint32_t Value) { DestCidUpdateIdleTimeoutMs = Value; IsSet.DestCidUpdateIdleTimeoutMs = TRUE; return *this; }
MsQuicSettings& SetGreaseQuicBitEnabled(bool Value) { GreaseQuicBitEnabled = Value; IsSet.GreaseQuicBitEnabled = TRUE; return *this; }
MsQuicSettings& SetEcnEnabled(bool Value) { EcnEnabled = Value; IsSet.EcnEnabled = TRUE; return *this; }
QUIC_STATUS
SetGlobal() const noexcept {

Просмотреть файл

@ -1936,6 +1936,46 @@
name="ID2"
/>
</template>
<template tid="tid_CONN_ECN_CAPABLE">
<data
inType="win:Pointer"
name="Connection"
/>
<data
inType="win:UInt32"
name="IsCapable"
/>
</template>
<template tid="tid_CONN_ECN_FAILED">
<data
inType="win:Pointer"
name="Connection"
/>
<data
inType="win:UInt32"
name="EncryptLevel"
/>
<data
inType="win:UInt64"
name="EcnEctCounter"
/>
<data
inType="win:UInt64"
name="EcnCeCounter"
/>
<data
inType="win:UInt64"
name="NumPacketsSentWithEct"
/>
<data
inType="win:Int64"
name="EctCeDeltaSum"
/>
<data
inType="win:UInt8"
name="State"
/>
</template>
</templates>
<events>
<!-- Don't use value=0 as some parsers don't like it -->
@ -3020,6 +3060,24 @@
template="tid_CONN_BBR"
value="5186"
/>
<event
keywords="ut:Connection ut:LowVolume"
level="win:Informational"
message="$(string.Etw.ConnEcnCapable)"
opcode="Connection"
symbol="QuicConnEcnCapable"
template="tid_CONN_ECN_CAPABLE"
value="5187"
/>
<event
keywords="ut:Connection ut:LowVolume"
level="win:Informational"
message="$(string.Etw.ConnEcnFailed)"
opcode="Connection"
symbol="QuicConnEcnFailed"
template="tid_CONN_ECN_FAILED"
value="5188"
/>
<!-- 6144 - 7167 | Stream Events -->
<event
keywords="ut:Stream ut:LowVolume ut:RPS"
@ -4499,6 +4557,14 @@
id="Etw.Bbr"
value="[conn][%1] BBR: State=%2 RState=%3 CongestionWindow=%4 BytesInFlight=%5 BytesInFlightMax=%6 MinRttEst=%7 EstBw=%8 AppLimited=%9"
/>
<string
id="Etw.ConnEcnCapable"
value="[conn][%1] Ecn: IsCapable=%2"
/>
<string
id="Etw.ConnEcnFailed"
value="[conn][%1][%2] ECN failed: EctCnt %3 CeCnt %4 TxEct %5 DeltaSum %6 State %7"
/>
</stringTable>
</resources>
</localization>

Просмотреть файл

@ -1223,6 +1223,58 @@
],
"macroName": "QuicTraceEvent"
},
"ConnEcnCapable": {
"ModuleProperites": {},
"TraceString": "[conn][%p] Ecn: IsCapable=%hu",
"UniqueId": "ConnEcnCapable",
"splitArgs": [
{
"DefinationEncoding": "p",
"MacroVariableName": "arg2"
},
{
"DefinationEncoding": "hu",
"MacroVariableName": "arg3"
}
],
"macroName": "QuicTraceEvent"
},
"ConnEcnFailed": {
"ModuleProperites": {},
"TraceString": "[conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu",
"UniqueId": "ConnEcnFailed",
"splitArgs": [
{
"DefinationEncoding": "p",
"MacroVariableName": "arg2"
},
{
"DefinationEncoding": "d",
"MacroVariableName": "arg3"
},
{
"DefinationEncoding": "llu",
"MacroVariableName": "arg4"
},
{
"DefinationEncoding": "llu",
"MacroVariableName": "arg5"
},
{
"DefinationEncoding": "llu",
"MacroVariableName": "arg6"
},
{
"DefinationEncoding": "lld",
"MacroVariableName": "arg7"
},
{
"DefinationEncoding": "hu",
"MacroVariableName": "arg8"
}
],
"macroName": "QuicTraceEvent"
},
"ConnError": {
"ModuleProperites": {},
"TraceString": "[conn][%p] ERROR, %s.",
@ -3455,6 +3507,46 @@
],
"macroName": "QuicTraceLogConnVerbose"
},
"EcnEnabled": {
"ModuleProperites": {},
"TraceString": "[conn][%p] Updated ECN enabled to %hhu",
"UniqueId": "EcnEnabled",
"splitArgs": [
{
"DefinationEncoding": "p",
"MacroVariableName": "arg1"
},
{
"DefinationEncoding": "hhu",
"MacroVariableName": "arg3"
}
],
"macroName": "QuicTraceLogConnVerbose"
},
"EcnValidationSuccess": {
"ModuleProperites": {},
"TraceString": "[conn][%p] ECN succeeded.",
"UniqueId": "EcnValidationSuccess",
"splitArgs": [
{
"DefinationEncoding": "p",
"MacroVariableName": "arg1"
}
],
"macroName": "QuicTraceLogConnInfo"
},
"EcnValidationUnknown": {
"ModuleProperites": {},
"TraceString": "[conn][%p] ECN unknown.",
"UniqueId": "EcnValidationUnknown",
"splitArgs": [
{
"DefinationEncoding": "p",
"MacroVariableName": "arg1"
}
],
"macroName": "QuicTraceLogConnInfo"
},
"EncodeMaxDatagramFrameSize": {
"ModuleProperites": {},
"TraceString": "[conn][%p] TP: Max Datagram Frame Size (%llu bytes)",
@ -10011,6 +10103,18 @@
],
"macroName": "QuicTraceLogVerbose"
},
"SettingEcnEnabled": {
"ModuleProperites": {},
"TraceString": "[sett] EcnEnabled = %hhu",
"UniqueId": "SettingEcnEnabled",
"splitArgs": [
{
"DefinationEncoding": "hhu",
"MacroVariableName": "arg2"
}
],
"macroName": "QuicTraceLogVerbose"
},
"SettingGreaseQuicBitEnabled": {
"ModuleProperites": {},
"TraceString": "[sett] GreaseQuicBitEnabled = %hhu",
@ -12419,6 +12523,16 @@
"TraceID": "ConnDropPacketEx",
"EncodingString": "[conn][%p] DROP packet Value=%llu Dst=%!ADDR! Src=%!ADDR! Reason=%s."
},
{
"UniquenessHash": "9c10f710-84bf-766d-0da5-9e31796cf450",
"TraceID": "ConnEcnCapable",
"EncodingString": "[conn][%p] Ecn: IsCapable=%hu"
},
{
"UniquenessHash": "5d7016bf-5f7f-7469-48f6-6212e8c8572b",
"TraceID": "ConnEcnFailed",
"EncodingString": "[conn][%p][%d] ECN failed: EctCnt %llu CeCnt %llu TxEct %llu DeltaSum %lld State %hu"
},
{
"UniquenessHash": "0ebbffbe-69d8-3f2b-949d-d93cdd7f8b99",
"TraceID": "ConnError",
@ -13114,6 +13228,21 @@
"TraceID": "DrainCrypto",
"EncodingString": "[conn][%p] Draining %u crypto bytes"
},
{
"UniquenessHash": "b978c2f3-b3a9-e423-d442-4860da0dc056",
"TraceID": "EcnEnabled",
"EncodingString": "[conn][%p] Updated ECN enabled to %hhu"
},
{
"UniquenessHash": "2fd085ac-656c-8e23-9f2f-a7c4116dbe83",
"TraceID": "EcnValidationSuccess",
"EncodingString": "[conn][%p] ECN succeeded."
},
{
"UniquenessHash": "59a602dd-2f8a-6b1d-4fe2-2ac0c1316f05",
"TraceID": "EcnValidationUnknown",
"EncodingString": "[conn][%p] ECN unknown."
},
{
"UniquenessHash": "d276c997-5dfb-645e-b54a-7ec567967851",
"TraceID": "EncodeMaxDatagramFrameSize",
@ -15144,6 +15273,11 @@
"TraceID": "SettingDumpVersionNegoExtEnabled",
"EncodingString": "[sett] Version Negotiation Ext Enabled = %hhu"
},
{
"UniquenessHash": "12ca7216-75fc-41d7-f9ce-3e93bd2fea64",
"TraceID": "SettingEcnEnabled",
"EncodingString": "[sett] EcnEnabled = %hhu"
},
{
"UniquenessHash": "eb526b83-28f4-1046-57bf-7727ba70f36d",
"TraceID": "SettingGreaseQuicBitEnabled",

Просмотреть файл

@ -107,7 +107,8 @@ public:
MsQuicSettings()
.SetDisconnectTimeoutMs(PERF_DEFAULT_DISCONNECT_TIMEOUT)
.SetIdleTimeoutMs(HPS_DEFAULT_IDLE_TIMEOUT)
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl),
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl)
.SetEcnEnabled(PerfDefaultEcnEnabled),
MsQuicCredentialConfig(
QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION)};

Просмотреть файл

@ -41,3 +41,4 @@ Abstract:
extern QUIC_EXECUTION_PROFILE PerfDefaultExecutionProfile;
extern QUIC_CONGESTION_CONTROL_ALGORITHM PerfDefaultCongestionControl;
extern uint8_t PerfDefaultEcnEnabled;

Просмотреть файл

@ -140,7 +140,8 @@ private:
.SetIdleTimeoutMs(PERF_DEFAULT_IDLE_TIMEOUT)
.SetSendBufferingEnabled(false)
.SetServerResumptionLevel(QUIC_SERVER_RESUME_AND_ZERORTT)
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl)};
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl)
.SetEcnEnabled(PerfDefaultEcnEnabled)};
MsQuicListener Listener {Registration, ListenerCallbackStatic, this};
QUIC_ADDR LocalAddr;
CXPLAT_EVENT* StopEvent {nullptr};

Просмотреть файл

@ -168,7 +168,8 @@ public:
.SetDisconnectTimeoutMs(PERF_DEFAULT_DISCONNECT_TIMEOUT)
.SetIdleTimeoutMs(PERF_DEFAULT_IDLE_TIMEOUT)
.SetSendBufferingEnabled(false)
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl),
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl)
.SetEcnEnabled(PerfDefaultEcnEnabled),
MsQuicCredentialConfig(
QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION)};

Просмотреть файл

@ -27,6 +27,7 @@ char Buffer[BufferLength];
PerfBase* TestToRun;
QUIC_EXECUTION_PROFILE PerfDefaultExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY;
QUIC_CONGESTION_CONTROL_ALGORITHM PerfDefaultCongestionControl = QUIC_CONGESTION_CONTROL_ALGORITHM_CUBIC;
uint8_t PerfDefaultEcnEnabled = false;
#include "quic_datapath.h"
@ -97,6 +98,7 @@ PrintHelp(
" -exec:<profile> Execution profile to use {lowlat, maxtput, scavenger, realtime}.\n"
" -cc:<algo> Congestion control algorithm to use {cubic, bbr}.\n"
" -pollidle:<time_us> Amount of time to poll while idle before sleeping (default: 0).\n"
" -ecn:<0/1> Enables/disables sender-side ECN support. (def:0)\n"
#ifndef _KERNEL_MODE
" -cpu:<cpu_index> Specify the processor(s) to use.\n"
" -cipher:<value> Decimal value of 1 or more QUIC_ALLOWED_CIPHER_SUITE_FLAGS.\n"
@ -252,6 +254,8 @@ QuicMainStart(
}
}
TryGetValue(argc, argv, "ecn", &PerfDefaultEcnEnabled);
if (ServerMode) {
TestToRun = new(std::nothrow) PerfServer(SelfSignedCredConfig);
} else {

Просмотреть файл

@ -117,7 +117,8 @@ private:
MsQuicSettings()
.SetConnFlowControlWindow(PERF_DEFAULT_CONN_FLOW_CONTROL)
.SetIdleTimeoutMs(TPUT_DEFAULT_IDLE_TIMEOUT)
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl),
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl)
.SetEcnEnabled(PerfDefaultEcnEnabled),
MsQuicCredentialConfig(
QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION)};

Просмотреть файл

@ -156,6 +156,8 @@ namespace QuicTrace.DataModel
ConnTimerCancel,
ConnTimerExpire,
ConnBbr,
ConnEcnCapable,
ConnEcnFailed,
StreamCreated = 6144,
StreamDestroyed,

Просмотреть файл

@ -514,6 +514,11 @@ QuicTestStreamBlockUnblockConnFlowControl(
_In_ BOOLEAN Bidirectional
);
void
QuicTestEcn(
_In_ int Family
);
//
// QuicDrill tests
//
@ -1146,4 +1151,7 @@ typedef struct {
#define IOCTL_QUIC_RUN_STREAM_BLOCK_UNBLOCK_CONN_FLOW_CONTROL \
QUIC_CTL_CODE(107, METHOD_BUFFERED, FILE_WRITE_DATA)
#define QUIC_MAX_IOCTL_FUNC_CODE 107
#define IOCTL_QUIC_RUN_ECN \
QUIC_CTL_CODE(108, METHOD_BUFFERED, FILE_WRITE_DATA)
#define QUIC_MAX_IOCTL_FUNC_CODE 108

Просмотреть файл

@ -508,6 +508,14 @@ TEST_P(WithBool, RejectConnection) {
}
#ifdef QUIC_TEST_DATAPATH_HOOKS_ENABLED
TEST_P(WithFamilyArgs, Ecn) {
TestLoggerT<ParamType> Logger("Ecn", GetParam());
if (TestingKernelMode) {
ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_ECN, GetParam().Family));
} else {
QuicTestEcn(GetParam().Family);
}
}
TEST_P(WithFamilyArgs, LocalPathChanges) {
TestLoggerT<ParamType> Logger("QuicTestLocalPathChanges", GetParam());

Просмотреть файл

@ -479,6 +479,7 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] =
sizeof(UINT8),
sizeof(UINT8),
sizeof(BOOLEAN),
sizeof(INT32),
};
CXPLAT_STATIC_ASSERT(
@ -1318,6 +1319,11 @@ QuicTestCtlEvtIoDeviceControl(
QuicTestCtlRun(QuicTestStreamBlockUnblockConnFlowControl(Params->Bidirectional));
break;
case IOCTL_QUIC_RUN_ECN:
CXPLAT_FRE_ASSERT(Params != nullptr);
QuicTestCtlRun(QuicTestEcn(Params->Family));
break;
default:
Status = STATUS_NOT_IMPLEMENTED;
break;

Просмотреть файл

@ -1903,7 +1903,7 @@ public:
}
};
void SettingApplyTests(HQUIC Handle, uint32_t Param, bool AllowMtuChange = true) {
void SettingApplyTests(HQUIC Handle, uint32_t Param, bool AllowMtuEcnChanges = true) {
struct TestSpec {
uint64_t Value;
QUIC_STATUS Status;
@ -2055,8 +2055,22 @@ void SettingApplyTests(HQUIC Handle, uint32_t Param, bool AllowMtuChange = true)
sizeof(QUIC_SETTINGS),
&Settings);
TEST_TRUE((AllowMtuChange && Status == QUIC_STATUS_SUCCESS) ||
(!AllowMtuChange && Status == QUIC_STATUS_INVALID_PARAMETER));
TEST_TRUE((AllowMtuEcnChanges && Status == QUIC_STATUS_SUCCESS) ||
(!AllowMtuEcnChanges && Status == QUIC_STATUS_INVALID_PARAMETER));
}
{
QUIC_SETTINGS Settings{0};
Settings.IsSet.EcnEnabled = TRUE;
Settings.EcnEnabled = TRUE;
QUIC_STATUS Status =
MsQuic->SetParam(
Handle,
Param,
sizeof(QUIC_SETTINGS),
&Settings);
TEST_TRUE((AllowMtuEcnChanges && Status == QUIC_STATUS_SUCCESS) ||
(!AllowMtuEcnChanges && Status == QUIC_STATUS_INVALID_PARAMETER));
}
//
@ -3458,7 +3472,7 @@ void QuicTestConnectionParam()
//
{
TestScopeLogger LogScope3("After ConnectionStart");
// Internally AllowMtuChanges become FALSE
// Internally AllowMtuEcnChanges become FALSE
MsQuicConnection Connection(Registration);
TEST_QUIC_SUCCEEDED(Connection.GetInitStatus());
TEST_QUIC_SUCCEEDED(

Просмотреть файл

@ -2165,6 +2165,188 @@ QuicTestAbortReceive(
TEST_TRUE(RecvContext.ServerStreamShutdown.WaitTimeout(TestWaitTimeout));
}
struct EcnTestContext {
CxPlatEvent ServerStreamRecv;
CxPlatEvent ServerStreamShutdown;
MsQuicStream* ServerStream {nullptr};
bool ServerStreamHasShutdown {false};
static QUIC_STATUS StreamCallback(_In_ MsQuicStream* Stream, _In_opt_ void* Context, _Inout_ QUIC_STREAM_EVENT* Event) {
auto TestContext = (EcnTestContext*)Context;
if (Event->Type == QUIC_STREAM_EVENT_RECEIVE) {
TestContext->ServerStreamRecv.Set();
} else if (Event->Type == QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE) {
TestContext->ServerStreamHasShutdown = true;
TestContext->ServerStreamShutdown.Set();
Stream->ConnectionShutdown(1);
}
return QUIC_STATUS_SUCCESS;
}
static QUIC_STATUS ConnCallback(_In_ MsQuicConnection*, _In_opt_ void* Context, _Inout_ QUIC_CONNECTION_EVENT* Event) {
auto TestContext = (EcnTestContext*)Context;
if (Event->Type == QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED) {
TestContext->ServerStream = new(std::nothrow) MsQuicStream(Event->PEER_STREAM_STARTED.Stream, CleanUpAutoDelete, StreamCallback, Context);
}
return QUIC_STATUS_SUCCESS;
}
};
void
QuicTestEcn(
_In_ int Family
)
{
QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6;
//
// Postive ECN test.
//
{
TestScopeLogger logScope("Postive ECN test");
MsQuicRegistration Registration;
TEST_QUIC_SUCCEEDED(Registration.GetInitStatus());
MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetPeerUnidiStreamCount(1), ServerSelfSignedCredConfig);
TEST_QUIC_SUCCEEDED(ServerConfiguration.GetInitStatus());
MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetEcnEnabled(true), MsQuicCredentialConfig());
TEST_QUIC_SUCCEEDED(ClientConfiguration.GetInitStatus());
EcnTestContext Context;
MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, EcnTestContext::ConnCallback, &Context);
TEST_QUIC_SUCCEEDED(Listener.GetInitStatus());
TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest"));
QuicAddr ServerLocalAddr;
TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr));
MsQuicConnection Connection(Registration);
TEST_QUIC_SUCCEEDED(Connection.GetInitStatus());
TEST_QUIC_SUCCEEDED(Connection.Start(ClientConfiguration, QuicAddrFamily, QUIC_TEST_LOOPBACK_FOR_AF(QuicAddrFamily), ServerLocalAddr.GetPort()));
MsQuicStream Stream(Connection, QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL);
TEST_QUIC_SUCCEEDED(Stream.GetInitStatus());
//
// Open a stream, send some data and a FIN.
//
uint8_t RawBuffer[100];
QUIC_BUFFER Buffer { sizeof(RawBuffer), RawBuffer };
TEST_QUIC_SUCCEEDED(Stream.Send(&Buffer, 1, QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN));
TEST_TRUE(Context.ServerStreamRecv.WaitTimeout(TestWaitTimeout));
CxPlatSleep(50);
TEST_TRUE(Context.ServerStreamShutdown.WaitTimeout(TestWaitTimeout));
TEST_TRUE(Context.ServerStreamHasShutdown);
QUIC_STATISTICS_V2 Stats;
Connection.GetStatistics(&Stats);
TEST_TRUE(Stats.EcnCapable);
}
//
// Negative ECN test: network erasing ECT bit or incorrectly modifying ECT bit.
//
TestScopeLogger logScope1("network erasing ECT bit or incorrectly modifying ECT bit");
for (int EcnType = CXPLAT_ECN_NON_ECT; EcnType <= CXPLAT_ECN_ECT_1; ++EcnType) {
EcnModifyHelper EctEraser;
MsQuicRegistration Registration;
TEST_QUIC_SUCCEEDED(Registration.GetInitStatus());
MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetPeerUnidiStreamCount(1), ServerSelfSignedCredConfig);
TEST_QUIC_SUCCEEDED(ServerConfiguration.GetInitStatus());
MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetEcnEnabled(true), MsQuicCredentialConfig());
TEST_QUIC_SUCCEEDED(ClientConfiguration.GetInitStatus());
EcnTestContext Context;
MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, EcnTestContext::ConnCallback, &Context);
TEST_QUIC_SUCCEEDED(Listener.GetInitStatus());
TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest"));
QuicAddr ServerLocalAddr;
TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr));
EctEraser.SetEcnType((CXPLAT_ECN_TYPE)EcnType);
MsQuicConnection Connection(Registration);
TEST_QUIC_SUCCEEDED(Connection.GetInitStatus());
TEST_QUIC_SUCCEEDED(Connection.Start(ClientConfiguration, QuicAddrFamily, QUIC_TEST_LOOPBACK_FOR_AF(QuicAddrFamily), ServerLocalAddr.GetPort()));
MsQuicStream Stream(Connection, QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL);
TEST_QUIC_SUCCEEDED(Stream.GetInitStatus());
//
// Open a stream, send some data and a FIN.
//
uint8_t RawBuffer[100];
QUIC_BUFFER Buffer { sizeof(RawBuffer), RawBuffer };
TEST_QUIC_SUCCEEDED(Stream.Send(&Buffer, 1, QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN));
TEST_TRUE(Context.ServerStreamRecv.WaitTimeout(TestWaitTimeout));
CxPlatSleep(50);
TEST_TRUE(Context.ServerStreamShutdown.WaitTimeout(TestWaitTimeout));
QUIC_STATISTICS_V2 Stats;
Connection.GetStatistics(&Stats);
TEST_FALSE(Stats.EcnCapable);
}
//
// Negative ECN test: network erasing ECT bit or incorrectly modifying ECT bit after successful ECN validation.
//
TestScopeLogger logScope2("network erasing ECT bit or incorrectly modifying ECT bit successful ECN validation");
for (int EcnType = CXPLAT_ECN_NON_ECT; EcnType <= CXPLAT_ECN_ECT_1; ++EcnType) {
MsQuicRegistration Registration;
TEST_QUIC_SUCCEEDED(Registration.GetInitStatus());
MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetPeerUnidiStreamCount(1), ServerSelfSignedCredConfig);
TEST_QUIC_SUCCEEDED(ServerConfiguration.GetInitStatus());
MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", MsQuicSettings().SetEcnEnabled(true), MsQuicCredentialConfig());
TEST_QUIC_SUCCEEDED(ClientConfiguration.GetInitStatus());
EcnTestContext Context;
MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, EcnTestContext::ConnCallback, &Context);
TEST_QUIC_SUCCEEDED(Listener.GetInitStatus());
TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest"));
QuicAddr ServerLocalAddr;
TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr));
MsQuicConnection Connection(Registration);
TEST_QUIC_SUCCEEDED(Connection.GetInitStatus());
TEST_QUIC_SUCCEEDED(Connection.Start(ClientConfiguration, QuicAddrFamily, QUIC_TEST_LOOPBACK_FOR_AF(QuicAddrFamily), ServerLocalAddr.GetPort()));
MsQuicStream Stream(Connection, QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL);
TEST_QUIC_SUCCEEDED(Stream.GetInitStatus());
//
// Open a stream, send some data.
//
uint8_t RawBuffer[100];
QUIC_BUFFER Buffer { sizeof(RawBuffer), RawBuffer };
TEST_QUIC_SUCCEEDED(Stream.Send(&Buffer, 1, QUIC_SEND_FLAG_START));
TEST_TRUE(Context.ServerStreamRecv.WaitTimeout(TestWaitTimeout));
CxPlatSleep(50);
QUIC_STATISTICS_V2 Stats;
Connection.GetStatistics(&Stats);
TEST_TRUE(Stats.EcnCapable);
//
// Send some more data.
//
EcnModifyHelper EctEraser;
EctEraser.SetEcnType((CXPLAT_ECN_TYPE)EcnType);
QUIC_BUFFER AnotherBuffer { sizeof(RawBuffer), RawBuffer };
TEST_QUIC_SUCCEEDED(Stream.Send(&AnotherBuffer, 1, QUIC_SEND_FLAG_FIN));
TEST_TRUE(Context.ServerStreamRecv.WaitTimeout(TestWaitTimeout));
CxPlatSleep(50);
TEST_TRUE(Context.ServerStreamShutdown.WaitTimeout(TestWaitTimeout));
TEST_TRUE(Context.ServerStreamHasShutdown);
Connection.GetStatistics(&Stats);
TEST_FALSE(Stats.EcnCapable);
}
}
struct SlowRecvTestContext {
CxPlatEvent ServerStreamRecv;
CxPlatEvent ServerStreamShutdown;

Просмотреть файл

@ -418,6 +418,23 @@ TestConnection::SetSettings(
&value);
}
bool
TestConnection::GetEcnEnabled()
{
return GetSettings().EcnEnabled;
}
QUIC_STATUS
TestConnection::SetEcnEnabled(
bool value
)
{
QUIC_SETTINGS Settings{0};
Settings.EcnEnabled = value;
Settings.IsSet.EcnEnabled = TRUE;
return SetSettings(Settings);
}
uint64_t
TestConnection::GetIdleTimeout()
{

Просмотреть файл

@ -251,6 +251,9 @@ public:
QUIC_STATUS GetRemoteAddr(_Out_ QuicAddr &remoteAddr);
QUIC_STATUS SetRemoteAddr(_In_ const QuicAddr &remoteAddr);
bool GetEcnEnabled();
QUIC_STATUS SetEcnEnabled(bool value);
uint64_t GetIdleTimeout(); // milliseconds
QUIC_STATUS SetIdleTimeout(uint64_t value); // milliseconds

Просмотреть файл

@ -581,6 +581,26 @@ public:
}
};
struct EcnModifyHelper : public DatapathHook
{
CXPLAT_ECN_TYPE EcnType = CXPLAT_ECN_NON_ECT;
EcnModifyHelper() {
DatapathHooks::Instance->AddHook(this);
}
~EcnModifyHelper() {
DatapathHooks::Instance->RemoveHook(this);
}
void SetEcnType(CXPLAT_ECN_TYPE Type) { EcnType = Type; }
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN
Receive(
_Inout_ struct CXPLAT_RECV_DATA* Datagram
) {
Datagram->TypeOfService = (uint8_t)EcnType;
return false;
}
};
struct RandomLossHelper : public DatapathHook
{
uint8_t LossPercentage;

Просмотреть файл

@ -344,6 +344,8 @@ typedef enum QUIC_EVENT_ID_CONNECTION {
EventId_QuicConnClientReceivedVersionList,
EventId_QuicConnServerSupportedVersionList,
EventId_QuicConnBbr,
EventId_QuicConnEcnCapable,
EventId_QuicConnEcnFailed,
EventId_QuicConnCount
} QUIC_EVENT_ID_CONNECTION;