Skip to content

Commit

Permalink
Possibility for datagrams to be dropped when not able to send (#4320)
Browse files Browse the repository at this point in the history
  • Loading branch information
iiztp authored Jan 9, 2025
1 parent 39c6b52 commit 4f61d6f
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 3 deletions.
31 changes: 30 additions & 1 deletion docs/api/DatagramSend.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,36 @@ QUIC_STATUS
# Parameters
**TODO**
`Connection`
The current established connection.
`Buffers`
An array of `QUIC_BUFFER` structs that each contain a pointer and length to app data to send on the stream. This may be `NULL` **only** if `BufferCount` is zero.
`BufferCount`
The number of `QUIC_BUFFER` structs in the `Buffers` array. This may be zero.
`Flags`
The set of flags that controls the behavior of `DatagramSend`:
Value | Meaning
--- | ---
**QUIC_SEND_FLAG_NONE**<br>0 | No special behavior. Data is not allowed in 0-RTT by default.
**QUIC_SEND_FLAG_ALLOW_0_RTT**<br>1 | Indicates that the data is allowed to be sent in 0-RTT (if available). Makes no guarantee the data will be sent in 0-RTT. Additionally, even if 0-RTT keys are available the data may end up being sent in 1-RTT for multiple reasons.
**QUIC_SEND_FLAG_START**<br>2 | **Unused and ignored** for `DatagramSend`
**QUIC_SEND_FLAG_FIN**<br>4 | **Unused and ignored** for `DatagramSend`
**QUIC_SEND_FLAG_DGRAM_PRIORITY**<br>8 | Sets a priority to ensure a datagram is sent before others.
**QUIC_SEND_FLAG_DELAY_SEND**<br>16 | **Unused and ignored** for `DatagramSend`
**QUIC_SEND_FLAG_CANCEL_ON_LOSS**<br>32 | **Unused and ignored** for `DatagramSend`
**QUIC_SEND_FLAG_CANCEL_ON_BLOCKED**<br>64 | Allows MsQuic to drop frames when all the data that could be sent has been flushed out, but there are still some frames remaining in the queue.
`ClientSendContext`
The app context pointer (possibly null) to be associated with the send.
# Return Value
Expand Down
1 change: 1 addition & 0 deletions docs/api/StreamSend.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Value | Meaning
**QUIC_SEND_FLAG_DGRAM_PRIORITY**<br>8 | **Unused and ignored** for `StreamSend`
**QUIC_SEND_FLAG_DELAY_SEND**<br>16 | Provides a hint to MsQuic to indicate the data does not need to be sent immediately, likely because more is soon to follow.
**QUIC_SEND_FLAG_CANCEL_ON_LOSS**<br>32 | Informs MsQuic to irreversibly mark the associated stream to be canceled when packet loss has been detected on it. I.e., all sends on a given stream are subject to this behavior from the moment the flag has been supplied for the first time.
**QUIC_SEND_FLAG_CANCEL_ON_BLOCKED**<br>64 | **Unused and ignored** for `StreamSend` for now
`ClientSendContext`
Expand Down
37 changes: 37 additions & 0 deletions src/core/datagram.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,3 +588,40 @@ QuicDatagramProcessFrame(

return TRUE;
}

_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicDatagramCancelBlocked(
_In_ QUIC_CONNECTION* Connection
)
{
QUIC_DATAGRAM* Datagram = &Connection->Datagram;
QUIC_SEND_REQUEST** SendQueue = &Datagram->SendQueue;

if (*SendQueue == NULL) {
return;
}

do {
if ((*SendQueue)->Flags & QUIC_SEND_FLAG_CANCEL_ON_BLOCKED) {
QUIC_SEND_REQUEST* SendRequest = *SendQueue;
if (Datagram->PrioritySendQueueTail == &SendRequest->Next) {
Datagram->PrioritySendQueueTail = SendQueue;
}
*SendQueue = SendRequest->Next;
QuicDatagramCancelSend(Connection, SendRequest);
} else {
SendQueue = &((*SendQueue)->Next);
}
} while (*SendQueue != NULL);

Datagram->SendQueueTail = SendQueue;

if (Datagram->SendQueue != NULL) {
QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_DATAGRAM);
} else {
QuicSendClearSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_DATAGRAM);
}

QuicDatagramValidate(Datagram);
}
6 changes: 6 additions & 0 deletions src/core/datagram.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,9 @@ QuicDatagramProcessFrame(
const uint8_t * const Buffer,
_Inout_ uint16_t* Offset
);

_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicDatagramCancelBlocked(
_In_ QUIC_CONNECTION* Connection
);
5 changes: 5 additions & 0 deletions src/core/send.c
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,11 @@ QuicSendFlush(
//QuicConnUpdatePeerPacketTolerance(Connection, Builder.TotalCountDatagrams);
}

//
// Clears the SendQueue list of not sent packets if the flag is applied
//
QuicDatagramCancelBlocked(Connection);

return Result != QUIC_SEND_INCOMPLETE;
}
#pragma warning(pop)
Expand Down
1 change: 1 addition & 0 deletions src/cs/lib/msquic_generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ internal enum QUIC_SEND_FLAGS
DELAY_SEND = 0x0010,
CANCEL_ON_LOSS = 0x0020,
PRIORITY_WORK = 0x0040,
CANCEL_ON_BLOCKED = 0x0080,
}

internal enum QUIC_DATAGRAM_SEND_STATE
Expand Down
1 change: 1 addition & 0 deletions src/ffi/linux_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DGRAM_PRIORITY: QUIC_SEND_FLAGS = 8;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DELAY_SEND: QUIC_SEND_FLAGS = 16;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_LOSS: QUIC_SEND_FLAGS = 32;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_PRIORITY_WORK: QUIC_SEND_FLAGS = 64;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_BLOCKED: QUIC_SEND_FLAGS = 128;
pub type QUIC_SEND_FLAGS = ::std::os::raw::c_uint;
pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_UNKNOWN: QUIC_DATAGRAM_SEND_STATE = 0;
pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_SENT: QUIC_DATAGRAM_SEND_STATE = 1;
Expand Down
1 change: 1 addition & 0 deletions src/ffi/win_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DGRAM_PRIORITY: QUIC_SEND_FLAGS = 8;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_DELAY_SEND: QUIC_SEND_FLAGS = 16;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_LOSS: QUIC_SEND_FLAGS = 32;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_PRIORITY_WORK: QUIC_SEND_FLAGS = 64;
pub const QUIC_SEND_FLAGS_QUIC_SEND_FLAG_CANCEL_ON_BLOCKED: QUIC_SEND_FLAGS = 128;
pub type QUIC_SEND_FLAGS = ::std::os::raw::c_int;
pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_UNKNOWN: QUIC_DATAGRAM_SEND_STATE = 0;
pub const QUIC_DATAGRAM_SEND_STATE_QUIC_DATAGRAM_SEND_SENT: QUIC_DATAGRAM_SEND_STATE = 1;
Expand Down
1 change: 1 addition & 0 deletions src/inc/msquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ typedef enum QUIC_SEND_FLAGS {
QUIC_SEND_FLAG_DELAY_SEND = 0x0010, // Indicates the send should be delayed because more will be queued soon.
QUIC_SEND_FLAG_CANCEL_ON_LOSS = 0x0020, // Indicates that a stream is to be cancelled when packet loss is detected.
QUIC_SEND_FLAG_PRIORITY_WORK = 0x0040, // Higher priority than other connection work.
QUIC_SEND_FLAG_CANCEL_ON_BLOCKED = 0x0080, // Indicates that a frame should be dropped when it can't be sent immediately.
} QUIC_SEND_FLAGS;

DEFINE_ENUM_FLAG_OPERATORS(QUIC_SEND_FLAGS)
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/trace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ One of the built-in capabilities of WPA is the ability to analyze CPU trace info
### Linux
Linux perf command is one of the way to collect such information.
```sh
# on Linux
# on Linux (kernel > 5.10)
sudo apt-get install -y linux-perf
# on Linux (kernel <= 5.10)
sudo apt-get install -y linux-tools-`uname -r`
# use your own options
perf record -a -g -F 10 -o out.perf.data
Expand Down
11 changes: 10 additions & 1 deletion src/test/MsQuicTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,11 @@ QuicTestDatagramSend(
_In_ int Family
);

void
QuicTestDatagramDrop(
_In_ int Family
);

//
// Storage tests
//
Expand Down Expand Up @@ -1331,4 +1336,8 @@ typedef struct {
QUIC_CTL_CODE(125, METHOD_BUFFERED, FILE_WRITE_DATA)
// BOOLEAN - EnableResumption

#define QUIC_MAX_IOCTL_FUNC_CODE 125
#define IOCTL_QUIC_RUN_DATAGRAM_DROP \
QUIC_CTL_CODE(126, METHOD_BUFFERED, FILE_WRITE_DATA)
// int - Family

#define QUIC_MAX_IOCTL_FUNC_CODE 126
9 changes: 9 additions & 0 deletions src/test/bin/quic_gtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2352,6 +2352,15 @@ TEST_P(WithFamilyArgs, DatagramSend) {
}
}

TEST_P(WithFamilyArgs, DatagramDrop) {
TestLoggerT<ParamType> Logger("QuicTestDatagramDrop", GetParam());
if (TestingKernelMode) {
ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_DATAGRAM_DROP, GetParam().Family));
} else {
QuicTestDatagramDrop(GetParam().Family);
}
}

#ifdef _WIN32 // Storage tests only supported on Windows

static BOOLEAN CanRunStorageTests = FALSE;
Expand Down
8 changes: 8 additions & 0 deletions src/test/bin/winkernel/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] =
0,
0,
sizeof(BOOLEAN),
sizeof(INT32),
};

CXPLAT_STATIC_ASSERT(
Expand Down Expand Up @@ -955,6 +956,13 @@ QuicTestCtlEvtIoDeviceControl(
Params->Family));
break;

case IOCTL_QUIC_RUN_DATAGRAM_DROP:
CXPLAT_FRE_ASSERT(Params != nullptr);
QuicTestCtlRun(
QuicTestDatagramDrop(
Params->Family));
break;

case IOCTL_QUIC_RUN_NAT_PORT_REBIND:
CXPLAT_FRE_ASSERT(Params != nullptr);
QuicTestCtlRun(
Expand Down
99 changes: 99 additions & 0 deletions src/test/lib/DatagramTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,102 @@ QuicTestDatagramSend(
}
}
}

void
QuicTestDatagramDrop(
_In_ int Family
)
{
MsQuicRegistration Registration;
TEST_TRUE(Registration.IsValid());

MsQuicAlpn Alpn("MsQuicTest");

MsQuicSettings Settings;
Settings.SetDatagramReceiveEnabled(true);

MsQuicCredentialConfig ClientCredConfig;
MsQuicConfiguration ClientConfiguration(Registration, Alpn, Settings, ClientCredConfig);
TEST_TRUE(ClientConfiguration.IsValid());

MsQuicConfiguration ServerConfiguration(Registration, Alpn, Settings, ServerSelfSignedCredConfig);
TEST_TRUE(ServerConfiguration.IsValid());

uint8_t RawBuffer[1100] = {0};
QUIC_BUFFER DatagramBuffer = { sizeof(RawBuffer), RawBuffer };

SelectiveLossHelper LossHelper;

{
TestListener Listener(Registration, ListenerAcceptConnection, ServerConfiguration);
TEST_TRUE(Listener.IsValid());

QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6;
QuicAddr ServerLocalAddr(QuicAddrFamily);
TEST_QUIC_SUCCEEDED(Listener.Start(Alpn, &ServerLocalAddr.SockAddr));
TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr));

{
UniquePtr<TestConnection> Server;
ServerAcceptContext ServerAcceptCtx((TestConnection**)&Server);
Listener.Context = &ServerAcceptCtx;

{
TestConnection Client(Registration);
TEST_TRUE(Client.IsValid());

TEST_TRUE(Client.GetDatagramSendEnabled());

for (int i = 0; i < 20; i++) {
TEST_QUIC_SUCCEEDED(
MsQuic->DatagramSend(
Client.GetConnection(),
&DatagramBuffer,
1,
(i%2 == 0) ? QUIC_SEND_FLAG_CANCEL_ON_BLOCKED : QUIC_SEND_FLAG_NONE,
nullptr));
}

TEST_QUIC_SUCCEEDED(
Client.Start(
ClientConfiguration,
QuicAddrFamily,
QUIC_TEST_LOOPBACK_FOR_AF(QuicAddrFamily),
ServerLocalAddr.GetPort()));

if (!Client.WaitForConnectionComplete()) {
return;
}
TEST_TRUE(Client.GetIsConnected());

TEST_TRUE(Client.GetDatagramSendEnabled());

TEST_NOT_EQUAL(nullptr, Server);
if (!Server->WaitForConnectionComplete()) {
return;
}
TEST_TRUE(Server->GetIsConnected());

TEST_TRUE(Server->GetDatagramSendEnabled());

CxPlatSleep(100);

uint32_t Tries = 0;
while (Client.GetDatagramsSent() != 10 && Client.GetDatagramsCanceled() != 10 && ++Tries < 10) {
CxPlatSleep(100);
}

TEST_EQUAL(10, Client.GetDatagramsCanceled());
TEST_EQUAL(10, Client.GetDatagramsSent());

Client.Shutdown(QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, QUIC_TEST_NO_ERROR);
if (!Client.WaitForShutdownComplete()) {
return;
}

TEST_FALSE(Client.GetPeerClosed());
TEST_FALSE(Client.GetTransportClosed());
}
}
}
}

0 comments on commit 4f61d6f

Please sign in to comment.