diff --git a/scripts/clog.inputs b/scripts/clog.inputs
index 266764a940..9514432edf 100644
--- a/scripts/clog.inputs
+++ b/scripts/clog.inputs
@@ -73,6 +73,7 @@
../src/core/configuration.c
../src/core/partition.c
../src/core/library.c
+../src/core/qmux.c
../src/bin/winuser_fuzz/dllmain.c
../src/bin/linux/init.c
../src/bin/winuser/dllmain.c
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 12b0d275e3..4eebf6e6c5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -34,6 +34,7 @@ set(SOURCES
packet_builder.c
packet_space.c
path.c
+ qmux.c
range.c
recv_buffer.c
registration.c
diff --git a/src/core/api.c b/src/core/api.c
index 15bf9087d6..7801dd6753 100644
--- a/src/core/api.c
+++ b/src/core/api.c
@@ -146,6 +146,122 @@ MsQuicConnectionOpenInPartition(
NewConnection);
}
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+QuicConnectionQmuxOpenInPartition(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ uint16_t PartitionIndex,
+ _In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _In_ BOOLEAN Partitioned,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewConnection
+ )
+{
+ QUIC_STATUS Status;
+ QUIC_REGISTRATION* Registration;
+ QUIC_CONNECTION* Connection = NULL;
+
+ QuicTraceEvent(
+ ApiEnter,
+ "[ api] Enter %u (%p).",
+ QUIC_TRACE_API_CONNECTION_OPEN,
+ RegistrationHandle);
+
+ if (!IS_REGISTRATION_HANDLE(RegistrationHandle) ||
+ PartitionIndex >= MsQuicLib.PartitionCount ||
+ NewConnection == NULL ||
+ Handler == NULL) {
+ Status = QUIC_STATUS_INVALID_PARAMETER;
+ goto Error;
+ }
+
+#pragma prefast(suppress: __WARNING_25024, "Pointer cast already validated.")
+ Registration = (QUIC_REGISTRATION*)RegistrationHandle;
+
+ //
+ // Just use the current partition for now. Once the connection receives a
+ // packet the partition can be updated accordingly.
+ //
+ Status =
+ QuicConnQMuxAlloc(
+ Registration,
+ &MsQuicLib.Partitions[PartitionIndex],
+ NULL,
+ FALSE,
+ &Connection);
+ if (QUIC_FAILED(Status)) {
+ goto Error;
+ }
+
+ //
+ // Hard partitioning is only supported on a subset of platforms.
+ //
+#if defined(__linux__) && !defined(CXPLAT_USE_IO_URING)
+ Connection->State.Partitioned = Partitioned;
+#else
+ UNREFERENCED_PARAMETER(Partitioned);
+#endif
+ Connection->ClientCallbackHandler = Handler;
+ Connection->ClientContext = Context;
+
+ *NewConnection = (HQUIC)Connection;
+ Status = QUIC_STATUS_SUCCESS;
+
+Error:
+
+ QuicTraceEvent(
+ ApiExitStatus,
+ "[ api] Exit %u",
+ Status);
+
+ return Status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicConnectionQmuxOpen(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewConnection
+ )
+{
+ return
+ QuicConnectionQmuxOpenInPartition(
+ RegistrationHandle,
+ QuicLibraryGetCurrentPartition()->Index,
+ Handler,
+ Context,
+ FALSE,
+ NewConnection);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicConnectionQmuxOpenInPartition(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ uint16_t PartitionIndex,
+ _In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewConnection
+ )
+{
+ return
+ QuicConnectionQmuxOpenInPartition(
+ RegistrationHandle,
+ PartitionIndex,
+ Handler,
+ Context,
+ TRUE,
+ NewConnection);
+}
+
#pragma warning(push)
#pragma warning(disable:6014) // SAL doesn't understand the free happens on the worker
_IRQL_requires_max_(PASSIVE_LEVEL)
@@ -620,7 +736,8 @@ MsQuicConnectionSendResumptionTicket(
if (!Connection->State.ResumptionEnabled ||
!Connection->State.Connected ||
- !Connection->Crypto.TlsState.HandshakeComplete) {
+ (!QuicConnIsQMux(Connection) && !Connection->Crypto.TlsState.HandshakeComplete) ||
+ (QuicConnIsQMux(Connection) && !QuicConnGetQMux(Connection)->TlsState.HandshakeComplete)) {
Status = QUIC_STATUS_INVALID_STATE; // TODO - Support queueing up the ticket to send once connected.
goto Error;
}
diff --git a/src/core/api.h b/src/core/api.h
index a02b4614bb..81cc0cbdb0 100644
--- a/src/core/api.h
+++ b/src/core/api.h
@@ -117,6 +117,17 @@ MsQuicListenerOpen(
HQUIC *Listener
);
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicListenerQmuxOpen(
+ _In_ _Pre_defensive_ HQUIC Registration,
+ _In_ _Pre_defensive_ QUIC_LISTENER_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*Listener, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *Listener
+ );
+
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QUIC_API
@@ -166,6 +177,29 @@ MsQuicConnectionOpenInPartition(
HQUIC *Connection
);
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicConnectionQmuxOpen(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewConnection
+ );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicConnectionQmuxOpenInPartition(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ uint16_t PartitionIndex,
+ _In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewConnection
+ );
+
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QUIC_API
diff --git a/src/core/connection.c b/src/core/connection.c
index d5379d9896..dffed6cc81 100644
--- a/src/core/connection.c
+++ b/src/core/connection.c
@@ -107,6 +107,7 @@ QuicConnAlloc(
#endif
Connection->PartitionID = PartitionId;
Connection->State.Allocated = TRUE;
+ Connection->State.IsQMux = FALSE;
Connection->State.ShareBinding = IsServer;
Connection->State.FixedBit = TRUE;
Connection->Stats.Timing.Start = CxPlatTimeUs64();
@@ -227,7 +228,6 @@ QuicConnAlloc(
Connection,
SourceCid->CID.SequenceNumber,
CASTED_CLOG_BYTEARRAY(SourceCid->CID.Length, SourceCid->CID.Data));
-
//
// Server lazily finishes initialization in response to first operation.
//
@@ -304,6 +304,117 @@ QuicConnAlloc(
return Status;
}
+_IRQL_requires_max_(DISPATCH_LEVEL)
+_Must_inspect_result_
+_Success_(return == QUIC_STATUS_SUCCESS)
+QUIC_STATUS
+QuicConnQMuxAlloc(
+ _In_ QUIC_REGISTRATION* Registration,
+ _In_ QUIC_PARTITION* Partition,
+ _In_opt_ QUIC_WORKER* Worker,
+ _In_ BOOLEAN IsServer,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem))
+ QUIC_CONNECTION** NewConnection
+ )
+{
+ *NewConnection = NULL;
+ QUIC_STATUS Status;
+
+ const uint16_t PartitionId = QuicPartitionIdCreate(Partition->Index);
+ CXPLAT_DBG_ASSERT(Partition->Index == QuicPartitionIdGetIndex(PartitionId));
+
+ QUIC_CONNECTION* Connection = CxPlatPoolAlloc(&Partition->ConnectionPool);
+ if (Connection == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "connection",
+ sizeof(QUIC_CONNECTION));
+ return QUIC_STATUS_OUT_OF_MEMORY;
+ }
+
+ CxPlatZeroMemory(Connection, sizeof(QUIC_CONNECTION));
+ Connection->Partition = Partition;
+
+ Status = QuicQMuxInitialize(Connection, &Connection->QMux);
+ if (QUIC_FAILED(Status)) {
+ CxPlatPoolFree(Connection);
+ return Status;
+ }
+
+#if DEBUG
+ InterlockedIncrement(&MsQuicLib.ConnectionCount);
+ QuicLibraryTrackDbgObject(QUIC_DBG_OBJECT_TYPE_CONNECTION, &Connection->DbgObjectLink);
+#endif
+ QuicPerfCounterIncrement(Connection->Partition, QUIC_PERF_COUNTER_CONN_CREATED);
+ QuicPerfCounterIncrement(Connection->Partition, QUIC_PERF_COUNTER_CONN_ACTIVE);
+
+ Connection->Stats.CorrelationId =
+ InterlockedIncrement64((int64_t*)&MsQuicLib.ConnectionCorrelationId) - 1;
+ QuicTraceEvent(
+ ConnCreated,
+ "[conn][%p] Created, IsServer=%hhu, CorrelationId=%llu",
+ Connection,
+ IsServer,
+ Connection->Stats.CorrelationId);
+
+ Connection->RefCount = 1;
+#if DEBUG
+ CxPlatRefInitializeMultiple(Connection->RefTypeBiasedCount, QUIC_CONN_REF_COUNT);
+ CxPlatRefIncrement(&Connection->RefTypeBiasedCount[QUIC_CONN_REF_HANDLE_OWNER]);
+#endif
+ Connection->PartitionID = PartitionId;
+ Connection->State.Allocated = TRUE;
+ Connection->State.IsQMux = TRUE;
+ Connection->State.FixedBit = TRUE;
+ Connection->Stats.Timing.Start = CxPlatTimeUs64();
+ QuicSettingsCopy(&Connection->Settings, &MsQuicLib.Settings);
+ Connection->Settings.IsSetFlags = 0; // Just grab the global values, not IsSet flags.
+ QuicStreamSetInitialize(&Connection->Streams);
+ QuicSendBufferInitialize(&Connection->SendBuffer);
+ QuicOperationQueueInitialize(&Connection->OperQ);
+ QuicSendInitialize(&Connection->Send, &Connection->Settings);
+ QuicDatagramInitialize(&Connection->Datagram);
+
+ Connection->EarliestExpirationTime = UINT64_MAX;
+ for (QUIC_CONN_TIMER_TYPE Type = 0; Type < QUIC_CONN_TIMER_COUNT; ++Type) {
+ Connection->ExpirationTimes[Type] = UINT64_MAX;
+ }
+
+ if (IsServer) {
+ Connection->Type = QUIC_HANDLE_TYPE_CONNECTION_SERVER;
+ //
+ // Server lazily finishes initialization in response to first operation.
+ //
+ } else {
+ Connection->Type = QUIC_HANDLE_TYPE_CONNECTION_CLIENT;
+ Connection->State.ExternalOwner = TRUE;
+ Connection->State.Initialized = TRUE;
+ QuicTraceEvent(
+ ConnInitializeComplete,
+ "[conn][%p] Initialize complete",
+ Connection);
+ }
+
+ if (Worker != NULL) {
+ QuicWorkerAssignConnection(Worker, Connection);
+ }
+ if (!QuicConnRegister(Connection, Registration)) {
+ Status = QUIC_STATUS_INVALID_STATE;
+ goto Error;
+ }
+
+ *NewConnection = Connection;
+ return QUIC_STATUS_SUCCESS;
+
+Error:
+
+ Connection->State.HandleClosed = TRUE;
+ QuicConnRelease(Connection, QUIC_CONN_REF_HANDLE_OWNER);
+
+ return Status;
+}
+
_IRQL_requires_max_(DISPATCH_LEVEL)
void
QuicConnFree(
@@ -433,6 +544,96 @@ QuicConnFree(
#endif
}
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicConnQMuxFree(
+ _In_ __drv_freesMem(Mem) QUIC_CONNECTION* Connection
+ )
+{
+ QUIC_PARTITION* Partition = Connection->Partition;
+#ifdef QUIC_SILO
+ QUIC_SILO Silo = NULL;
+ QuicConfigurationAttachSilo(Connection->Configuration);
+#endif
+
+ CXPLAT_FRE_ASSERT(!Connection->State.Freed);
+ CXPLAT_TEL_ASSERT(Connection->RefCount == 0);
+ if (Connection->State.ExternalOwner) {
+ CXPLAT_TEL_ASSERT(Connection->State.HandleClosed);
+ }
+ CXPLAT_TEL_ASSERT(CxPlatListIsEmpty(&Connection->Streams.ClosedStreams));
+ QuicSendUninitialize(&Connection->Send);
+#if DEBUG
+ while (!CxPlatListIsEmpty(&Connection->Streams.AllStreams)) {
+ QUIC_STREAM *Stream =
+ CXPLAT_CONTAINING_RECORD(
+ CxPlatListRemoveHead(&Connection->Streams.AllStreams),
+ QUIC_STREAM,
+ AllStreamsLink);
+ CXPLAT_DBG_ASSERTMSG(Stream != NULL, "Stream was leaked!");
+ }
+#endif
+ QuicConnUnregister(Connection);
+ if (Connection->Worker != NULL) {
+ QuicTimerWheelRemoveConnection(&Connection->Worker->TimerWheel, Connection);
+ QuicOperationQueueClear(&Connection->OperQ, Partition);
+ }
+ QuicOperationQueueUninitialize(&Connection->OperQ);
+ QuicStreamSetUninitialize(&Connection->Streams);
+ QuicSendBufferUninitialize(&Connection->SendBuffer);
+ QuicDatagramSendShutdown(&Connection->Datagram);
+ QuicDatagramUninitialize(&Connection->Datagram);
+ if (Connection->Configuration != NULL) {
+#ifdef QUIC_SILO
+ //
+ // Take a ref on the silo before releasing the configuration
+ // to prevent the silo from being destroyed while we are still
+ // holding onto the thread to clean up other stuff for this connection.
+ //
+ Silo = Connection->Configuration->Silo;
+ QuicSiloAddRef(Silo);
+#endif
+ QuicConfigurationRelease(Connection->Configuration, QUIC_CONF_REF_CONNECTION);
+ Connection->Configuration = NULL;
+ }
+ if (Connection->RemoteServerName != NULL) {
+ CXPLAT_FREE(Connection->RemoteServerName, QUIC_POOL_SERVERNAME);
+ }
+ QuicSettingsCleanup(&Connection->Settings);
+ if (Connection->State.Started && !Connection->State.Connected) {
+ QuicPerfCounterIncrement(Partition, QUIC_PERF_COUNTER_CONN_HANDSHAKE_FAIL);
+ }
+ if (Connection->State.Connected) {
+ QuicPerfCounterDecrement(Partition, QUIC_PERF_COUNTER_CONN_CONNECTED);
+ }
+ if (Connection->Registration != NULL) {
+ QuicRegistrationRundownRelease(Connection->Registration, QUIC_REG_REF_CONNECTION);
+ }
+ if (Connection->CloseReasonPhrase != NULL) {
+ CXPLAT_FREE(Connection->CloseReasonPhrase, QUIC_POOL_CLOSE_REASON);
+ }
+ Connection->State.Freed = TRUE;
+#if DEBUG
+ QuicLibraryUntrackDbgObject(QUIC_DBG_OBJECT_TYPE_CONNECTION, &Connection->DbgObjectLink);
+#endif
+ QuicQMuxUninitialize(Connection->QMux);
+ Connection->QMux = NULL;
+ QuicTraceEvent(
+ ConnDestroyed,
+ "[conn][%p] Destroyed",
+ Connection);
+ CxPlatPoolFree(Connection);
+
+#if DEBUG
+ InterlockedDecrement(&MsQuicLib.ConnectionCount);
+#endif
+ QuicPerfCounterDecrement(Partition, QUIC_PERF_COUNTER_CONN_ACTIVE);
+#ifdef QUIC_SILO
+ QuicConfigurationDetachSilo();
+ QuicSiloRelease(Silo);
+#endif
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicConnShutdown(
@@ -730,7 +931,7 @@ QuicConnQueueOper(
#if DEBUG
if (!Connection->State.Initialized) {
CXPLAT_DBG_ASSERT(QuicConnIsServer(Connection));
- CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL || CxPlatIsRandomMemoryFailureEnabled());
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Connection) || Connection->SourceCids.Next != NULL || CxPlatIsRandomMemoryFailureEnabled());
}
if (Oper->Type == QUIC_OPER_TYPE_API_CALL) {
if (Oper->API_CALL.Context->Type == QUIC_API_TYPE_CONN_SHUTDOWN) {
@@ -1412,6 +1613,11 @@ QuicConnOnShutdownComplete(
QuicLossDetectionUninitialize(&Connection->LossDetection);
QuicSendUninitialize(&Connection->Send);
QuicDatagramSendShutdown(&Connection->Datagram);
+ if (QuicConnIsQMux(Connection)) {
+ if (QuicConnGetQMux(Connection)->Socket != NULL) {
+ CxPlatSocketDelete(QuicConnGetQMux(Connection)->Socket);
+ }
+ }
if (Connection->State.ExternalOwner) {
@@ -1819,10 +2025,14 @@ QuicConnStart(
FALSE,
&Configuration->Settings);
+ QUIC_ADDR* RemoteAddress =
+ QuicConnIsQMux(Connection) ?
+ &QuicConnGetQMux(Connection)->Route.RemoteAddress :
+ &Path->Route.RemoteAddress;
if (!Connection->State.RemoteAddressSet) {
CXPLAT_DBG_ASSERT(ServerName != NULL);
- QuicAddrSetFamily(&Path->Route.RemoteAddress, Family);
+ QuicAddrSetFamily(RemoteAddress, Family);
#ifdef QUIC_COMPARTMENT_ID
BOOLEAN RevertCompartmentId = FALSE;
@@ -1848,7 +2058,7 @@ QuicConnStart(
CxPlatDataPathResolveAddress(
MsQuicLib.Datapath,
ServerName,
- &Path->Route.RemoteAddress);
+ RemoteAddress);
#ifdef QUIC_COMPARTMENT_ID
if (RevertCompartmentId) {
@@ -1863,7 +2073,7 @@ QuicConnStart(
Connection->State.RemoteAddressSet = TRUE;
}
- if (QuicAddrIsWildCard(&Path->Route.RemoteAddress)) {
+ if (QuicAddrIsWildCard(RemoteAddress)) {
Status = QUIC_STATUS_INVALID_PARAMETER;
QuicTraceEvent(
ConnError,
@@ -1873,94 +2083,120 @@ QuicConnStart(
goto Exit;
}
- QuicAddrSetPort(&Path->Route.RemoteAddress, ServerPort);
+ QuicAddrSetPort(RemoteAddress, ServerPort);
QuicTraceEvent(
ConnRemoteAddrAdded,
"[conn][%p] New Remote IP: %!ADDR!",
Connection,
- CASTED_CLOG_BYTEARRAY(sizeof(Path->Route.RemoteAddress), &Path->Route.RemoteAddress));
-
- CXPLAT_UDP_CONFIG UdpConfig = {0};
- UdpConfig.LocalAddress = Connection->State.LocalAddressSet ? &Path->Route.LocalAddress : NULL;
- UdpConfig.RemoteAddress = &Path->Route.RemoteAddress;
- UdpConfig.Flags = CXPLAT_SOCKET_FLAG_NONE;
- UdpConfig.InterfaceIndex = Connection->State.LocalInterfaceSet ? (uint32_t)Path->Route.LocalAddress.Ipv6.sin6_scope_id : 0; // NOLINT(google-readability-casting)
- UdpConfig.PartitionIndex = QuicPartitionIdGetIndex(Connection->PartitionID);
+ CASTED_CLOG_BYTEARRAY(sizeof(*RemoteAddress), RemoteAddress));
+
+ if (!QuicConnIsQMux(Connection)) {
+ CXPLAT_UDP_CONFIG UdpConfig = {0};
+ UdpConfig.LocalAddress = Connection->State.LocalAddressSet ? &Path->Route.LocalAddress : NULL;
+ UdpConfig.RemoteAddress = &Path->Route.RemoteAddress;
+ UdpConfig.Flags = CXPLAT_SOCKET_FLAG_NONE;
+ UdpConfig.InterfaceIndex = Connection->State.LocalInterfaceSet ? (uint32_t)Path->Route.LocalAddress.Ipv6.sin6_scope_id : 0; // NOLINT(google-readability-casting)
+ UdpConfig.PartitionIndex = QuicPartitionIdGetIndex(Connection->PartitionID);
#ifdef QUIC_COMPARTMENT_ID
- UdpConfig.CompartmentId = Configuration->CompartmentId;
+ UdpConfig.CompartmentId = Configuration->CompartmentId;
#endif
#ifdef QUIC_OWNING_PROCESS
- UdpConfig.OwningProcess = Configuration->OwningProcess;
+ UdpConfig.OwningProcess = Configuration->OwningProcess;
#endif
- if (Connection->State.ShareBinding) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_SHARE;
- }
- if (Connection->Settings.XdpEnabled) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_XDP;
- }
- if (Connection->Settings.QTIPEnabled) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_QTIP;
- }
- if (Connection->State.Partitioned) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_PARTITIONED;
- }
+ if (Connection->State.ShareBinding) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_SHARE;
+ }
+ if (Connection->Settings.XdpEnabled) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_XDP;
+ }
+ if (Connection->Settings.QTIPEnabled) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_QTIP;
+ }
+ if (Connection->State.Partitioned) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_PARTITIONED;
+ }
- //
- // Get the binding for the current local & remote addresses.
- //
- Status =
- QuicLibraryGetBinding(
- &UdpConfig,
- &Path->Binding);
- if (QUIC_FAILED(Status)) {
- goto Exit;
- }
+ //
+ // Get the binding for the current local & remote addresses.
+ //
+ Status =
+ QuicLibraryGetBinding(
+ &UdpConfig,
+ &Path->Binding);
+ if (QUIC_FAILED(Status)) {
+ goto Exit;
+ }
- //
- // Clients only need to generate a non-zero length source CID if it
- // intends to share the UDP binding.
- //
- QUIC_CID_HASH_ENTRY* SourceCid;
- if (Connection->State.ShareBinding) {
- SourceCid =
- QuicCidNewRandomSource(
- Connection,
- NULL,
- Connection->PartitionID,
- Connection->CibirId[0],
- Connection->CibirId+2);
- } else {
- SourceCid = QuicCidNewNullSource(Connection);
- }
- if (SourceCid == NULL) {
- Status = QUIC_STATUS_OUT_OF_MEMORY;
- goto Exit;
- }
+ //
+ // Clients only need to generate a non-zero length source CID if it
+ // intends to share the UDP binding.
+ //
+ QUIC_CID_HASH_ENTRY* SourceCid;
+ if (Connection->State.ShareBinding) {
+ SourceCid =
+ QuicCidNewRandomSource(
+ Connection,
+ NULL,
+ Connection->PartitionID,
+ Connection->CibirId[0],
+ Connection->CibirId+2);
+ } else {
+ SourceCid = QuicCidNewNullSource(Connection);
+ }
+ if (SourceCid == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
- Connection->NextSourceCidSequenceNumber++;
- QuicTraceEvent(
- ConnSourceCidAdded,
- "[conn][%p] (SeqNum=%llu) New Source CID: %!CID!",
- Connection,
- SourceCid->CID.SequenceNumber,
- CASTED_CLOG_BYTEARRAY(SourceCid->CID.Length, SourceCid->CID.Data));
- CxPlatListPushEntry(&Connection->SourceCids, &SourceCid->Link);
+ Connection->NextSourceCidSequenceNumber++;
+ QuicTraceEvent(
+ ConnSourceCidAdded,
+ "[conn][%p] (SeqNum=%llu) New Source CID: %!CID!",
+ Connection,
+ SourceCid->CID.SequenceNumber,
+ CASTED_CLOG_BYTEARRAY(SourceCid->CID.Length, SourceCid->CID.Data));
+ CxPlatListPushEntry(&Connection->SourceCids, &SourceCid->Link);
- if (!QuicBindingAddSourceConnectionID(Path->Binding, SourceCid)) {
- QuicLibraryReleaseBinding(Path->Binding);
- Path->Binding = NULL;
- Status = QUIC_STATUS_OUT_OF_MEMORY;
- goto Exit;
- }
+ if (!QuicBindingAddSourceConnectionID(Path->Binding, SourceCid)) {
+ QuicLibraryReleaseBinding(Path->Binding);
+ Path->Binding = NULL;
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
- Connection->State.LocalAddressSet = TRUE;
- QuicBindingGetLocalAddress(Path->Binding, &Path->Route.LocalAddress);
- QuicTraceEvent(
- ConnLocalAddrAdded,
- "[conn][%p] New Local IP: %!ADDR!",
- Connection,
- CASTED_CLOG_BYTEARRAY(sizeof(Path->Route.LocalAddress), &Path->Route.LocalAddress));
+ Connection->State.LocalAddressSet = TRUE;
+ QuicBindingGetLocalAddress(Path->Binding, &Path->Route.LocalAddress);
+ QuicTraceEvent(
+ ConnLocalAddrAdded,
+ "[conn][%p] New Local IP: %!ADDR!",
+ Connection,
+ CASTED_CLOG_BYTEARRAY(sizeof(Path->Route.LocalAddress), &Path->Route.LocalAddress));
+
+ } else {
+ QUIC_QMUX* QMux = QuicConnGetQMux(Connection);
+ Status = CxPlatSocketCreateTcp(MsQuicLib.Datapath,
+ Connection->State.LocalAddressSet ? &QMux->Route.LocalAddress : NULL,
+ &QMux->Route.RemoteAddress,
+ QMux,
+ &QMux->Socket
+ );
+ if (QUIC_FAILED(Status)) {
+ goto Exit;
+ }
+ QuicConnResetIdleTimeout(Connection);
+ BOOLEAN ConnectCompleted = CxPlatEventWaitWithTimeout(QMux->ConnectEvent, (uint32_t)Connection->Settings.HandshakeIdleTimeoutMs);
+ if (!ConnectCompleted) {
+ Status = QUIC_STATUS_CONNECTION_TIMEOUT;
+ goto Exit;
+ }
+ if (!Connection->State.TcpConnected) {
+ Status = QUIC_STATUS_INTERNAL_ERROR;
+ goto Exit;
+ }
+ Connection->State.LocalAddressSet = TRUE;
+ CxPlatSocketGetLocalAddress(QMux->Socket, &QMux->Route.LocalAddress);
+ }
//
// Save the server name.
@@ -1968,9 +2204,11 @@ QuicConnStart(
Connection->RemoteServerName = ServerName;
ServerName = NULL;
- Status = QuicCryptoInitialize(&Connection->Crypto);
- if (QUIC_FAILED(Status)) {
- goto Exit;
+ if (!QuicConnIsQMux(Connection)) {
+ Status = QuicCryptoInitialize(&Connection->Crypto);
+ if (QUIC_FAILED(Status)) {
+ goto Exit;
+ }
}
//
@@ -2072,7 +2310,9 @@ QuicConnSendResumptionTicket(
QUIC_STATUS Status;
uint8_t* TicketBuffer = NULL;
uint32_t TicketLength = 0;
- uint8_t AlpnLength = Connection->Crypto.TlsState.NegotiatedAlpn[0];
+ uint8_t AlpnLength = !QuicConnIsQMux(Connection) ?
+ Connection->Crypto.TlsState.NegotiatedAlpn[0] :
+ QuicConnGetQMux(Connection)->TlsState.NegotiatedAlpn[0];
if (Connection->HandshakeTP == NULL) {
Status = QUIC_STATUS_OUT_OF_MEMORY;
@@ -2088,14 +2328,30 @@ QuicConnSendResumptionTicket(
Connection->HandshakeTP,
NULL, // No Careful Resumption data
AlpnLength,
- Connection->Crypto.TlsState.NegotiatedAlpn + 1,
+ !QuicConnIsQMux(Connection) ?
+ Connection->Crypto.TlsState.NegotiatedAlpn + 1 :
+ QuicConnGetQMux(Connection)->TlsState.NegotiatedAlpn + 1,
&TicketBuffer,
&TicketLength);
if (QUIC_FAILED(Status)) {
goto Error;
}
- Status = QuicCryptoProcessAppData(&Connection->Crypto, TicketLength, TicketBuffer);
+ if (!QuicConnIsQMux(Connection)) {
+ Status = QuicCryptoProcessAppData(&Connection->Crypto, TicketLength, TicketBuffer);
+ } else {
+ uint32_t TicketLengthConsumed = TicketLength;
+ uint32_t TicketOffset = 0;
+ do {
+ Status =
+ QuicQMuxProcessHandshake(
+ QuicConnGetQMux(Connection),
+ CXPLAT_TLS_TICKET_DATA,
+ TicketBuffer + TicketOffset,
+ &TicketLengthConsumed);
+ TicketOffset += TicketLengthConsumed;
+ } while (TicketOffset < TicketLength && QUIC_SUCCEEDED(Status));
+ }
Error:
if (TicketBuffer != NULL) {
@@ -2122,17 +2378,32 @@ QuicConnRecvResumptionTicket(
QUIC_TRANSPORT_PARAMETERS ResumedTP = {0};
CxPlatZeroMemory(&ResumedTP, sizeof(ResumedTP));
if (QuicConnIsServer(Connection)) {
- if (Connection->Crypto.TicketValidationRejecting) {
- QuicTraceEvent(
- ConnError,
- "[conn][%p] ERROR, %s.",
- Connection,
- "Resumption Ticket rejected by server app asynchronously");
- Connection->Crypto.TicketValidationRejecting = FALSE;
- Connection->Crypto.TicketValidationPending = FALSE;
- goto Error;
+ if (!QuicConnIsQMux(Connection)) {
+ if (Connection->Crypto.TicketValidationRejecting) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Resumption Ticket rejected by server app asynchronously");
+ Connection->Crypto.TicketValidationRejecting = FALSE;
+ Connection->Crypto.TicketValidationPending = FALSE;
+ goto Error;
+ }
+ Connection->Crypto.TicketValidationPending = TRUE;
+ } else {
+ if (QuicConnGetQMux(Connection)->TicketValidationRejecting) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Resumption Ticket rejected by server app asynchronously");
+ QuicConnGetQMux(Connection)->TicketValidationRejecting = FALSE;
+ QuicConnGetQMux(Connection)->TicketValidationPending = FALSE;
+ goto Error;
+ }
+ QuicConnGetQMux(Connection)->TicketValidationPending = TRUE;
+
}
- Connection->Crypto.TicketValidationPending = TRUE;
if (TicketLength > UINT16_MAX) {
QuicTraceEvent(
@@ -2198,7 +2469,11 @@ QuicConnRecvResumptionTicket(
"[conn][%p] Server app accepted resumption ticket",
Connection);
ResumptionAccepted = TRUE;
- Connection->Crypto.TicketValidationPending = FALSE;
+ if (!QuicConnIsQMux(Connection)) {
+ Connection->Crypto.TicketValidationPending = FALSE;
+ } else {
+ QuicConnGetQMux(Connection)->TicketValidationPending = FALSE;
+ }
} else if (Status == QUIC_STATUS_PENDING) {
QuicTraceEvent(
ConnServerResumeTicket,
@@ -2212,7 +2487,11 @@ QuicConnRecvResumptionTicket(
Connection,
"Resumption Ticket rejected by server app");
ResumptionAccepted = FALSE;
- Connection->Crypto.TicketValidationPending = FALSE;
+ if (!QuicConnIsQMux(Connection)) {
+ Connection->Crypto.TicketValidationPending = FALSE;
+ } else {
+ QuicConnGetQMux(Connection)->TicketValidationPending = FALSE;
+ }
}
} else {
@@ -2302,71 +2581,89 @@ QuicConnGenerateLocalTransportParameters(
{
CXPLAT_TEL_ASSERT(Connection->Configuration != NULL);
- CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL);
- const QUIC_CID_HASH_ENTRY* SourceCid =
- CXPLAT_CONTAINING_RECORD(
- Connection->SourceCids.Next,
- QUIC_CID_HASH_ENTRY,
- Link);
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Connection) || Connection->SourceCids.Next != NULL);
+ const QUIC_CID_HASH_ENTRY* SourceCid = NULL;
+ if (!QuicConnIsQMux(Connection)) {
+ SourceCid =
+ CXPLAT_CONTAINING_RECORD(
+ Connection->SourceCids.Next,
+ QUIC_CID_HASH_ENTRY,
+ Link);
+ }
LocalTP->InitialMaxData = Connection->Send.MaxData;
LocalTP->InitialMaxStreamDataBidiLocal = Connection->Settings.StreamRecvWindowBidiLocalDefault;
LocalTP->InitialMaxStreamDataBidiRemote = Connection->Settings.StreamRecvWindowBidiRemoteDefault;
LocalTP->InitialMaxStreamDataUni = Connection->Settings.StreamRecvWindowUnidiDefault;
- LocalTP->MaxUdpPayloadSize =
- MaxUdpPayloadSizeFromMTU(
- CxPlatSocketGetLocalMtu(
- Connection->Paths[0].Binding->Socket,
- &Connection->Paths[0].Route));
- LocalTP->MaxAckDelay = QuicConnGetAckDelay(Connection);
- LocalTP->MinAckDelay =
- MsQuicLib.ExecutionConfig != NULL &&
- MsQuicLib.ExecutionConfig->PollingIdleTimeoutUs != 0 ?
- 0 : MS_TO_US(MsQuicLib.TimerResolutionMs);
- LocalTP->ActiveConnectionIdLimit = QUIC_ACTIVE_CONNECTION_ID_LIMIT;
- LocalTP->Flags =
- QUIC_TP_FLAG_INITIAL_MAX_DATA |
- QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_LOCAL |
- QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_REMOTE |
- QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_UNI |
- QUIC_TP_FLAG_MAX_UDP_PAYLOAD_SIZE |
- QUIC_TP_FLAG_MAX_ACK_DELAY |
- QUIC_TP_FLAG_MIN_ACK_DELAY |
- QUIC_TP_FLAG_ACTIVE_CONNECTION_ID_LIMIT;
+ if (!QuicConnIsQMux(Connection)) {
+ LocalTP->MaxUdpPayloadSize =
+ MaxUdpPayloadSizeFromMTU(
+ CxPlatSocketGetLocalMtu(
+ Connection->Paths[0].Binding->Socket,
+ &Connection->Paths[0].Route));
+ LocalTP->MaxAckDelay = QuicConnGetAckDelay(Connection);
+ LocalTP->MinAckDelay =
+ MsQuicLib.ExecutionConfig != NULL &&
+ MsQuicLib.ExecutionConfig->PollingIdleTimeoutUs != 0 ?
+ 0 : MS_TO_US(MsQuicLib.TimerResolutionMs);
+ LocalTP->ActiveConnectionIdLimit = QUIC_ACTIVE_CONNECTION_ID_LIMIT;
+ }
+
+ if (!QuicConnIsQMux(Connection)) {
+ LocalTP->Flags =
+ QUIC_TP_FLAG_INITIAL_MAX_DATA |
+ QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_LOCAL |
+ QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_REMOTE |
+ QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_UNI |
+ QUIC_TP_FLAG_MAX_UDP_PAYLOAD_SIZE |
+ QUIC_TP_FLAG_MAX_ACK_DELAY |
+ QUIC_TP_FLAG_MIN_ACK_DELAY |
+ QUIC_TP_FLAG_ACTIVE_CONNECTION_ID_LIMIT;
+ } else {
+ LocalTP->Flags =
+ QUIC_TP_FLAG_INITIAL_MAX_DATA |
+ QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_LOCAL |
+ QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_REMOTE |
+ QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_UNI;
+ }
if (Connection->Settings.IdleTimeoutMs != 0) {
LocalTP->Flags |= QUIC_TP_FLAG_IDLE_TIMEOUT;
LocalTP->IdleTimeout = Connection->Settings.IdleTimeoutMs;
}
- if (Connection->AckDelayExponent != QUIC_TP_ACK_DELAY_EXPONENT_DEFAULT) {
+ if (!QuicConnIsQMux(Connection) &&
+ Connection->AckDelayExponent != QUIC_TP_ACK_DELAY_EXPONENT_DEFAULT) {
LocalTP->Flags |= QUIC_TP_FLAG_ACK_DELAY_EXPONENT;
LocalTP->AckDelayExponent = Connection->AckDelayExponent;
}
- LocalTP->Flags |= QUIC_TP_FLAG_INITIAL_SOURCE_CONNECTION_ID;
- LocalTP->InitialSourceConnectionIDLength = SourceCid->CID.Length;
- CxPlatCopyMemory(
- LocalTP->InitialSourceConnectionID,
- SourceCid->CID.Data,
- SourceCid->CID.Length);
+ if (!QuicConnIsQMux(Connection)) {
+ LocalTP->Flags |= QUIC_TP_FLAG_INITIAL_SOURCE_CONNECTION_ID;
+ LocalTP->InitialSourceConnectionIDLength = SourceCid->CID.Length;
+ CxPlatCopyMemory(
+ LocalTP->InitialSourceConnectionID,
+ SourceCid->CID.Data,
+ SourceCid->CID.Length);
+ }
if (Connection->Settings.DatagramReceiveEnabled) {
LocalTP->Flags |= QUIC_TP_FLAG_MAX_DATAGRAM_FRAME_SIZE;
LocalTP->MaxDatagramFrameSize = QUIC_DEFAULT_MAX_DATAGRAM_LENGTH;
}
- if (Connection->State.Disable1RttEncrytion) {
+ if (!QuicConnIsQMux(Connection) && Connection->State.Disable1RttEncrytion) {
LocalTP->Flags |= QUIC_TP_FLAG_DISABLE_1RTT_ENCRYPTION;
}
- if (Connection->CibirId[0] != 0) {
+ if (!QuicConnIsQMux(Connection) && Connection->CibirId[0] != 0) {
LocalTP->Flags |= QUIC_TP_FLAG_CIBIR_ENCODING;
LocalTP->CibirLength = Connection->CibirId[0];
LocalTP->CibirOffset = Connection->CibirId[1];
}
- if (Connection->Settings.VersionNegotiationExtEnabled
+ if (!QuicConnIsQMux(Connection) &&
+ Connection->Settings.VersionNegotiationExtEnabled
#if QUIC_TEST_DISABLE_VNE_TP_GENERATION
&& !Connection->State.DisableVneTp
#endif
@@ -2382,7 +2679,7 @@ QuicConnGenerateLocalTransportParameters(
}
}
- if (Connection->Settings.GreaseQuicBitEnabled) {
+ if (!QuicConnIsQMux(Connection) && Connection->Settings.GreaseQuicBitEnabled) {
LocalTP->Flags |= QUIC_TP_FLAG_GREASE_QUIC_BIT;
}
@@ -2390,7 +2687,7 @@ QuicConnGenerateLocalTransportParameters(
LocalTP->Flags |= QUIC_TP_FLAG_RELIABLE_RESET_ENABLED;
}
- if (Connection->Settings.OneWayDelayEnabled) {
+ if (!QuicConnIsQMux(Connection) && Connection->Settings.OneWayDelayEnabled) {
LocalTP->Flags |= QUIC_TP_FLAG_TIMESTAMP_RECV_ENABLED |
QUIC_TP_FLAG_TIMESTAMP_SEND_ENABLED;
}
@@ -2409,27 +2706,29 @@ QuicConnGenerateLocalTransportParameters(
Connection->Streams.Types[STREAM_ID_FLAG_IS_CLIENT | STREAM_ID_FLAG_IS_UNI_DIR].MaxTotalStreamCount;
}
- if (!Connection->Settings.MigrationEnabled) {
+ if (!QuicConnIsQMux(Connection) && !Connection->Settings.MigrationEnabled) {
LocalTP->Flags |= QUIC_TP_FLAG_DISABLE_ACTIVE_MIGRATION;
}
- LocalTP->Flags |= QUIC_TP_FLAG_STATELESS_RESET_TOKEN;
- QUIC_STATUS Status =
- QuicLibraryGenerateStatelessResetToken(
- Connection->Partition,
- SourceCid->CID.Data,
- LocalTP->StatelessResetToken);
- if (QUIC_FAILED(Status)) {
- QuicTraceEvent(
- ConnErrorStatus,
- "[conn][%p] ERROR, %u, %s.",
- Connection,
- Status,
- "QuicLibraryGenerateStatelessResetToken");
- return Status;
+ if (!QuicConnIsQMux(Connection) && SourceCid != NULL) {
+ LocalTP->Flags |= QUIC_TP_FLAG_STATELESS_RESET_TOKEN;
+ QUIC_STATUS Status =
+ QuicLibraryGenerateStatelessResetToken(
+ Connection->Partition,
+ SourceCid->CID.Data,
+ LocalTP->StatelessResetToken);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "QuicLibraryGenerateStatelessResetToken");
+ return Status;
+ }
}
- if (Connection->OrigDestCID != NULL) {
+ if (!QuicConnIsQMux(Connection) && Connection->OrigDestCID != NULL) {
CXPLAT_DBG_ASSERT(Connection->OrigDestCID->Length <= QUIC_MAX_CONNECTION_ID_LENGTH_V1);
LocalTP->Flags |= QUIC_TP_FLAG_ORIGINAL_DESTINATION_CONNECTION_ID;
LocalTP->OriginalDestinationConnectionIDLength = Connection->OrigDestCID->Length;
@@ -2439,7 +2738,7 @@ QuicConnGenerateLocalTransportParameters(
Connection->OrigDestCID->Length);
if (Connection->State.HandshakeUsedRetryPacket) {
- CXPLAT_DBG_ASSERT(SourceCid->Link.Next != NULL);
+ CXPLAT_DBG_ASSERT(SourceCid != NULL && SourceCid->Link.Next != NULL);
const QUIC_CID_HASH_ENTRY* PrevSourceCid =
CXPLAT_CONTAINING_RECORD(
SourceCid->Link.Next,
@@ -2523,38 +2822,39 @@ QuicConnSetConfiguration(
}
}
- CXPLAT_DBG_ASSERT(!CxPlatListIsEmpty(&Connection->DestCids));
- const QUIC_CID_LIST_ENTRY* DestCid =
- CXPLAT_CONTAINING_RECORD(
- Connection->DestCids.Flink,
- QUIC_CID_LIST_ENTRY,
- Link);
-
- //
- // Save the original CID for later validation in the TP.
- //
- Connection->OrigDestCID =
- CXPLAT_ALLOC_NONPAGED(
- sizeof(QUIC_CID) +
- DestCid->CID.Length,
- QUIC_POOL_CID);
- if (Connection->OrigDestCID == NULL) {
- QuicTraceEvent(
- AllocFailure,
- "Allocation of '%s' failed. (%llu bytes)",
- "OrigDestCID",
- sizeof(QUIC_CID) + DestCid->CID.Length);
- Status = QUIC_STATUS_OUT_OF_MEMORY;
- goto Error;
- }
+ if (!QuicConnIsQMux(Connection)) {
+ CXPLAT_DBG_ASSERT(!CxPlatListIsEmpty(&Connection->DestCids));
+ const QUIC_CID_LIST_ENTRY* DestCid =
+ CXPLAT_CONTAINING_RECORD(
+ Connection->DestCids.Flink,
+ QUIC_CID_LIST_ENTRY,
+ Link);
- Connection->OrigDestCID->Length = DestCid->CID.Length;
- CxPlatCopyMemory(
- Connection->OrigDestCID->Data,
- DestCid->CID.Data,
- DestCid->CID.Length);
+ //
+ // Save the original CID for later validation in the TP.
+ //
+ Connection->OrigDestCID =
+ CXPLAT_ALLOC_NONPAGED(
+ sizeof(QUIC_CID) +
+ DestCid->CID.Length,
+ QUIC_POOL_CID);
+ if (Connection->OrigDestCID == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "OrigDestCID",
+ sizeof(QUIC_CID) + DestCid->CID.Length);
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Error;
+ }
- } else {
+ Connection->OrigDestCID->Length = DestCid->CID.Length;
+ CxPlatCopyMemory(
+ Connection->OrigDestCID->Data,
+ DestCid->CID.Data,
+ DestCid->CID.Length);
+ }
+ } else if (!QuicConnIsQMux(Connection)) {
if (!QuicConnPostAcceptValidatePeerTransportParameters(Connection)) {
QuicConnTransportError(Connection, QUIC_ERROR_CONNECTION_REFUSED);
Status = QUIC_STATUS_INVALID_PARAMETER;
@@ -2594,11 +2894,18 @@ QuicConnSetConfiguration(
"[conn][%p] Handshake start",
Connection);
- Status =
- QuicCryptoInitializeTls(
- &Connection->Crypto,
- Configuration->SecurityConfig,
- &LocalTP);
+ if (!QuicConnIsQMux(Connection)) {
+ Status =
+ QuicCryptoInitializeTls(
+ &Connection->Crypto,
+ Configuration->SecurityConfig,
+ &LocalTP);
+ } else {
+ Status =
+ QuicQMuxInitializeTls(
+ QuicConnGetQMux(Connection),
+ Configuration->SecurityConfig);
+ }
Cleanup:
@@ -2616,6 +2923,8 @@ QuicConnValidateTransportParameterCIDs(
_In_ QUIC_CONNECTION* Connection
)
{
+ CXPLAT_DBG_ASSERT(!QuicConnIsQMux(Connection));
+
if (!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_INITIAL_SOURCE_CONNECTION_ID)) {
QuicTraceEvent(
ConnError,
@@ -3073,7 +3382,8 @@ QuicConnProcessPeerTransportParameters(
//
// Fully validate all exchanged connection IDs.
//
- if (!QuicConnValidateTransportParameterCIDs(Connection)) {
+ if (!QuicConnIsQMux(Connection) &&
+ !QuicConnValidateTransportParameterCIDs(Connection)) {
goto Error;
}
@@ -6147,7 +6457,6 @@ 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.
@@ -6163,7 +6472,8 @@ QuicConnResetIdleTimeout(
}
if (IdleTimeoutMs != 0) {
- if (Connection->State.Connected) {
+ if (Connection->State.Connected && !QuicConnIsQMux(Connection)) {
+ QUIC_PATH* Path = &Connection->Paths[0];
//
// Idle timeout must be no less than the PTOs for closing.
//
@@ -6216,11 +6526,18 @@ QuicConnProcessKeepAliveOperation(
_In_ QUIC_CONNECTION* Connection
)
{
- //
- // Send a PING frame to keep the connection alive.
- //
- Connection->Send.TailLossProbeNeeded = TRUE;
- QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PING);
+ if (!QuicConnIsQMux(Connection)) {
+ //
+ // Send a PING frame to keep the connection alive.
+ //
+ Connection->Send.TailLossProbeNeeded = TRUE;
+ QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PING);
+ } else {
+ //
+ // Send a QX PING frame to keep the connection alive.
+ //
+ QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_QX_PING);
+ }
//
// Restart the keep alive timer.
@@ -6620,8 +6937,12 @@ QuicConnParamSet(
(uint16_t)BufferLength,
Buffer,
&Connection->PeerTransportParams,
- &Connection->Crypto.ResumptionTicket,
- &Connection->Crypto.ResumptionTicketLength,
+ !QuicConnIsQMux(Connection) ?
+ &Connection->Crypto.ResumptionTicket :
+ &QuicConnGetQMux(Connection)->ResumptionTicket,
+ !QuicConnIsQMux(Connection) ?
+ &Connection->Crypto.ResumptionTicketLength :
+ &QuicConnGetQMux(Connection)->ResumptionTicketLength,
&Connection->Stats.QuicVersion);
if (QUIC_FAILED(Status)) {
break;
@@ -6630,6 +6951,9 @@ QuicConnParamSet(
QuicConnOnQuicVersionSet(Connection);
Status = QuicConnProcessPeerTransportParameters(Connection, TRUE);
CXPLAT_DBG_ASSERT(QUIC_SUCCEEDED(Status));
+ if (QuicConnIsQMux(Connection)) {
+ QuicConnGetQMux(Connection)->PermitEarlyData = TRUE;
+ }
break;
}
@@ -7082,10 +7406,17 @@ QuicConnParamGet(
}
*BufferLength = sizeof(QUIC_ADDR);
- CxPlatCopyMemory(
- Buffer,
- &Connection->Paths[0].Route.LocalAddress,
- sizeof(QUIC_ADDR));
+ if (!QuicConnIsQMux(Connection)) {
+ CxPlatCopyMemory(
+ Buffer,
+ &Connection->Paths[0].Route.LocalAddress,
+ sizeof(QUIC_ADDR));
+ } else {
+ CxPlatCopyMemory(
+ Buffer,
+ &QuicConnGetQMux(Connection)->Route.LocalAddress,
+ sizeof(QUIC_ADDR));
+ }
Status = QUIC_STATUS_SUCCESS;
break;
@@ -7109,10 +7440,17 @@ QuicConnParamGet(
}
*BufferLength = sizeof(QUIC_ADDR);
- CxPlatCopyMemory(
- Buffer,
- &Connection->Paths[0].Route.RemoteAddress,
- sizeof(QUIC_ADDR));
+ if (!QuicConnIsQMux(Connection)) {
+ CxPlatCopyMemory(
+ Buffer,
+ &Connection->Paths[0].Route.RemoteAddress,
+ sizeof(QUIC_ADDR));
+ } else {
+ CxPlatCopyMemory(
+ Buffer,
+ &QuicConnGetQMux(Connection)->Route.RemoteAddress,
+ sizeof(QUIC_ADDR));
+ }
Status = QUIC_STATUS_SUCCESS;
break;
@@ -7859,7 +8197,8 @@ QuicConnDrainOperations(
//
CXPLAT_DBG_ASSERT(QuicConnIsServer(Connection));
QUIC_STATUS Status;
- if (QUIC_FAILED(Status = QuicCryptoInitialize(&Connection->Crypto))) {
+ if (!QuicConnIsQMux(Connection) &&
+ QUIC_FAILED(Status = QuicCryptoInitialize(&Connection->Crypto))) {
QuicConnFatalError(Connection, Status, "Lazily initialize failure");
} else {
Connection->State.Initialized = TRUE;
@@ -7966,6 +8305,27 @@ QuicConnDrainOperations(
Connection, Oper->ROUTE.PhysicalAddress, Oper->ROUTE.PathId, Oper->ROUTE.Succeeded);
break;
+ case QUIC_OPER_TYPE_FLUSH_TCP_RECV:
+ if (Connection->State.ShutdownComplete) {
+ break; // Ignore if already shutdown
+ }
+ if (!QuicQMuxFlushRecv(QuicConnGetQMux(Connection))) {
+ //
+ // Still have more data to recv. Put the operation back on the
+ // queue.
+ //
+ FreeOper = FALSE;
+ (void)QuicOperationEnqueue(&Connection->OperQ, Connection->Partition, Oper);
+ }
+ break;
+
+ case QUIC_OPER_TYPE_TCP_DISCONNECT:
+ if (Connection->State.ShutdownComplete) {
+ break; // Ignore if already shutdown
+ }
+ QuicQMuxProcessTcpDisconnect(QuicConnGetQMux(Connection));
+ break;
+
default:
CXPLAT_FRE_ASSERT(FALSE);
break;
diff --git a/src/core/connection.h b/src/core/connection.h
index 512e750dc1..c64a8be57e 100644
--- a/src/core/connection.h
+++ b/src/core/connection.h
@@ -30,6 +30,10 @@ typedef union QUIC_CONNECTION_STATE {
uint64_t Flags;
struct {
BOOLEAN Allocated : 1; // Allocated. Used for Debugging.
+ BOOLEAN IsQMux : 1; // Connection created by QMux.
+ BOOLEAN TcpConnected : 1; // TCP connection established (QMux).
+ BOOLEAN PeerTPReceived : 1; // Peer transport parameters received (QMux).
+ BOOLEAN LocalTPSent : 1; // Local transport parameters sent (QMux).
BOOLEAN Initialized : 1; // Initialized successfully. Used for Debugging.
BOOLEAN Started : 1; // Handshake started.
BOOLEAN Connected : 1; // Handshake completed.
@@ -392,6 +396,11 @@ typedef struct QUIC_CONNECTION {
//
QUIC_CONNECTION_STATE State;
+ //
+ // QMux-specific state. Only valid if State.IsQmux is true.
+ //
+ QUIC_QMUX* QMux;
+
//
// The current worker thread ID. 0 if not being processed right now.
//
@@ -740,6 +749,33 @@ typedef struct QUIC_SERIALIZED_RESUMPTION_STATE {
#define QuicConnAllocOperation(Connection, Type) \
QuicOperationAlloc((Connection)->Partition, (Type))
+//
+// Helper to get the QMUX module.
+//
+QUIC_INLINE
+_Ret_notnull_
+QUIC_QMUX*
+QuicConnGetQMux(
+ _In_ QUIC_CONNECTION* Connection
+ )
+{
+ CXPLAT_DBG_ASSERT(Connection->State.IsQMux);
+ CXPLAT_DBG_ASSERT(Connection->QMux != NULL);
+ return Connection->QMux;
+}
+
+//
+// Helper to determine if a connection is for QMux.
+//
+QUIC_INLINE
+BOOLEAN
+QuicConnIsQMux(
+ _In_ const QUIC_CONNECTION * const Connection
+ )
+{
+ return Connection->State.IsQMux;
+}
+
//
// Helper to determine if a connection is server side.
//
@@ -1038,6 +1074,22 @@ QuicConnAlloc(
QUIC_CONNECTION** NewConnection
);
+//
+// Allocates and initializes a connection object for QMux.
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+_Must_inspect_result_
+_Success_(return == QUIC_STATUS_SUCCESS)
+QUIC_STATUS
+QuicConnQMuxAlloc(
+ _In_ QUIC_REGISTRATION* Registration,
+ _In_ QUIC_PARTITION* Partition,
+ _In_opt_ QUIC_WORKER* Worker,
+ _In_ BOOLEAN IsServer,
+ _Outptr_ _At_(*NewConnection, __drv_allocatesMem(Mem))
+ QUIC_CONNECTION** NewConnection
+ );
+
//
// Called to free the memory for a connection.
//
@@ -1047,6 +1099,15 @@ QuicConnFree(
_In_ __drv_freesMem(Mem) QUIC_CONNECTION* Connection
);
+//
+// Called to free the memory for a QMux connection.
+//
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicConnQMuxFree(
+ _In_ __drv_freesMem(Mem) QUIC_CONNECTION* Connection
+ );
+
//
// Releases the handle usage of the app.
//
@@ -1137,7 +1198,11 @@ QuicConnRelease(
CXPLAT_DBG_ASSERT(Connection->RefTypeBiasedCount[i] == 1);
}
#endif
- QuicConnFree(Connection);
+ if (!QuicConnIsQMux(Connection)) {
+ QuicConnFree(Connection);
+ } else {
+ QuicConnQMuxFree(Connection);
+ }
}
}
}
@@ -1533,6 +1598,17 @@ QuicConnFlushDeferred(
_In_ QUIC_CONNECTION* Connection
);
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicConnTryClose(
+ _In_ QUIC_CONNECTION* Connection,
+ _In_ uint32_t Flags,
+ _In_ uint64_t ErrorCode,
+ _In_reads_bytes_opt_(RemoteReasonPhraseLength)
+ const char* RemoteReasonPhrase,
+ _In_ uint16_t RemoteReasonPhraseLength
+ );
+
//
// Starts the (async) process of closing the connection locally.
//
diff --git a/src/core/core.kernel.vcxproj b/src/core/core.kernel.vcxproj
index 1a92edfb93..d5fc493de5 100644
--- a/src/core/core.kernel.vcxproj
+++ b/src/core/core.kernel.vcxproj
@@ -44,6 +44,7 @@
+
diff --git a/src/core/crypto.c b/src/core/crypto.c
index 57a061139f..dc6c0c6977 100644
--- a/src/core/crypto.c
+++ b/src/core/crypto.c
@@ -2724,7 +2724,8 @@ QuicCryptoDecodeServerTicket(
uint32_t QuicVersion;
memcpy(&QuicVersion, Ticket + Offset, sizeof(QuicVersion));
- if (!QuicVersionNegotiationExtIsVersionClientSupported(Connection, QuicVersion)) {
+ if (!QuicConnIsQMux(Connection) &&
+ !QuicVersionNegotiationExtIsVersionClientSupported(Connection, QuicVersion)) {
QuicTraceEvent(
ConnError,
"[conn][%p] ERROR, %s.",
diff --git a/src/core/crypto_tls.c b/src/core/crypto_tls.c
index 713405fef7..dbf9fd0f4f 100644
--- a/src/core/crypto_tls.c
+++ b/src/core/crypto_tls.c
@@ -68,6 +68,7 @@ typedef enum eSniNameType {
#define QUIC_TP_ID_GREASE_QUIC_BIT 0x2AB2 // N/A
#define QUIC_TP_ID_RELIABLE_RESET_ENABLED 0x17f7586d2cb570 // varint
#define QUIC_TP_ID_ENABLE_TIMESTAMP 0x7158 // varint
+#define QX_TP_ID_MAX_RECORD_SIZE 0x0571c59429cd0845 // varint
BOOLEAN
QuicTpIdIsReserved(
@@ -904,6 +905,12 @@ QuicCryptoTlsEncodeTransportParameters(
QUIC_TP_ID_ENABLE_TIMESTAMP,
QuicVarIntSize(value));
}
+ if (TransportParams->Flags & QX_TP_FLAG_MAX_RECORD_SIZE) {
+ RequiredTPLen +=
+ TlsTransportParamLength(
+ QX_TP_ID_MAX_RECORD_SIZE,
+ QuicVarIntSize(TransportParams->MaxRecordSize));
+ }
if (TestParam != NULL) {
RequiredTPLen +=
TlsTransportParamLength(
@@ -1246,6 +1253,18 @@ QuicCryptoTlsEncodeTransportParameters(
"TP: Timestamp (%u)",
value);
}
+ if (TransportParams->Flags & QX_TP_FLAG_MAX_RECORD_SIZE) {
+ TPBuf =
+ TlsWriteTransportParamVarInt(
+ QX_TP_ID_MAX_RECORD_SIZE,
+ TransportParams->MaxRecordSize,
+ TPBuf);
+ QuicTraceLogConnVerbose(
+ EncodeTPMaxRecordSize,
+ Connection,
+ "TP: Max Record Size (%llu)",
+ TransportParams->MaxRecordSize);
+ }
if (TestParam != NULL) {
TPBuf =
TlsWriteTransportParam(
@@ -1953,6 +1972,24 @@ QuicCryptoTlsDecodeTransportParameters( // NOLINT(readability-function-size, goo
break;
}
+ case QX_TP_ID_MAX_RECORD_SIZE:
+ if (!TRY_READ_VAR_INT(TransportParams->MaxRecordSize)) {
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Length,
+ "Invalid length of QX_TP_ID_MAX_RECORD_SIZE");
+ goto Exit;
+ }
+ TransportParams->Flags |= QX_TP_FLAG_MAX_RECORD_SIZE;
+ QuicTraceLogConnVerbose(
+ DecodeTPMaxRecordSize,
+ Connection,
+ "TP: Max Record Size (%llu)",
+ TransportParams->MaxRecordSize);
+ break;
+
default:
if (QuicTpIdIsReserved(Id)) {
QuicTraceLogConnWarning(
diff --git a/src/core/datagram.c b/src/core/datagram.c
index 01ce817738..68de767800 100644
--- a/src/core/datagram.c
+++ b/src/core/datagram.c
@@ -263,23 +263,35 @@ QuicDatagramOnSendStateChanged(
}
if (SendEnabled) {
- uint16_t MtuMaxSendLength;
- if (!Connection->State.Started) {
- MtuMaxSendLength =
- QuicCalculateDatagramLength(
- QUIC_ADDRESS_FAMILY_INET6,
- QUIC_DPLPMTUD_MIN_MTU,
- QUIC_MIN_INITIAL_CONNECTION_ID_LENGTH);
+ if (!QuicConnIsQMux(Connection)) {
+ uint16_t MtuMaxSendLength;
+ if (!Connection->State.Started) {
+ MtuMaxSendLength =
+ QuicCalculateDatagramLength(
+ QUIC_ADDRESS_FAMILY_INET6,
+ QUIC_DPLPMTUD_MIN_MTU,
+ QUIC_MIN_INITIAL_CONNECTION_ID_LENGTH);
+ } else {
+ const QUIC_PATH* Path = &Connection->Paths[0];
+ MtuMaxSendLength =
+ QuicCalculateDatagramLength(
+ QuicAddrGetFamily(&Path->Route.RemoteAddress),
+ Path->Mtu,
+ Path->DestCid->CID.Length);
+ }
+ if (NewMaxSendLength > MtuMaxSendLength) {
+ NewMaxSendLength = MtuMaxSendLength;
+ }
} else {
- const QUIC_PATH* Path = &Connection->Paths[0];
- MtuMaxSendLength =
- QuicCalculateDatagramLength(
- QuicAddrGetFamily(&Path->Route.RemoteAddress),
- Path->Mtu,
- Path->DestCid->CID.Length);
- }
- if (NewMaxSendLength > MtuMaxSendLength) {
- NewMaxSendLength = MtuMaxSendLength;
+ uint16_t RecordMaxSendLength = UINT16_MAX;
+ if (!(Connection->PeerTransportParams.Flags & QX_TP_FLAG_MAX_RECORD_SIZE)) {
+ RecordMaxSendLength = QX_TP_MAX_RECORD_SIZE_DEFAULT - DATAGRAM_FRAME_HEADER_LENGTH;
+ } else if (Connection->PeerTransportParams.MaxRecordSize <= UINT16_MAX + DATAGRAM_FRAME_HEADER_LENGTH) {
+ RecordMaxSendLength = (uint16_t)(Connection->PeerTransportParams.MaxRecordSize - DATAGRAM_FRAME_HEADER_LENGTH);
+ }
+ if (NewMaxSendLength > RecordMaxSendLength) {
+ NewMaxSendLength = RecordMaxSendLength;
+ }
}
}
@@ -551,7 +563,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN
QuicDatagramProcessFrame(
_In_ QUIC_DATAGRAM* Datagram,
- _In_ const QUIC_RX_PACKET* const Packet,
+ _In_opt_ const QUIC_RX_PACKET* const Packet,
_In_ QUIC_FRAME_TYPE FrameType,
_In_ uint16_t BufferLength,
_In_reads_bytes_(BufferLength)
@@ -574,7 +586,7 @@ QuicDatagramProcessFrame(
QUIC_CONNECTION_EVENT Event;
Event.Type = QUIC_CONNECTION_EVENT_DATAGRAM_RECEIVED;
Event.DATAGRAM_RECEIVED.Buffer = &QuicBuffer;
- if (Packet->EncryptedWith0Rtt) {
+ if (Packet != NULL && Packet->EncryptedWith0Rtt) {
Event.DATAGRAM_RECEIVED.Flags = QUIC_RECEIVE_FLAG_0_RTT;
} else {
Event.DATAGRAM_RECEIVED.Flags = 0;
diff --git a/src/core/datagram.h b/src/core/datagram.h
index 95caf08ceb..c3445f3b37 100644
--- a/src/core/datagram.h
+++ b/src/core/datagram.h
@@ -96,7 +96,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
BOOLEAN
QuicDatagramProcessFrame(
_In_ QUIC_DATAGRAM* Datagram,
- _In_ const QUIC_RX_PACKET* const Packet,
+ _In_opt_ const QUIC_RX_PACKET* const Packet,
_In_ QUIC_FRAME_TYPE FrameType,
_In_ uint16_t BufferLength,
_In_reads_bytes_(BufferLength)
diff --git a/src/core/frame.c b/src/core/frame.c
index c5afcc628f..e18c8b76b6 100644
--- a/src/core/frame.c
+++ b/src/core/frame.c
@@ -280,6 +280,9 @@ QuicAckFrameDecode(
)
{
*InvalidFrame = FALSE;
+ if (Ecn != NULL) {
+ CxPlatZeroMemory(Ecn, sizeof(*Ecn));
+ }
CXPLAT_DBG_ASSERT(AckRanges->SubRanges); // Should be pre-initialized.
//
@@ -358,6 +361,7 @@ QuicAckFrameDecode(
//
// The ECN section was provided. Decode it as well.
//
+ CXPLAT_DBG_ASSERT(Ecn != NULL);
if (!QuicAckEcnDecode(BufferLength, Buffer, Offset, Ecn)) {
return FALSE;
}
@@ -662,7 +666,7 @@ QuicStreamFrameDecode(
_Out_ QUIC_STREAM_EX* Frame
)
{
- QUIC_STREAM_FRAME_TYPE Type = { .Type = FrameType };
+ QUIC_STREAM_FRAME_TYPE Type = { .Type = (uint8_t)FrameType };
if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID)) {
return FALSE;
}
@@ -1078,7 +1082,7 @@ QuicPathChallengeFrameEncode(
}
Buffer = Buffer + *Offset;
- Buffer = QuicUint8Encode(FrameType, Buffer);
+ Buffer = QuicUint8Encode((uint8_t)FrameType, Buffer);
CxPlatCopyMemory(Buffer, Frame->Data, sizeof(Frame->Data));
*Offset += RequiredLength;
@@ -1224,7 +1228,7 @@ QuicDatagramFrameDecode(
_Out_ QUIC_DATAGRAM_EX* Frame
)
{
- QUIC_DATAGRAM_FRAME_TYPE Type = { .Type = FrameType };
+ QUIC_DATAGRAM_FRAME_TYPE Type = { .Type = (uint8_t)FrameType };
if (Type.LEN) {
if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Length) ||
BufferLength < Frame->Length + *Offset) {
@@ -1331,6 +1335,96 @@ QuicTimestampFrameDecode(
return TRUE;
}
+_Success_(return != FALSE)
+BOOLEAN
+QxTransportParametersFrameEncode(
+ _In_ const QX_TRANSPORT_PARAMETERS_EX * const Frame,
+ _Inout_ uint16_t* Offset,
+ _In_ uint16_t BufferLength,
+ _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
+ )
+{
+ uint16_t RequiredLength =
+ QuicVarIntSize(QX_FRAME_TRANSPORT_PARAMETERS) + // Type
+ QuicVarIntSize(Frame->Length) +
+ (uint16_t)Frame->Length;
+
+ if (BufferLength < *Offset + RequiredLength) {
+ return FALSE;
+ }
+
+ Buffer = Buffer + *Offset;
+ Buffer = QuicVarIntEncode(QX_FRAME_TRANSPORT_PARAMETERS, Buffer);
+ Buffer = QuicVarIntEncode(Frame->Length, Buffer);
+ CxPlatCopyMemory(Buffer, Frame->TP, (size_t)Frame->Length);
+ *Offset += RequiredLength;
+
+ return TRUE;
+}
+
+_Success_(return != FALSE)
+BOOLEAN
+QxTransportParametersFrameDecode(
+ _In_ uint16_t BufferLength,
+ _In_reads_bytes_(BufferLength)
+ const uint8_t * const Buffer,
+ _Inout_ uint16_t* Offset,
+ _Out_ QX_TRANSPORT_PARAMETERS_EX* Frame
+ )
+{
+ if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Length) ||
+ BufferLength < Frame->Length + *Offset) {
+ return FALSE;
+ }
+ Frame->TP = Buffer + *Offset;
+ *Offset += (uint16_t)Frame->Length;
+ return TRUE;
+}
+
+_Success_(return != FALSE)
+BOOLEAN
+QxPingFrameEncode(
+ _In_ const QX_PING_EX * const Frame,
+ _Inout_ uint16_t* Offset,
+ _In_ uint16_t BufferLength,
+ _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
+ )
+{
+ QUIC_FRAME_TYPE FrameType = !Frame->IsResponse ? QX_FRAME_PING : QX_FRAME_PING_1;
+ uint16_t RequiredLength =
+ QuicVarIntSize(FrameType) +
+ QuicVarIntSize(Frame->SequenceNumber);
+
+ if (BufferLength < *Offset + RequiredLength) {
+ return FALSE;
+ }
+
+ Buffer = Buffer + *Offset;
+ Buffer = QuicVarIntEncode(FrameType, Buffer);
+ Buffer = QuicVarIntEncode(Frame->SequenceNumber, Buffer);
+ *Offset += RequiredLength;
+
+ return TRUE;
+}
+
+_Success_(return != FALSE)
+BOOLEAN
+QxPingFrameDecode(
+ _In_ QUIC_FRAME_TYPE FrameType,
+ _In_ uint16_t BufferLength,
+ _In_reads_bytes_(BufferLength)
+ const uint8_t * const Buffer,
+ _Inout_ uint16_t* Offset,
+ _Out_ QX_PING_EX* Frame
+ )
+{
+ if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->SequenceNumber)) {
+ return FALSE;
+ }
+ Frame->IsResponse = (FrameType == QX_FRAME_PING_1);
+ return TRUE;
+}
+
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN
QuicFrameLog(
@@ -2013,6 +2107,51 @@ QuicFrameLog(
break;
}
+ case QX_FRAME_TRANSPORT_PARAMETERS: {
+ QX_TRANSPORT_PARAMETERS_EX Frame;
+ if (!QxTransportParametersFrameDecode(PacketLength, Packet, Offset, &Frame)) {
+ QuicTraceLogVerbose(
+ FrameLogQxTransportParametersInvalid,
+ "[%c][%cX][%llu] QX TRANSPORT_PARAMETERS [Invalid]",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+ return FALSE;
+ }
+
+ QuicTraceLogVerbose(
+ FrameLogQxTransportParameters,
+ "[%c][%cX][%llu] QX TRANSPORT_PARAMETERS",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+ break;
+ }
+
+ case QX_FRAME_PING:
+ case QX_FRAME_PING_1: {
+ QX_PING_EX Frame;
+ if (!QxPingFrameDecode(FrameType, PacketLength, Packet, Offset, &Frame)) {
+ QuicTraceLogVerbose(
+ FrameLogQxPingInvalid,
+ "[%c][%cX][%llu] QX PING [Invalid]",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+ return FALSE;
+ }
+
+ QuicTraceLogVerbose(
+ FrameLogQxPing,
+ "[%c][%cX][%llu] QX PING %hu %llu",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber,
+ Frame.IsResponse,
+ Frame.SequenceNumber);
+ break;
+ }
+
default:
CXPLAT_FRE_ASSERT(FALSE);
break;
diff --git a/src/core/frame.h b/src/core/frame.h
index 35b94bd812..1a2363bf96 100644
--- a/src/core/frame.h
+++ b/src/core/frame.h
@@ -117,67 +117,61 @@ QuicErrorIsProtocolError(
//
// Different types of QUIC frames
//
-typedef enum QUIC_FRAME_TYPE {
- QUIC_FRAME_PADDING = 0x0ULL,
- QUIC_FRAME_PING = 0x1ULL,
- QUIC_FRAME_ACK = 0x2ULL, // to 0x3
- QUIC_FRAME_ACK_1 = 0x3ULL,
- QUIC_FRAME_RESET_STREAM = 0x4ULL,
- QUIC_FRAME_STOP_SENDING = 0x5ULL,
- QUIC_FRAME_CRYPTO = 0x6ULL,
- QUIC_FRAME_NEW_TOKEN = 0x7ULL,
- QUIC_FRAME_STREAM = 0x8ULL, // to 0xf
- QUIC_FRAME_STREAM_1 = 0x9ULL,
- QUIC_FRAME_STREAM_2 = 0xaULL,
- QUIC_FRAME_STREAM_3 = 0xbULL,
- QUIC_FRAME_STREAM_4 = 0xcULL,
- QUIC_FRAME_STREAM_5 = 0xdULL,
- QUIC_FRAME_STREAM_6 = 0xeULL,
- QUIC_FRAME_STREAM_7 = 0xfULL,
- QUIC_FRAME_MAX_DATA = 0x10ULL,
- QUIC_FRAME_MAX_STREAM_DATA = 0x11ULL,
- QUIC_FRAME_MAX_STREAMS = 0x12ULL, // to 0x13
- QUIC_FRAME_MAX_STREAMS_1 = 0x13ULL,
- QUIC_FRAME_DATA_BLOCKED = 0x14ULL,
- QUIC_FRAME_STREAM_DATA_BLOCKED = 0x15ULL,
- QUIC_FRAME_STREAMS_BLOCKED = 0x16ULL, // to 0x17
- QUIC_FRAME_STREAMS_BLOCKED_1 = 0x17ULL,
- QUIC_FRAME_NEW_CONNECTION_ID = 0x18ULL,
- QUIC_FRAME_RETIRE_CONNECTION_ID = 0x19ULL,
- QUIC_FRAME_PATH_CHALLENGE = 0x1aULL,
- QUIC_FRAME_PATH_RESPONSE = 0x1bULL,
- QUIC_FRAME_CONNECTION_CLOSE = 0x1cULL, // to 0x1d
- QUIC_FRAME_CONNECTION_CLOSE_1 = 0x1dULL,
- QUIC_FRAME_HANDSHAKE_DONE = 0x1eULL,
- /* 0x1f to 0x20 are unused currently */
- QUIC_FRAME_RELIABLE_RESET_STREAM = 0x21ULL, // intentionally ignore type 0x20 of QUIC_RESET_STREAM cause it's likely to be removed from RFC.
- /* 0x22 to 0x2f are unused currently */
- QUIC_FRAME_DATAGRAM = 0x30ULL, // to 0x31
- QUIC_FRAME_DATAGRAM_1 = 0x31ULL,
- /* 0x32 to 0xad are unused currently */
- QUIC_FRAME_ACK_FREQUENCY = 0xafULL,
- QUIC_FRAME_IMMEDIATE_ACK = 0x1fULL,
+typedef QUIC_VAR_INT QUIC_FRAME_TYPE;
+#define QUIC_FRAME_PADDING ((QUIC_FRAME_TYPE)0x0ULL)
+#define QUIC_FRAME_PING ((QUIC_FRAME_TYPE)0x1ULL)
+#define QUIC_FRAME_ACK ((QUIC_FRAME_TYPE)0x2ULL) // to 0x3
+#define QUIC_FRAME_ACK_1 ((QUIC_FRAME_TYPE)0x3ULL)
+#define QUIC_FRAME_RESET_STREAM ((QUIC_FRAME_TYPE)0x4ULL)
+#define QUIC_FRAME_STOP_SENDING ((QUIC_FRAME_TYPE)0x5ULL)
+#define QUIC_FRAME_CRYPTO ((QUIC_FRAME_TYPE)0x6ULL)
+#define QUIC_FRAME_NEW_TOKEN ((QUIC_FRAME_TYPE)0x7ULL)
+#define QUIC_FRAME_STREAM ((QUIC_FRAME_TYPE)0x8ULL) // to 0xf
+#define QUIC_FRAME_STREAM_1 ((QUIC_FRAME_TYPE)0x9ULL)
+#define QUIC_FRAME_STREAM_2 ((QUIC_FRAME_TYPE)0xaULL)
+#define QUIC_FRAME_STREAM_3 ((QUIC_FRAME_TYPE)0xbULL)
+#define QUIC_FRAME_STREAM_4 ((QUIC_FRAME_TYPE)0xcULL)
+#define QUIC_FRAME_STREAM_5 ((QUIC_FRAME_TYPE)0xdULL)
+#define QUIC_FRAME_STREAM_6 ((QUIC_FRAME_TYPE)0xeULL)
+#define QUIC_FRAME_STREAM_7 ((QUIC_FRAME_TYPE)0xfULL)
+#define QUIC_FRAME_MAX_DATA ((QUIC_FRAME_TYPE)0x10ULL)
+#define QUIC_FRAME_MAX_STREAM_DATA ((QUIC_FRAME_TYPE)0x11ULL)
+#define QUIC_FRAME_MAX_STREAMS ((QUIC_FRAME_TYPE)0x12ULL) // to 0x13
+#define QUIC_FRAME_MAX_STREAMS_1 ((QUIC_FRAME_TYPE)0x13ULL)
+#define QUIC_FRAME_DATA_BLOCKED ((QUIC_FRAME_TYPE)0x14ULL)
+#define QUIC_FRAME_STREAM_DATA_BLOCKED ((QUIC_FRAME_TYPE)0x15ULL)
+#define QUIC_FRAME_STREAMS_BLOCKED ((QUIC_FRAME_TYPE)0x16ULL) // to 0x17
+#define QUIC_FRAME_STREAMS_BLOCKED_1 ((QUIC_FRAME_TYPE)0x17ULL)
+#define QUIC_FRAME_NEW_CONNECTION_ID ((QUIC_FRAME_TYPE)0x18ULL)
+#define QUIC_FRAME_RETIRE_CONNECTION_ID ((QUIC_FRAME_TYPE)0x19ULL)
+#define QUIC_FRAME_PATH_CHALLENGE ((QUIC_FRAME_TYPE)0x1aULL)
+#define QUIC_FRAME_PATH_RESPONSE ((QUIC_FRAME_TYPE)0x1bULL)
+#define QUIC_FRAME_CONNECTION_CLOSE ((QUIC_FRAME_TYPE)0x1cULL) // to 0x1d
+#define QUIC_FRAME_CONNECTION_CLOSE_1 ((QUIC_FRAME_TYPE)0x1dULL)
+#define QUIC_FRAME_HANDSHAKE_DONE ((QUIC_FRAME_TYPE)0x1eULL)
+/* 0x1f to 0x20 are unused currently */
+#define QUIC_FRAME_RELIABLE_RESET_STREAM ((QUIC_FRAME_TYPE)0x21ULL) // intentionally ignore type 0x20 of QUIC_RESET_STREAM cause it's likely to be removed from RFC.
+/* 0x22 to 0x2f are unused currently */
+#define QUIC_FRAME_DATAGRAM ((QUIC_FRAME_TYPE)0x30ULL) // to 0x31
+#define QUIC_FRAME_DATAGRAM_1 ((QUIC_FRAME_TYPE)0x31ULL)
+/* 0x32 to 0xad are unused currently */
+#define QUIC_FRAME_ACK_FREQUENCY ((QUIC_FRAME_TYPE)0xafULL)
+#define QUIC_FRAME_IMMEDIATE_ACK ((QUIC_FRAME_TYPE)0x1fULL)
/* 0xaf to 0x2f4 are unused currently */
- QUIC_FRAME_TIMESTAMP = 0x2f5ULL,
+#define QUIC_FRAME_TIMESTAMP ((QUIC_FRAME_TYPE)0x2f5ULL)
+ /* 0x2f6 to 0x9f80 are unused currently */
- QUIC_FRAME_MAX_SUPPORTED
-
-} QUIC_FRAME_TYPE;
-
-CXPLAT_STATIC_ASSERT(
- QUIC_FRAME_MAX_SUPPORTED <= (uint64_t)UINT16_MAX,
- "Sent packet metadata 'Type' field above assumes frames types fit in 16-bits");
-
-CXPLAT_STATIC_ASSERT(
- QUIC_FRAME_MAX_SUPPORTED <= (uint64_t)UINT32_MAX,
- "Logging assumes frames types fit in 32-bits");
+#define QX_FRAME_TRANSPORT_PARAMETERS ((QUIC_FRAME_TYPE)0x3f5153300d0a0d0aULL)
+#define QX_FRAME_PING ((QUIC_FRAME_TYPE)0x348c67529ef8c7bdULL)
+#define QX_FRAME_PING_1 ((QUIC_FRAME_TYPE)0x348c67529ef8c7beULL)
#define QUIC_FRAME_IS_KNOWN(X) \
(X <= QUIC_FRAME_HANDSHAKE_DONE || \
(X >= QUIC_FRAME_DATAGRAM && X <= QUIC_FRAME_DATAGRAM_1) || \
X == QUIC_FRAME_ACK_FREQUENCY || X == QUIC_FRAME_IMMEDIATE_ACK || \
X == QUIC_FRAME_RELIABLE_RESET_STREAM || \
- X == QUIC_FRAME_TIMESTAMP \
+ X == QUIC_FRAME_TIMESTAMP || \
+ X == QX_FRAME_TRANSPORT_PARAMETERS || X == QX_FRAME_PING || X == QX_FRAME_PING_1 \
)
//
@@ -903,6 +897,60 @@ QuicTimestampFrameDecode(
_Out_ QUIC_TIMESTAMP_EX* Frame
);
+typedef struct QX_TRANSPORT_PARAMETERS_EX {
+
+ QUIC_VAR_INT Length;
+ _Field_size_bytes_(Length)
+ const uint8_t * TP;
+
+} QX_TRANSPORT_PARAMETERS_EX;
+
+_Success_(return != FALSE)
+BOOLEAN
+QxTransportParametersFrameEncode(
+ _In_ const QX_TRANSPORT_PARAMETERS_EX * const Frame,
+ _Inout_ uint16_t* Offset,
+ _In_ uint16_t BufferLength,
+ _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
+ );
+
+_Success_(return != FALSE)
+BOOLEAN
+QxTransportParametersFrameDecode(
+ _In_ uint16_t BufferLength,
+ _In_reads_bytes_(BufferLength)
+ const uint8_t * const Buffer,
+ _Inout_ uint16_t* Offset,
+ _Out_ QX_TRANSPORT_PARAMETERS_EX* Frame
+ );
+
+typedef struct QX_PING_EX {
+
+ QUIC_VAR_INT SequenceNumber;
+ BOOLEAN IsResponse;
+
+} QX_PING_EX;
+
+_Success_(return != FALSE)
+BOOLEAN
+QxPingFrameEncode(
+ _In_ const QX_PING_EX * const Frame,
+ _Inout_ uint16_t* Offset,
+ _In_ uint16_t BufferLength,
+ _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
+ );
+
+_Success_(return != FALSE)
+BOOLEAN
+QxPingFrameDecode(
+ _In_ QUIC_FRAME_TYPE FrameType,
+ _In_ uint16_t BufferLength,
+ _In_reads_bytes_(BufferLength)
+ const uint8_t * const Buffer,
+ _Inout_ uint16_t* Offset,
+ _Out_ QX_PING_EX* Frame
+ );
+
//
// Helper functions
//
diff --git a/src/core/library.c b/src/core/library.c
index 2498e2bfb5..9925087499 100644
--- a/src/core/library.c
+++ b/src/core/library.c
@@ -876,6 +876,13 @@ QuicLibraryLazyInitialize(
QuicBindingUnreachable,
};
+ const CXPLAT_TCP_DATAPATH_CALLBACKS DatapathTcpCallbacks = {
+ QuicQMuxTcpAccept,
+ QuicQMuxTcpConnect,
+ QuicQMuxTcpReceive,
+ QuicQMuxTcpSendComplete,
+ };
+
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
BOOLEAN CreatedWorkerPool = FALSE;
@@ -914,7 +921,7 @@ QuicLibraryLazyInitialize(
CxPlatDataPathInitialize(
sizeof(QUIC_RX_PACKET),
&DatapathCallbacks,
- NULL, // TcpCallbacks
+ &DatapathTcpCallbacks,
MsQuicLib.WorkerPool,
&InitConfig,
&MsQuicLib.Datapath);
@@ -2158,6 +2165,10 @@ MsQuicOpenVersion(
Api->ConnectionPoolCreate = MsQuicConnectionPoolCreate;
+ Api->ConnectionQmuxOpen = MsQuicConnectionQmuxOpen;
+ Api->ConnectionQmuxOpenInPartition = MsQuicConnectionQmuxOpenInPartition;
+ Api->ListenerQmuxOpen = MsQuicListenerQmuxOpen;
+
*QuicApi = Api;
Exit:
diff --git a/src/core/listener.c b/src/core/listener.c
index 97ba781523..7af97ead4e 100644
--- a/src/core/listener.c
+++ b/src/core/listener.c
@@ -36,8 +36,9 @@ QuicListenerIsOnWorker(
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
QUIC_API
-MsQuicListenerOpen(
+QuicListenerOpen(
_In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ BOOLEAN IsQmux,
_In_ _Pre_defensive_ QUIC_LISTENER_CALLBACK_HANDLER Handler,
_In_opt_ void* Context,
_Outptr_ _At_(*NewListener, __drv_allocatesMem(Mem)) _Pre_defensive_
@@ -82,6 +83,7 @@ MsQuicListenerOpen(
Listener->ClientContext = Context;
Listener->Stopped = TRUE;
Listener->DosModeEventsEnabled = FALSE;
+ Listener->IsQmux = IsQmux;
CxPlatEventInitialize(&Listener->StopEvent, TRUE, TRUE);
CxPlatRefInitialize(&Listener->RefCount);
@@ -137,6 +139,44 @@ MsQuicListenerOpen(
return Status;
}
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicListenerOpen(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ _Pre_defensive_ QUIC_LISTENER_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*NewListener, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewListener
+ )
+{
+ return QuicListenerOpen(
+ RegistrationHandle,
+ FALSE,
+ Handler,
+ Context,
+ NewListener);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicListenerQmuxOpen(
+ _In_ _Pre_defensive_ HQUIC RegistrationHandle,
+ _In_ _Pre_defensive_ QUIC_LISTENER_CALLBACK_HANDLER Handler,
+ _In_opt_ void* Context,
+ _Outptr_ _At_(*NewListener, __drv_allocatesMem(Mem)) _Pre_defensive_
+ HQUIC *NewListener
+ )
+{
+ return QuicListenerOpen(
+ RegistrationHandle,
+ TRUE,
+ Handler,
+ Context,
+ NewListener);
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicListenerFree(
@@ -306,10 +346,17 @@ MsQuicListenerStart(
if (LocalAddress != NULL) {
CxPlatCopyMemory(&Listener->LocalAddress, LocalAddress, sizeof(QUIC_ADDR));
+ if (Listener->IsQmux && QuicAddrGetFamily(LocalAddress) == QUIC_ADDRESS_FAMILY_UNSPEC) {
+ // QMUX listeners must use an IPv6 family address for unspecified family.
+ QuicAddrSetFamily(&Listener->LocalAddress, QUIC_ADDRESS_FAMILY_INET6);
+ }
Listener->WildCard = QuicAddrIsWildCard(LocalAddress);
PortUnspecified = QuicAddrGetPort(LocalAddress) == 0;
} else {
CxPlatZeroMemory(&Listener->LocalAddress, sizeof(Listener->LocalAddress));
+ if (Listener->IsQmux) {
+ QuicAddrSetFamily(&Listener->LocalAddress, QUIC_ADDRESS_FAMILY_INET6);
+ }
Listener->WildCard = TRUE;
PortUnspecified = TRUE;
}
@@ -328,74 +375,98 @@ MsQuicListenerStart(
goto Error;
}
- CXPLAT_UDP_CONFIG UdpConfig = {0};
- UdpConfig.LocalAddress = &BindingLocalAddress;
- UdpConfig.RemoteAddress = NULL;
- UdpConfig.Flags = CXPLAT_SOCKET_FLAG_SHARE | CXPLAT_SOCKET_SERVER_OWNED; // Listeners always share the binding.
- UdpConfig.InterfaceIndex = 0;
+ if (!Listener->IsQmux) {
+ CXPLAT_UDP_CONFIG UdpConfig = {0};
+ UdpConfig.LocalAddress = &BindingLocalAddress;
+ UdpConfig.RemoteAddress = NULL;
+ UdpConfig.Flags = CXPLAT_SOCKET_FLAG_SHARE | CXPLAT_SOCKET_SERVER_OWNED; // Listeners always share the binding.
+ UdpConfig.InterfaceIndex = 0;
#ifdef QUIC_COMPARTMENT_ID
- UdpConfig.CompartmentId = QuicCompartmentIdGetCurrent();
+ UdpConfig.CompartmentId = QuicCompartmentIdGetCurrent();
#endif
#ifdef QUIC_OWNING_PROCESS
- UdpConfig.OwningProcess = NULL; // Owning process not supported for listeners.
+ UdpConfig.OwningProcess = NULL; // Owning process not supported for listeners.
#endif
- if (Listener->Partitioned) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_PARTITIONED;
- UdpConfig.PartitionIndex = Listener->PartitionIndex;
- }
+ if (Listener->Partitioned) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_PARTITIONED;
+ UdpConfig.PartitionIndex = Listener->PartitionIndex;
+ }
- // for RAW datapath
- UdpConfig.CibirIdLength = Listener->CibirId[0];
- UdpConfig.CibirIdOffsetSrc = MsQuicLib.CidServerIdLength + 2;
- UdpConfig.CibirIdOffsetDst = MsQuicLib.CidServerIdLength + 2;
- if (UdpConfig.CibirIdLength) {
- CXPLAT_DBG_ASSERT(UdpConfig.CibirIdLength <= sizeof(UdpConfig.CibirId));
- CxPlatCopyMemory(
- UdpConfig.CibirId,
- &Listener->CibirId[2],
- UdpConfig.CibirIdLength);
- }
+ // for RAW datapath
+ UdpConfig.CibirIdLength = Listener->CibirId[0];
+ UdpConfig.CibirIdOffsetSrc = MsQuicLib.CidServerIdLength + 2;
+ UdpConfig.CibirIdOffsetDst = MsQuicLib.CidServerIdLength + 2;
+ if (UdpConfig.CibirIdLength) {
+ CXPLAT_DBG_ASSERT(UdpConfig.CibirIdLength <= sizeof(UdpConfig.CibirId));
+ CxPlatCopyMemory(
+ UdpConfig.CibirId,
+ &Listener->CibirId[2],
+ UdpConfig.CibirIdLength);
+ }
- if (MsQuicLib.Settings.XdpEnabled) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_XDP;
- }
- if (MsQuicLib.Settings.QTIPEnabled) {
- UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_QTIP;
- }
+ if (MsQuicLib.Settings.XdpEnabled) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_XDP;
+ }
+ if (MsQuicLib.Settings.QTIPEnabled) {
+ UdpConfig.Flags |= CXPLAT_SOCKET_FLAG_QTIP;
+ }
- CXPLAT_TEL_ASSERT(Listener->Binding == NULL);
- Status =
- QuicLibraryGetBinding(
- &UdpConfig,
- &Listener->Binding);
- if (QUIC_FAILED(Status)) {
- QuicTraceEvent(
- ListenerErrorStatus,
- "[list][%p] ERROR, %u, %s.",
- Listener,
- Status,
- "Get binding");
- goto Error;
+ CXPLAT_TEL_ASSERT(Listener->Binding == NULL);
+ Status =
+ QuicLibraryGetBinding(
+ &UdpConfig,
+ &Listener->Binding);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "Get binding");
+ goto Error;
+ }
+ } else {
+ Status =
+ CxPlatSocketCreateTcpListener(
+ MsQuicLib.Datapath,
+ &Listener->LocalAddress,
+ Listener,
+ &Listener->Socket);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "Create TCP listener");
+ goto Error;
+ }
}
Listener->Stopped = FALSE;
CxPlatEventReset(Listener->StopEvent);
CxPlatRefInitialize(&Listener->StartRefCount);
- Status = QuicBindingRegisterListener(Listener->Binding, Listener);
- if (QUIC_FAILED(Status)) {
- QuicTraceEvent(
- ListenerErrorStatus,
- "[list][%p] ERROR, %u, %s.",
- Listener,
- Status,
- "Register with binding");
- QuicListenerStartRelease(Listener, FALSE);
- goto Error;
+ if (!Listener->IsQmux) {
+ Status = QuicBindingRegisterListener(Listener->Binding, Listener);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "Register with binding");
+ QuicListenerStartRelease(Listener, FALSE);
+ goto Error;
+ }
}
if (PortUnspecified) {
- QuicBindingGetLocalAddress(Listener->Binding, &BindingLocalAddress);
+ if (!Listener->IsQmux) {
+ QuicBindingGetLocalAddress(Listener->Binding, &BindingLocalAddress);
+ } else {
+ CxPlatSocketGetLocalAddress(Listener->Socket, &BindingLocalAddress);
+ }
QuicAddrSetPort(
&Listener->LocalAddress,
QuicAddrGetPort(&BindingLocalAddress));
@@ -416,6 +487,10 @@ MsQuicListenerStart(
QuicLibraryReleaseBinding(Listener->Binding);
Listener->Binding = NULL;
}
+ if (Listener->Socket != NULL) {
+ CxPlatSocketDelete(Listener->Socket);
+ Listener->Socket = NULL;
+ }
if (Listener->AlpnList != NULL) {
CXPLAT_FREE(Listener->AlpnList, QUIC_POOL_ALPN);
Listener->AlpnList = NULL;
@@ -597,12 +672,20 @@ QuicListenerStopAsync(
_In_ QUIC_LISTENER* Listener
)
{
- if (Listener->Binding != NULL) {
- QuicBindingUnregisterListener(Listener->Binding, Listener);
- QuicLibraryReleaseBinding(Listener->Binding);
- Listener->Binding = NULL;
+ if (!Listener->IsQmux) {
+ if (Listener->Binding != NULL) {
+ QuicBindingUnregisterListener(Listener->Binding, Listener);
+ QuicLibraryReleaseBinding(Listener->Binding);
+ Listener->Binding = NULL;
- QuicListenerStartRelease(Listener, TRUE);
+ QuicListenerStartRelease(Listener, TRUE);
+ }
+ } else {
+ if (Listener->Socket != NULL) {
+ CxPlatSocketDelete(Listener->Socket);
+ Listener->Socket = NULL;
+ QuicListenerStartRelease(Listener, TRUE);
+ }
}
}
diff --git a/src/core/listener.h b/src/core/listener.h
index f87a526608..f31bd6b0df 100644
--- a/src/core/listener.h
+++ b/src/core/listener.h
@@ -58,6 +58,7 @@ typedef struct QUIC_LISTENER {
//
BOOLEAN Partitioned : 1;
+ BOOLEAN IsQmux : 1;
//
// The thread ID that the listener is actively indicating a stop compelete
// callback on.
@@ -135,6 +136,7 @@ typedef struct QUIC_LISTENER {
//
QUIC_BINDING* Binding;
+ CXPLAT_SOCKET* Socket;
//
// The handler for the API client's callbacks.
//
@@ -195,6 +197,13 @@ QuicListenerTraceRundown(
_In_ QUIC_LISTENER* Listener
);
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicListenerIndicateEvent(
+ _In_ QUIC_LISTENER* Listener,
+ _Inout_ QUIC_LISTENER_EVENT* Event
+ );
+
_IRQL_requires_max_(DISPATCH_LEVEL)
void
QuicListenerStartReference(
diff --git a/src/core/operation.h b/src/core/operation.h
index e8e7a9246a..03d2f2388d 100644
--- a/src/core/operation.h
+++ b/src/core/operation.h
@@ -31,6 +31,8 @@ typedef enum QUIC_OPERATION_TYPE {
QUIC_OPER_TYPE_TIMER_EXPIRED, // A timer expired.
QUIC_OPER_TYPE_TRACE_RUNDOWN, // A trace rundown was triggered.
QUIC_OPER_TYPE_ROUTE_COMPLETION, // Process route completion event.
+ QUIC_OPER_TYPE_FLUSH_TCP_RECV, // Process queue of received TCP data.
+ QUIC_OPER_TYPE_TCP_DISCONNECT, // Process TCP disconnect event.
//
// All stateless operations follow.
diff --git a/src/core/packet_builder.c b/src/core/packet_builder.c
index 2c7d8792e7..ca99342017 100644
--- a/src/core/packet_builder.c
+++ b/src/core/packet_builder.c
@@ -33,6 +33,12 @@ QuicPacketBuilderSendBatch(
_Inout_ QUIC_PACKET_BUILDER* Builder
);
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicPacketBuilderQMuxSendBatch(
+ _Inout_ QUIC_PACKET_BUILDER* Builder
+ );
+
#if DEBUG
_IRQL_requires_max_(PASSIVE_LEVEL)
void
@@ -42,7 +48,7 @@ QuicPacketBuilderValidate(
)
{
if (ShouldHaveData) {
- CXPLAT_DBG_ASSERT(Builder->Key != NULL);
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Builder->Connection) || Builder->Key != NULL);
CXPLAT_DBG_ASSERT(Builder->SendData != NULL);
CXPLAT_DBG_ASSERT(Builder->Datagram != NULL);
CXPLAT_DBG_ASSERT(Builder->DatagramLength != 0);
@@ -50,8 +56,8 @@ QuicPacketBuilderValidate(
CXPLAT_DBG_ASSERT(Builder->Metadata->FrameCount != 0);
}
- CXPLAT_DBG_ASSERT(Builder->Path != NULL);
- CXPLAT_DBG_ASSERT(Builder->Path->DestCid != NULL);
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Builder->Connection) || Builder->Path != NULL);
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Builder->Connection) || Builder->Path->DestCid != NULL);
CXPLAT_DBG_ASSERT(Builder->BatchCount <= QUIC_MAX_CRYPTO_BATCH_COUNT);
if (Builder->Key != NULL) {
@@ -59,7 +65,7 @@ QuicPacketBuilderValidate(
CXPLAT_DBG_ASSERT(Builder->Key->HeaderKey != NULL);
}
- CXPLAT_DBG_ASSERT(Builder->EncryptionOverhead <= 16);
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Builder->Connection) || Builder->EncryptionOverhead <= 16);
if (Builder->SendData == NULL) {
CXPLAT_DBG_ASSERT(Builder->Datagram == NULL);
}
@@ -72,7 +78,7 @@ QuicPacketBuilderValidate(
CXPLAT_DBG_ASSERT(Builder->DatagramLength >= Builder->PacketStart);
CXPLAT_DBG_ASSERT(Builder->DatagramLength >= Builder->HeaderLength);
CXPLAT_DBG_ASSERT(Builder->DatagramLength >= Builder->PacketStart + Builder->HeaderLength);
- if (Builder->PacketType != SEND_PACKET_SHORT_HEADER_TYPE) {
+ if (!QuicConnIsQMux(Builder->Connection) && Builder->PacketType != SEND_PACKET_SHORT_HEADER_TYPE) {
CXPLAT_DBG_ASSERT(Builder->PayloadLengthOffset != 0);
if (ShouldHaveData) {
CXPLAT_DBG_ASSERT(Builder->DatagramLength >= Builder->PacketStart + Builder->PayloadLengthOffset);
@@ -96,29 +102,37 @@ QuicPacketBuilderInitialize(
_In_ QUIC_PATH* Path
)
{
- CXPLAT_DBG_ASSERT(Path->DestCid != NULL);
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Connection) || Path->DestCid != NULL);
Builder->Connection = Connection;
Builder->Path = Path;
Builder->PacketBatchSent = FALSE;
Builder->PacketBatchRetransmittable = FALSE;
Builder->WrittenConnectionCloseFrame = FALSE;
Builder->Metadata = &Builder->MetadataStorage.Metadata;
- Builder->EncryptionOverhead = CXPLAT_ENCRYPTION_OVERHEAD;
+ if (!QuicConnIsQMux(Connection)) {
+ Builder->EncryptionOverhead = CXPLAT_ENCRYPTION_OVERHEAD;
+ } else {
+ CXPLAT_TLS_RECORD_OVERHEAD Overhead;
+ CxPlatTlsGetRecordOverhead(QuicConnGetQMux(Connection)->TLS, &Overhead);
+ Builder->EncryptionOverhead = (uint16_t)Overhead.MaxTrailer;
+ }
Builder->TotalDatagramsLength = 0;
- if (Connection->SourceCids.Next == NULL) {
- QuicTraceLogConnWarning(
- NoSrcCidAvailable,
- Connection,
- "No src CID to send with");
- return FALSE;
- }
+ if (!QuicConnIsQMux(Connection)) {
+ if (Connection->SourceCids.Next == NULL) {
+ QuicTraceLogConnWarning(
+ NoSrcCidAvailable,
+ Connection,
+ "No src CID to send with");
+ return FALSE;
+ }
- Builder->SourceCid =
- CXPLAT_CONTAINING_RECORD(
- Connection->SourceCids.Next,
- QUIC_CID_HASH_ENTRY,
- Link);
+ Builder->SourceCid =
+ CXPLAT_CONTAINING_RECORD(
+ Connection->SourceCids.Next,
+ QUIC_CID_HASH_ENTRY,
+ Link);
+ }
uint64_t TimeNow = CxPlatTimeUs64();
uint64_t TimeSinceLastSend;
@@ -128,13 +142,16 @@ QuicPacketBuilderInitialize(
} else {
TimeSinceLastSend = 0;
}
- Builder->SendAllowance =
- QuicCongestionControlGetSendAllowance(
- &Connection->CongestionControl,
- TimeSinceLastSend,
- Connection->Send.LastFlushTimeValid);
- if (Builder->SendAllowance > Path->Allowance) {
- Builder->SendAllowance = Path->Allowance;
+
+ if (!QuicConnIsQMux(Connection)) {
+ Builder->SendAllowance =
+ QuicCongestionControlGetSendAllowance(
+ &Connection->CongestionControl,
+ TimeSinceLastSend,
+ Connection->Send.LastFlushTimeValid);
+ if (Builder->SendAllowance > Path->Allowance) {
+ Builder->SendAllowance = Path->Allowance;
+ }
}
Connection->Send.LastFlushTime = TimeNow;
Connection->Send.LastFlushTimeValid = TRUE;
@@ -176,6 +193,7 @@ QuicPacketBuilderPrepare(
)
{
QUIC_CONNECTION* Connection = Builder->Connection;
+ CXPLAT_DBG_ASSERT(!QuicConnIsQMux(Connection));
if (Connection->Crypto.TlsState.WriteKeys[NewPacketKeyType] == NULL) {
//
// A NULL key here usually means the connection had a fatal error in
@@ -488,6 +506,142 @@ QuicPacketBuilderPrepare(
return Result;
}
+_IRQL_requires_max_(PASSIVE_LEVEL)
+_Success_(return != FALSE)
+BOOLEAN
+QuicPacketBuilderQMuxPrepare(
+ _Inout_ QUIC_PACKET_BUILDER* Builder
+ )
+{
+ QUIC_CONNECTION* Connection = Builder->Connection;
+ BOOLEAN Result = FALSE;
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Builder->Connection));
+ QUIC_QMUX* QMux = QuicConnGetQMux(Builder->Connection);
+ CXPLAT_TLS_RECORD_OVERHEAD Overhead;
+ CxPlatTlsGetRecordOverhead(QMux->TLS, &Overhead);
+ uint16_t DatagramSize = (uint16_t)Overhead.MaxHeader
+ + QuicVarIntSize(QX_TP_MAX_RECORD_SIZE_DEFAULT)
+ + QX_TP_MAX_RECORD_SIZE_DEFAULT
+ + (uint16_t)Overhead.MaxTrailer;
+ QuicPacketBuilderValidate(Builder, FALSE);
+
+ //
+ // Next, make sure the current QUIC packet matches the new packet type. If
+ // the current one doesn't match, finalize it and then start a new one.
+ //
+
+ QUIC_PARTITION* Partition = Connection->Partition;
+ const uint64_t PartitionShifted = ((uint64_t)Partition->Index + 1) << 40;
+
+ BOOLEAN NewQuicPacket = FALSE;
+ if ((Builder->Datagram != NULL && (Builder->Datagram->Length - Builder->DatagramLength) < QUIC_MIN_PACKET_SPARE_SPACE)) {
+ //
+ // The current data cannot go in the current QUIC packet. Finalize the
+ // current QUIC packet up so we can create another.
+ //
+ CXPLAT_DBG_ASSERT(Builder->SendData != NULL);
+ if (QMux->PermitEarlyData) {
+ // Early data cannot exceed the size of a single datagram,
+ // so if we're here, it means the current datagram is already
+ // full of early data and needs to be flushed.
+ QuicPacketBuilderQMuxFinalize(Builder, TRUE);
+ QMux->PermitEarlyData = FALSE;
+ return FALSE;
+ } else {
+ QuicPacketBuilderFinalize(Builder, FALSE);
+ }
+ NewQuicPacket = TRUE;
+
+ } else if (Builder->Datagram == NULL) {
+ NewQuicPacket = TRUE;
+ }
+
+ if (Builder->Datagram == NULL) {
+
+ //
+ // Allocate and initialize a new send buffer (UDP packet/payload).
+ //
+ BOOLEAN SendDataAllocated = FALSE;
+ if (Builder->SendData == NULL) {
+ Builder->BatchId =
+ PartitionShifted | InterlockedIncrement64((int64_t*)&Partition->SendBatchId);
+ CXPLAT_SEND_CONFIG SendConfig = {
+ &QMux->Route,
+ DatagramSize,
+ Builder->EcnEctSet ? CXPLAT_ECN_ECT_0 : CXPLAT_ECN_NON_ECT,
+ Builder->Connection->Registration->ExecProfile == QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT ?
+ CXPLAT_SEND_FLAGS_MAX_THROUGHPUT : CXPLAT_SEND_FLAGS_NONE,
+ Connection->DSCP
+ };
+ Builder->SendData =
+ CxPlatSendDataAlloc(QMux->Socket, &SendConfig);
+ if (Builder->SendData == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "packet send context",
+ 0);
+ goto Error;
+ }
+ SendDataAllocated = TRUE;
+ }
+
+ Builder->Datagram =
+ CxPlatSendDataAllocBuffer(
+ Builder->SendData,
+ DatagramSize);
+ if (Builder->Datagram == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "packet datagram",
+ DatagramSize);
+ if (SendDataAllocated) {
+ CxPlatSendDataFree(Builder->SendData);
+ Builder->SendData = NULL;
+ }
+ goto Error;
+ }
+
+ Builder->DatagramLength = 0;
+ Builder->MinimumDatagramLength = 0;
+ }
+
+ if (NewQuicPacket) {
+
+ //
+ // Initialize the new QUIC packet state.
+ //
+
+ Builder->Metadata->PacketId =
+ PartitionShifted | InterlockedIncrement64((int64_t*)&Partition->SendPacketId);
+ QuicTraceEvent(
+ PacketCreated,
+ "[pack][%llu] Created in batch %llu",
+ Builder->Metadata->PacketId,
+ Builder->BatchId);
+
+ Builder->Metadata->FrameCount = 0;
+#if DEBUG
+ Builder->Metadata->Flags.Freed = FALSE;
+#endif
+
+ Builder->PacketStart = Builder->DatagramLength;
+ Builder->HeaderLength = 5 + 2; // 5 byte header, 2 byte length.
+
+ Builder->DatagramLength += Builder->HeaderLength;
+ }
+
+ Result = TRUE;
+
+Error:
+
+ QuicPacketBuilderValidate(Builder, FALSE);
+
+ return Result;
+
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
_Success_(return != FALSE)
BOOLEAN
@@ -620,6 +774,9 @@ QuicPacketBuilderPrepareForControlFrames(
_In_ uint32_t SendFlags
)
{
+ if (QuicConnIsQMux(Builder->Connection)) {
+ return QuicPacketBuilderQMuxPrepare(Builder);
+ }
CXPLAT_DBG_ASSERT(!(SendFlags & QUIC_CONN_SEND_FLAG_DPLPMTUD));
QUIC_PACKET_KEY_TYPE PacketKeyType;
return
@@ -641,6 +798,7 @@ QuicPacketBuilderPrepareForPathMtuDiscovery(
_Inout_ QUIC_PACKET_BUILDER* Builder
)
{
+ CXPLAT_DBG_ASSERT(!QuicConnIsQMux(Builder->Connection));
return
QuicPacketBuilderPrepare(
Builder,
@@ -658,6 +816,9 @@ QuicPacketBuilderPrepareForStreamFrames(
_In_ BOOLEAN IsTailLossProbe
)
{
+ if (QuicConnIsQMux(Builder->Connection)) {
+ return QuicPacketBuilderQMuxPrepare(Builder);
+ }
QUIC_PACKET_KEY_TYPE PacketKeyType;
if (Builder->Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_0_RTT] != NULL &&
@@ -682,6 +843,7 @@ QuicPacketBuilderFinalizeHeaderProtection(
_Inout_ QUIC_PACKET_BUILDER* Builder
)
{
+ CXPLAT_DBG_ASSERT(!QuicConnIsQMux(Builder->Connection));
CXPLAT_DBG_ASSERT(Builder->Key != NULL);
QUIC_STATUS Status;
@@ -1083,6 +1245,257 @@ QuicPacketBuilderFinalize(
return CanKeepSending;
}
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+QuicPacketBuilderQMuxFinalize(
+ _Inout_ QUIC_PACKET_BUILDER* Builder,
+ _In_ BOOLEAN FlushBatchedDatagrams
+ )
+{
+ QUIC_CONNECTION* Connection = Builder->Connection;
+ QUIC_QMUX* QMux = QuicConnGetQMux(Connection);
+ BOOLEAN FinalQuicPacket = FALSE;
+ BOOLEAN CanKeepSending = TRUE;
+
+ QuicPacketBuilderValidate(Builder, FALSE);
+
+ if (Builder->Datagram == NULL || Builder->Metadata->FrameCount == 0) {
+ //
+ // Nothing got framed into this packet. Undo the header of this
+ // packet.
+ //
+ if (Builder->Datagram != NULL) {
+ Builder->DatagramLength -= Builder->HeaderLength;
+ Builder->HeaderLength = 0;
+ CanKeepSending = FALSE;
+
+ if (Builder->DatagramLength == 0) {
+ CxPlatSendDataFreeBuffer(Builder->SendData, Builder->Datagram);
+ Builder->Datagram = NULL;
+ }
+ }
+ FinalQuicPacket = FlushBatchedDatagrams && (Builder->TotalCountDatagrams != 0);
+ goto Exit;
+ }
+
+ QuicPacketBuilderValidate(Builder, TRUE);
+
+ //
+ // Calculate some of the packet buffer parameters (mostly used for encryption).
+ //
+ uint8_t* Header =
+ Builder->Datagram->Buffer + Builder->PacketStart;
+ uint16_t PayloadLength =
+ Builder->DatagramLength - (Builder->PacketStart + Builder->HeaderLength);
+ uint16_t ExpectedFinalDatagramLength =
+ Builder->DatagramLength + Builder->EncryptionOverhead;
+
+ if (FlushBatchedDatagrams ||
+ (uint16_t)Builder->Datagram->Length - ExpectedFinalDatagramLength < QUIC_MIN_PACKET_SPARE_SPACE) {
+ FinalQuicPacket = TRUE;
+ }
+
+ uint8_t QMuxRecordLength = QuicVarIntSize(PayloadLength);
+ CXPLAT_DBG_ASSERT(QMuxRecordLength <= 2); // QMUX datagram length must fit in 2 bytes.
+ if (QMuxRecordLength == 1) {
+ CxPlatMoveMemory(
+ Header + (Builder->HeaderLength - 1), // Move the payload forward by 1 byte.
+ Header + Builder->HeaderLength,
+ PayloadLength);
+ Builder->HeaderLength -= 1;
+ Builder->DatagramLength -= 1;
+ }
+ QuicVarIntEncode(PayloadLength, Header + 5); // XXX 5 byte TLS record header.
+
+#ifdef QUIC_FUZZER
+ QuicFuzzInjectHook(Builder);
+#endif
+
+ if (QuicTraceLogVerboseEnabled()) {
+ QuicFrameLogAll(
+ Connection,
+ FALSE,
+ 0,
+ Builder->HeaderLength + PayloadLength,
+ Header,
+ Builder->HeaderLength);
+ }
+
+ if (QMux->PermitEarlyData) {
+ uint32_t RequiredLength = QMux->EarlyDataBufferLength + PayloadLength + QMuxRecordLength;
+ uint32_t NewEarlyDataBufferAllocLength = QMux->EarlyDataBufferAllocLength;
+ if (RequiredLength > NewEarlyDataBufferAllocLength) {
+ while (RequiredLength > NewEarlyDataBufferAllocLength) {
+ if (NewEarlyDataBufferAllocLength > UINT32_MAX / 2) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ " early data exceeds maximum buffer size");
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)QUIC_STATUS_OUT_OF_MEMORY,
+ NULL);
+ goto Exit;
+ }
+ if (NewEarlyDataBufferAllocLength == 0) {
+ NewEarlyDataBufferAllocLength = 4096; // Start with 4KB buffer.
+ } else {
+ NewEarlyDataBufferAllocLength *= 2;
+ }
+ }
+ uint8_t* NewEarlyDataBuffer = CXPLAT_ALLOC_NONPAGED(NewEarlyDataBufferAllocLength, QUIC_POOL_QMUX_EARLY_DATA_BUFFER);
+ if (NewEarlyDataBuffer == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "QMux early data buffer",
+ NewEarlyDataBufferAllocLength);
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)QUIC_STATUS_OUT_OF_MEMORY,
+ NULL);
+ goto Exit;
+ }
+ if (QMux->EarlyDataBuffer != NULL) {
+ CXPLAT_DBG_ASSERT(QMux->EarlyDataBufferLength <= NewEarlyDataBufferAllocLength);
+ CxPlatCopyMemory(NewEarlyDataBuffer, QMux->EarlyDataBuffer, QMux->EarlyDataBufferLength);
+ CXPLAT_FREE(QMux->EarlyDataBuffer, QUIC_POOL_QMUX_EARLY_DATA_BUFFER);
+ }
+ QMux->EarlyDataBuffer = NewEarlyDataBuffer;
+ }
+ CXPLAT_DBG_ASSERT(QMux->EarlyDataBuffer != NULL);
+ CxPlatCopyMemory(
+ QMux->EarlyDataBuffer + QMux->EarlyDataBufferLength,
+ Header + 5,
+ PayloadLength + QMuxRecordLength);
+ QMux->EarlyDataBufferLength += PayloadLength + QMuxRecordLength;
+
+ //
+ // Allocate a copy of the packet metadata.
+ //
+ QUIC_SENT_PACKET_METADATA* SentPacket =
+ QuicSentPacketPoolGetPacketMetadata(
+ &Connection->Partition->SentPacketPool,
+ Builder->Metadata->FrameCount);
+ if (SentPacket == NULL) {
+ //
+ // We can't allocate the memory to permanently track this packet so just
+ // go ahead and immediately clean up and mark the data in it as lost.
+ //
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "Sent packet metadata",
+ SIZEOF_QUIC_SENT_PACKET_METADATA(Builder->Metadata->FrameCount));
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)QUIC_STATUS_OUT_OF_MEMORY,
+ NULL);
+ goto Exit;
+ }
+ CxPlatCopyMemory(
+ SentPacket,
+ Builder->Metadata,
+ sizeof(QUIC_SENT_PACKET_METADATA) +
+ sizeof(QUIC_SENT_FRAME_METADATA) * Builder->Metadata->FrameCount);
+
+ //
+ // Add to the outstanding-packet queue.
+ //
+ SentPacket->Next = NULL;
+ *QMux->SentEarlyDataPacketsTail = SentPacket;
+ QMux->SentEarlyDataPacketsTail = &SentPacket->Next;
+ CanKeepSending = FALSE;
+ } else {
+ //
+ // Encrypt the data.
+ //
+
+ QuicTraceEvent(
+ PacketEncrypt,
+ "[pack][%llu] Encrypting",
+ Builder->Metadata->PacketId);
+
+ CXPLAT_TLS_ENCRYPT_BUFFER EncryptBuffer = {
+ Builder->Datagram->Buffer,
+ Builder->Datagram->Length,
+ 5,
+ PayloadLength + QMuxRecordLength
+ };
+ if (!CxPlatTlsEncrypt(
+ QMux->TLS,
+ &EncryptBuffer)) {
+ QuicConnFatalError(Connection, QUIC_STATUS_ABORTED, "Encryption failure");
+ goto Exit;
+ }
+ Builder->Datagram->Length = (uint32_t)EncryptBuffer.DataLength;
+ Builder->DatagramLength = (uint16_t)Builder->Datagram->Length;
+
+ //
+ // Track the sent packet.
+ //
+ CXPLAT_DBG_ASSERT(Builder->Metadata->FrameCount != 0);
+
+ Builder->Metadata->SentTime = CxPlatTimeUs64();
+ Builder->Metadata->PacketLength = (uint16_t)Builder->Datagram->Length;
+ Builder->Metadata->Flags.EcnEctSet = Builder->EcnEctSet;
+
+ QuicQMuxOnPacketAcknowledged(QMux, Builder->Metadata);
+ QuicSentPacketMetadataReleaseFrames(Builder->Metadata, Connection);
+ }
+
+ Builder->Metadata->FrameCount = 0;
+
+Exit:
+
+ //
+ // Send the packet out if necessary.
+ //
+
+ if (FinalQuicPacket) {
+ if (Builder->Datagram != NULL) {
+ if (Builder->Metadata->Flags.EcnEctSet) {
+ ++Connection->Send.NumPacketsSentWithEct;
+ }
+ Builder->Datagram->Length = Builder->DatagramLength;
+ Builder->Datagram = NULL;
+ ++Builder->TotalCountDatagrams;
+ Builder->TotalDatagramsLength += Builder->DatagramLength;
+ Builder->DatagramLength = 0;
+ }
+
+ if (FlushBatchedDatagrams || CxPlatSendDataIsFull(Builder->SendData)) {
+ CXPLAT_DBG_ASSERT(Builder->TotalCountDatagrams > 0);
+ QuicPacketBuilderQMuxSendBatch(Builder);
+ CXPLAT_DBG_ASSERT(Builder->Metadata->FrameCount == 0);
+ QuicTraceEvent(
+ PacketBatchSent,
+ "[pack][%llu] Batch sent",
+ Builder->BatchId);
+ }
+ } else if (FlushBatchedDatagrams) {
+ if (Builder->Datagram != NULL) {
+ CxPlatSendDataFreeBuffer(Builder->SendData, Builder->Datagram);
+ Builder->Datagram = NULL;
+ Builder->DatagramLength = 0;
+ }
+ if (Builder->SendData != NULL) {
+ CxPlatSendDataFree(Builder->SendData);
+ Builder->SendData = NULL;
+ }
+ }
+
+ QuicPacketBuilderValidate(Builder, FALSE);
+
+ CXPLAT_DBG_ASSERT(!FlushBatchedDatagrams || Builder->SendData == NULL);
+
+ return CanKeepSending;
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicPacketBuilderSendBatch(
@@ -1108,3 +1521,38 @@ QuicPacketBuilderSendBatch(
Builder->TotalDatagramsLength = 0;
Builder->Metadata->FrameCount = 0;
}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicPacketBuilderQMuxSendBatch(
+ _Inout_ QUIC_PACKET_BUILDER* Builder
+ )
+{
+ QUIC_CONNECTION* Connection = Builder->Connection;
+ QUIC_QMUX* QMux = QuicConnGetQMux(Connection);
+
+ if (QMux->PermitEarlyData) {
+ if (Builder->SendData != NULL) {
+ CxPlatSendDataFree(Builder->SendData);
+ Builder->SendData = NULL;
+ }
+ QMux->PermitEarlyData = FALSE;
+ return;
+ }
+ QuicTraceLogConnVerbose(
+ PacketBuilderQMuxSendBatch,
+ Builder->Connection,
+ "Sending batch. %hu datagrams %u bytes",
+ (uint16_t)Builder->TotalCountDatagrams,
+ Builder->TotalDatagramsLength);
+
+ CxPlatSocketSend(QMux->Socket, &QMux->Route, Builder->SendData);
+
+ Builder->PacketBatchSent = TRUE;
+ Builder->SendData = NULL;
+ Builder->TotalCountDatagrams = 0;
+ Builder->TotalDatagramsLength = 0;
+ Builder->Metadata->FrameCount = 0;
+
+ QuicConnResetIdleTimeout(Connection);
+}
diff --git a/src/core/packet_builder.h b/src/core/packet_builder.h
index 06081cb31d..e07e9acfe4 100644
--- a/src/core/packet_builder.h
+++ b/src/core/packet_builder.h
@@ -92,7 +92,7 @@ typedef struct QUIC_PACKET_BUILDER {
// The size of the encryption AEAD tag at the end of the current QUIC
// packet.
//
- uint8_t EncryptionOverhead;
+ uint16_t EncryptionOverhead;
//
// The encryption level for the current QUIC packet.
@@ -224,6 +224,13 @@ QuicPacketBuilderFinalize(
_In_ BOOLEAN FlushBatchedDatagrams
);
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+QuicPacketBuilderQMuxFinalize(
+ _Inout_ QUIC_PACKET_BUILDER* Builder,
+ _In_ BOOLEAN FlushBatchedDatagrams
+ );
+
//
// Returns TRUE if congestion control isn't currently blocking sends.
//
@@ -247,7 +254,7 @@ QUIC_INLINE
BOOLEAN
QuicPacketBuilderAddFrame(
_Inout_ QUIC_PACKET_BUILDER* Builder,
- _In_ uint8_t FrameType,
+ _In_ uint64_t FrameType,
_In_ BOOLEAN IsAckEliciting
)
{
diff --git a/src/core/partition.c b/src/core/partition.c
index cc4eba2fcb..9c09724fc8 100644
--- a/src/core/partition.c
+++ b/src/core/partition.c
@@ -34,6 +34,7 @@ QuicPartitionInitialize(
Partition->Index = Index;
Partition->Processor = Processor;
CxPlatPoolInitialize(FALSE, sizeof(QUIC_CONNECTION), QUIC_POOL_CONN, &Partition->ConnectionPool);
+ CxPlatPoolInitialize(FALSE, sizeof(QUIC_QMUX), QUIC_POOL_CONN_QMUX, &Partition->ConnectionQMuxPool);
CxPlatPoolInitialize(FALSE, sizeof(QUIC_TRANSPORT_PARAMETERS), QUIC_POOL_TP, &Partition->TransportParamPool);
CxPlatPoolInitialize(FALSE, sizeof(QUIC_PACKET_SPACE), QUIC_POOL_TP, &Partition->PacketSpacePool);
CxPlatPoolInitialize(FALSE, sizeof(QUIC_STREAM), QUIC_POOL_STREAM, &Partition->StreamPool);
@@ -59,6 +60,7 @@ QuicPartitionUninitialize(
CxPlatKeyFree(Partition->StatelessRetryKeys[i].Key);
}
CxPlatPoolUninitialize(&Partition->ConnectionPool);
+ CxPlatPoolUninitialize(&Partition->ConnectionQMuxPool);
CxPlatPoolUninitialize(&Partition->TransportParamPool);
CxPlatPoolUninitialize(&Partition->PacketSpacePool);
CxPlatPoolUninitialize(&Partition->StreamPool);
diff --git a/src/core/partition.h b/src/core/partition.h
index 978e86ae84..aeb239ab49 100644
--- a/src/core/partition.h
+++ b/src/core/partition.h
@@ -83,6 +83,7 @@ typedef struct QUIC_CACHEALIGN QUIC_PARTITION {
// Pools for allocations.
//
CXPLAT_POOL ConnectionPool; // QUIC_CONNECTION
+ CXPLAT_POOL ConnectionQMuxPool; // QUIC_CONNECTION_QMUX
CXPLAT_POOL TransportParamPool; // QUIC_TRANSPORT_PARAMETER
CXPLAT_POOL PacketSpacePool; // QUIC_PACKET_SPACE
CXPLAT_POOL StreamPool; // QUIC_STREAM
diff --git a/src/core/precomp.h b/src/core/precomp.h
index 127e7e7912..510dce9cb8 100644
--- a/src/core/precomp.h
+++ b/src/core/precomp.h
@@ -73,6 +73,7 @@
#include "stream_set.h"
#include "datagram.h"
#include "version_neg.h"
+#include "qmux.h"
#include "connection.h"
#include "packet_builder.h"
#include "listener.h"
diff --git a/src/core/qmux.c b/src/core/qmux.c
new file mode 100644
index 0000000000..3ba5847806
--- /dev/null
+++ b/src/core/qmux.c
@@ -0,0 +1,1723 @@
+/*++
+
+ Copyright (c) Microsoft Corporation.
+ Licensed under the MIT License.
+
+Abstract:
+
+ The QMux is a component of the connection that manages QMux operations.
+
+--*/
+
+#include "precomp.h"
+#ifdef QUIC_CLOG
+#include "qmux.c.clog.h"
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxInitialize(
+ _In_ QUIC_CONNECTION* Connection,
+ _Out_ QUIC_QMUX** NewQMux
+ )
+{
+ QUIC_QMUX* QMux = NULL;
+ QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
+
+ QMux = CxPlatPoolAlloc(&Connection->Partition->ConnectionQMuxPool);
+ if (QMux == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "connection QMux",
+ sizeof(QUIC_QMUX));
+ goto Error;
+ }
+
+ CxPlatZeroMemory(QMux, sizeof(QUIC_QMUX));
+
+ QMux->RecvBufferAllocLength = QX_TP_MAX_RECORD_SIZE_DEFAULT + 2; // Add 2 bytes for length field.
+ QMux->RecvBuffer = CXPLAT_ALLOC_NONPAGED(QMux->RecvBufferAllocLength, QUIC_POOL_QMUX_RECV_BUFFER);
+ if (QMux->RecvBuffer == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "QMux receive buffer",
+ QMux->RecvBufferAllocLength);
+ goto Error;
+ }
+
+ QMux->Connection = Connection;
+ QMux->TcpReceiveQueueTail = &QMux->TcpReceiveQueue;
+ CxPlatDispatchLockInitialize(&QMux->TcpReceiveQueueLock);
+ CxPlatEventInitialize(&QMux->ConnectEvent, TRUE, FALSE);
+
+ QMux->PermitEarlyData = FALSE;
+ QMux->SentEarlyDataPackets = NULL;
+ QMux->SentEarlyDataPacketsTail = &QMux->SentEarlyDataPackets;
+
+ *NewQMux = QMux;
+ return Status;
+
+Error:
+ if (QMux != NULL && QMux->RecvBuffer != NULL) {
+ CXPLAT_FREE(QMux->RecvBuffer, QUIC_POOL_QMUX_RECV_BUFFER);
+ }
+ if (QMux != NULL) {
+ CxPlatPoolFree(QMux);
+ }
+ return Status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxUninitialize(
+ _In_ QUIC_QMUX* QMux
+ )
+{
+ if (QMux->TcpReceiveQueue != NULL) {
+ CxPlatRecvDataReturn(QMux->TcpReceiveQueue);
+ QMux->TcpReceiveQueue = NULL;
+ }
+
+ if (QMux->SentEarlyDataPackets != NULL) {
+ QUIC_SENT_PACKET_METADATA* SentPacket = QMux->SentEarlyDataPackets;
+ while (SentPacket != NULL) {
+ QUIC_SENT_PACKET_METADATA* Next = SentPacket->Next;
+ QuicSentPacketPoolReturnPacketMetadata(SentPacket, QMux->Connection);
+ SentPacket = Next;
+ }
+ QMux->SentEarlyDataPackets = NULL;
+ QMux->SentEarlyDataPacketsTail = &QMux->SentEarlyDataPackets;
+ }
+
+ if (QMux->RecvBuffer != NULL) {
+ CXPLAT_FREE(QMux->RecvBuffer, QUIC_POOL_QMUX_RECV_BUFFER);
+ QMux->RecvBuffer = NULL;
+ }
+
+ if (QMux->EarlyDataBuffer != NULL) {
+ CXPLAT_FREE(QMux->EarlyDataBuffer, QUIC_POOL_QMUX_EARLY_DATA_BUFFER);
+ QMux->EarlyDataBuffer = NULL;
+ }
+
+ if (QMux->TlsState.EarlyDataBuffer != NULL) {
+ CXPLAT_FREE(QMux->TlsState.EarlyDataBuffer, QUIC_POOL_TLS_EARLY_DATA_BUFFER);
+ QMux->TlsState.EarlyDataBuffer = NULL;
+ }
+
+ CxPlatDispatchLockUninitialize(&QMux->TcpReceiveQueueLock);
+ CxPlatEventUninitialize(QMux->ConnectEvent);
+ CxPlatPoolFree(QMux);
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxInitializeTls(
+ _Inout_ QUIC_QMUX* QMux,
+ _In_ CXPLAT_SEC_CONFIG* SecConfig
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ QUIC_STATUS Status;
+ CXPLAT_TLS_CONFIG TlsConfig = { 0 };
+ BOOLEAN IsServer = QuicConnIsServer(Connection);
+
+ CXPLAT_DBG_ASSERT(SecConfig != NULL);
+ CXPLAT_DBG_ASSERT(Connection->Configuration != NULL);
+
+ TlsConfig.IsQMux = TRUE;
+ TlsConfig.IsServer = IsServer;
+ TlsConfig.AlpnBuffer = Connection->Configuration->AlpnList;
+ TlsConfig.AlpnBufferLength = Connection->Configuration->AlpnListLength;
+
+ TlsConfig.SecConfig = SecConfig;
+ TlsConfig.Connection = Connection;
+ TlsConfig.ResumptionTicketBuffer = QMux->ResumptionTicket;
+ TlsConfig.ResumptionTicketLength = QMux->ResumptionTicketLength;
+ if (QuicConnIsClient(Connection)) {
+ TlsConfig.ServerName = Connection->RemoteServerName;
+ }
+ TlsConfig.TlsSecrets = Connection->TlsSecrets;
+
+ if (QMux->TLS != NULL) {
+ CxPlatTlsUninitialize(QMux->TLS);
+ QMux->TLS = NULL;
+ }
+
+ Status = CxPlatTlsInitialize(&TlsConfig, &QMux->TlsState, &QMux->TLS);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "CxPlatTlsInitialize");
+ goto Error;
+ }
+
+ QMux->ResumptionTicket = NULL; // Owned by TLS now.
+ QMux->ResumptionTicketLength = 0;
+ QMux->ReadEarlyData = IsServer;
+ QMux->TlsState.ReadEarlyData = IsServer;
+ QMux->TlsState.EarlyDataBufferAllocLength = IsServer ? 4096 : 0;
+ if (QMux->TlsState.EarlyDataBufferAllocLength > 0) {
+ QMux->TlsState.EarlyDataBuffer =
+ CXPLAT_ALLOC_NONPAGED(
+ QMux->TlsState.EarlyDataBufferAllocLength,
+ QUIC_POOL_TLS_EARLY_DATA_BUFFER);
+ if (QMux->TlsState.EarlyDataBuffer == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS early data buffer",
+ QMux->TlsState.EarlyDataBufferAllocLength);
+ goto Error;
+ }
+ }
+
+ if (QuicConnIsClient(Connection)) {
+ if (QMux->PermitEarlyData) {
+ // If early data is permitted, we can start sending
+ // the transport parameters immediately.
+ QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS);
+ // Also flush the send path to ensure the transport parameters or any other data
+ // are sent in the first flight.
+ QuicSendFlush(&Connection->Send);
+ }
+ if (QMux->EarlyDataBufferLength > 0) {
+ // If we buffered any early data before TLS was initialized, send it now.
+ size_t EarlyDataBufferOffset = 0;
+ while (EarlyDataBufferOffset < QMux->EarlyDataBufferLength) {
+ size_t EarlyDataBufferConsumedLength =
+ QMux->EarlyDataBufferLength - EarlyDataBufferOffset;
+ if (!CxPlatTlsWriteEarlyData(
+ QMux->TLS,
+ QMux->EarlyDataBuffer + EarlyDataBufferOffset,
+ &EarlyDataBufferConsumedLength)) {
+ Status = QUIC_STATUS_HANDSHAKE_FAILURE;
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)Status,
+ NULL);
+ goto Error;
+ }
+ EarlyDataBufferOffset += EarlyDataBufferConsumedLength;
+ }
+ QMux->EarlyDataBufferLength = 0;
+ }
+ QMux->PermitEarlyData = FALSE;
+ uint32_t BufferLength = 0;
+ Status = QuicQMuxProcessHandshake(QMux, CXPLAT_TLS_CRYPTO_DATA, NULL, &BufferLength);
+ }
+
+Error:
+
+ return Status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxProcessHandshake(
+ _In_ QUIC_QMUX* QMux,
+ _In_ CXPLAT_TLS_DATA_TYPE DataType,
+ _In_reads_bytes_(*BufferLength)
+ const uint8_t* Buffer,
+ _Inout_ uint32_t* BufferLength
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
+ CXPLAT_SEND_DATA* SendData = NULL;
+ uint32_t TotalSendLength = 0;
+ uint16_t OutputBufferLength = 0;
+
+ if (QMux->TLS == NULL) {
+ //
+ // The listener still hasn't given us the security config to initialize
+ // TLS with yet.
+ //
+ goto Exit;
+ }
+
+ CXPLAT_TLS_RECORD_OVERHEAD Overhead;
+ CxPlatTlsGetRecordOverhead(QMux->TLS, &Overhead);
+ OutputBufferLength = (uint16_t)Overhead.MaxHeader + 16384 + (uint16_t)Overhead.MaxTrailer;
+ CXPLAT_SEND_CONFIG SendConfig = { &QMux->Route, OutputBufferLength, CXPLAT_ECN_NON_ECT, 0, CXPLAT_DSCP_CS0 };
+
+ uint32_t BufferOffset = 0;
+ uint32_t BufferCapacity = *BufferLength;
+ uint32_t BufferConsumedLength = DataType == CXPLAT_TLS_CRYPTO_DATA ? *BufferLength / 2 : *BufferLength;
+ QUIC_BUFFER* SendBuffer = NULL;
+ QUIC_BUFFER* SendBuffers[3];
+ QUIC_BUFFER OutputBuffers[3], OldOutputBuffers[3];
+ uint32_t OutputBuffersCount = 0;
+ uint32_t i;
+
+ do {
+ if (SendData == NULL) {
+ SendData = CxPlatSendDataAlloc(QMux->Socket, &SendConfig);
+ if (SendData == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "packet send context",
+ 0);
+ goto Exit;
+ }
+ }
+
+ if (SendBuffer == NULL || (QMux->ResultFlags & CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL)) {
+ if (OutputBuffersCount == ARRAYSIZE(OutputBuffers)) {
+ // We've already filled all our output buffers,
+ // so flush what we have before allocating more.
+
+ // Update the send buffers to match the actual data length produced by TLS.
+ for (i = 0; i < OutputBuffersCount; ++i) {
+ SendBuffers[i]->Length =
+ (uint32_t)(OutputBuffers[i].Buffer - SendBuffers[i]->Buffer);
+ }
+ CxPlatSocketSend(QMux->Socket, &QMux->Route, SendData);
+ TotalSendLength = 0;
+ OutputBuffersCount = 0;
+ SendData = NULL;
+ continue;
+ }
+
+ SendBuffer = CxPlatSendDataAllocBuffer(SendData, OutputBufferLength);
+ if (SendBuffer == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "packet datagram",
+ OutputBufferLength);
+ goto Exit;
+ }
+
+ OutputBuffers[OutputBuffersCount] = *SendBuffer;
+ SendBuffers[OutputBuffersCount++] = SendBuffer;
+ }
+
+ CxPlatCopyMemory(OldOutputBuffers, OutputBuffers, sizeof(OutputBuffers));
+ QMux->ResultFlags &= ~(CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL | CXPLAT_TLS_RESULT_DATA);
+
+ QMux->ResultFlags |=
+ CxPlatTlsHandshake(
+ QMux->TLS,
+ DataType,
+ Buffer + BufferOffset,
+ &BufferConsumedLength,
+ OutputBuffers,
+ OutputBuffersCount,
+ &QMux->TlsState);
+ if (QMux->ResultFlags & CXPLAT_TLS_RESULT_ERROR) {
+ Status = QUIC_STATUS_HANDSHAKE_FAILURE;
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "CxPlatTlsHandshake");
+ if (DataType == CXPLAT_TLS_CRYPTO_DATA) {
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)Status,
+ NULL);
+ }
+ goto Exit;
+ }
+
+ BufferOffset += BufferConsumedLength;
+ BufferConsumedLength = BufferCapacity - BufferOffset;
+ *BufferLength = BufferOffset;
+
+ if ((QMux->ResultFlags & CXPLAT_TLS_RESULT_DATA)) {
+ // TLS has produced data to be sent. Update the output buffers.
+ for (i = 0; i < OutputBuffersCount; ++i) {
+ if (OutputBuffers[i].Length > 0) {
+ QuicTraceLogConnVerbose(
+ TlsHandshakeData,
+ Connection,
+ "TLS handshake data ready to send, length=%u",
+ OutputBuffers[i].Length);
+ }
+ TotalSendLength += OutputBuffers[i].Length;
+ OutputBuffers[i].Buffer += OutputBuffers[i].Length;
+ OutputBuffers[i].Length = OldOutputBuffers[i].Length - OutputBuffers[i].Length;
+ }
+ }
+ } while (BufferConsumedLength > 0 ||
+ (QMux->ResultFlags & CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL));
+
+ // Update the send buffers to match the actual data length produced by TLS.
+ for (i = 0; i < OutputBuffersCount; ++i) {
+ SendBuffers[i]->Length = (uint32_t)(OutputBuffers[i].Buffer - SendBuffers[i]->Buffer);
+ }
+
+Exit:
+ if (SendData != NULL) {
+ if (QUIC_SUCCEEDED(Status) && TotalSendLength > 0) {
+ CxPlatSocketSend(QMux->Socket, &QMux->Route, SendData);
+ QuicConnResetIdleTimeout(Connection);
+ } else {
+ CxPlatSendDataFree(SendData);
+ }
+ }
+ return Status;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxQueueRecvData(
+ _In_ QUIC_QMUX* QMux,
+ _In_ CXPLAT_RECV_DATA* RecvDataChain,
+ _In_ uint32_t RecvDataChainLength,
+ _In_ uint32_t RecvDataChainByteLength
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ CXPLAT_RECV_DATA** RecvDataChainTail = (CXPLAT_RECV_DATA**)&RecvDataChain->Next;
+ while (*RecvDataChainTail != NULL) {
+ RecvDataChainTail = (CXPLAT_RECV_DATA**)&((*RecvDataChainTail)->Next);
+ }
+
+ QuicTraceLogConnVerbose(
+ QueueDatagrams,
+ Connection,
+ "Queuing %u TCP data",
+ RecvDataChainLength);
+
+ BOOLEAN QueueOperation;
+ CxPlatDispatchLockAcquire(&QMux->TcpReceiveQueueLock);
+ *QMux->TcpReceiveQueueTail = RecvDataChain;
+ QMux->TcpReceiveQueueTail = RecvDataChainTail;
+ RecvDataChain = NULL;
+ QueueOperation = (QMux->TcpReceiveQueueCount == 0);
+ QMux->TcpReceiveQueueCount += RecvDataChainLength;
+ QMux->TcpReceiveQueueByteCount += RecvDataChainByteLength;
+ CxPlatDispatchLockRelease(&QMux->TcpReceiveQueueLock);
+
+ if (QueueOperation) {
+ QUIC_OPERATION* ConnOper =
+ QuicConnAllocOperation(Connection, QUIC_OPER_TYPE_FLUSH_TCP_RECV);
+ if (ConnOper != NULL) {
+ QuicConnQueueOper(Connection, ConnOper);
+ } else {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "Flush Recv TCP operation",
+ 0);
+ }
+ }
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+QuicQMuxRecvFrames(
+ _In_ QUIC_QMUX* QMux,
+ _In_reads_bytes_(PayloadLength)
+ const uint8_t* Payload,
+ _In_ uint16_t PayloadLength
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ BOOLEAN UpdatedFlowControl = FALSE;
+ BOOLEAN Closed = Connection->State.ClosedLocally || Connection->State.ClosedRemotely;
+ const BOOLEAN ClosingState = Connection->State.ClosedLocally && !Connection->State.ClosedRemotely;
+ uint64_t RecvTime = CxPlatTimeUs64();
+
+ //
+ // In closing state, respond to any packet with a new close frame (rate-limited).
+ // Note this excludes the draining state (i.e., ClosedRemotely == TRUE)
+ // in which we should be silent.
+ //
+ if (ClosingState && !Connection->State.ShutdownComplete) {
+ if (RecvTime - Connection->LastCloseResponseTimeUs >= QUIC_CLOSING_RESPONSE_MIN_INTERVAL) {
+ QuicSendSetSendFlag(
+ &Connection->Send,
+ Connection->State.AppClosed ?
+ QUIC_CONN_SEND_FLAG_APPLICATION_CLOSE :
+ QUIC_CONN_SEND_FLAG_CONNECTION_CLOSE);
+ }
+ }
+
+ if (QuicConnIsClient(Connection) &&
+ !Connection->State.GotFirstServerResponse) {
+ Connection->State.GotFirstServerResponse = TRUE;
+ }
+
+ QuicFrameLogAll(
+ Connection,
+ TRUE,
+ 0,
+ PayloadLength,
+ Payload,
+ 0);
+
+ uint16_t Offset = 0;
+ while (Offset < PayloadLength) {
+
+ //
+ // Read the frame type.
+ //
+ QUIC_VAR_INT FrameType INIT_NO_SAL(0);
+ if (!QuicVarIntDecode(PayloadLength, Payload, &Offset, &FrameType)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Frame type decode failure");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (!QUIC_FRAME_IS_KNOWN(FrameType)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Unknown frame type");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ //
+ // Validate allowable frames based on the packet type.
+ //
+ switch (FrameType) {
+ //
+ // The following frames are allowed for QMux:
+ //
+ case QUIC_FRAME_PADDING:
+ case QUIC_FRAME_RESET_STREAM:
+ case QUIC_FRAME_RELIABLE_RESET_STREAM:
+ case QUIC_FRAME_STOP_SENDING:
+ case QUIC_FRAME_STREAM:
+ case QUIC_FRAME_STREAM_1:
+ case QUIC_FRAME_STREAM_2:
+ case QUIC_FRAME_STREAM_3:
+ case QUIC_FRAME_STREAM_4:
+ case QUIC_FRAME_STREAM_5:
+ case QUIC_FRAME_STREAM_6:
+ case QUIC_FRAME_STREAM_7:
+ case QUIC_FRAME_MAX_DATA:
+ case QUIC_FRAME_MAX_STREAM_DATA:
+ case QUIC_FRAME_MAX_STREAMS:
+ case QUIC_FRAME_DATA_BLOCKED:
+ case QUIC_FRAME_STREAM_DATA_BLOCKED:
+ case QUIC_FRAME_STREAMS_BLOCKED:
+ case QUIC_FRAME_STREAMS_BLOCKED_1:
+ case QUIC_FRAME_CONNECTION_CLOSE:
+ case QUIC_FRAME_CONNECTION_CLOSE_1:
+ case QUIC_FRAME_DATAGRAM:
+ case QUIC_FRAME_DATAGRAM_1:
+ case QX_FRAME_TRANSPORT_PARAMETERS:
+ case QX_FRAME_PING:
+ case QX_FRAME_PING_1:
+ break;
+ //
+ // All other frame types are disallowed.
+ //
+ default:
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ (uint32_t)FrameType,
+ "Disallowed frame type");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ //
+ // Process the frame based on the frame type.
+ //
+ switch (FrameType) {
+
+ case QX_FRAME_TRANSPORT_PARAMETERS: {
+ QX_TRANSPORT_PARAMETERS_EX Frame;
+ if (!QxTransportParametersFrameDecode(PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding QX Transport Parameters frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (Closed) {
+ break; // Ignore frame if we are closed.
+ }
+
+ CXPLAT_DBG_ASSERT(Frame.Length <= UINT16_MAX);
+ if (!QuicCryptoTlsDecodeTransportParameters(
+ Connection,
+ !QuicConnIsServer(Connection),
+ Frame.TP,
+ (uint16_t)Frame.Length,
+ &Connection->PeerTransportParams)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Processing transport parameters frame");
+ return FALSE;
+ }
+
+ if (QUIC_FAILED(QuicConnProcessPeerTransportParameters(Connection, FALSE))) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Processing peer transport parameters");
+ return FALSE;
+ }
+ Connection->State.PeerTPReceived = TRUE;
+ if (QMux->TlsState.HandshakeComplete) {
+ Connection->State.Connected = TRUE;
+ QuicPerfCounterIncrement(Connection->Partition, QUIC_PERF_COUNTER_CONN_CONNECTED);
+
+ QUIC_CONNECTION_EVENT Event = { 0 };
+ Event.Type = QUIC_CONNECTION_EVENT_CONNECTED;
+ Event.CONNECTED.NegotiatedAlpnLength = QuicConnGetQMux(Connection)->TlsState.NegotiatedAlpn[0];
+ Event.CONNECTED.NegotiatedAlpn = QuicConnGetQMux(Connection)->TlsState.NegotiatedAlpn + 1;
+
+ QuicTraceLogConnVerbose(
+ IndicateConnected,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)",
+ Event.CONNECTED.SessionResumed);
+ (void)QuicConnIndicateEvent(Connection, &Event);
+ }
+ break;
+ }
+
+ case QX_FRAME_PING:
+ case QX_FRAME_PING_1: {
+ QX_PING_EX Frame;
+ if (!QxPingFrameDecode(FrameType, PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding QX PING frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (Closed) {
+ break; // Ignore frame if we are closed.
+ }
+
+ if (!Frame.IsResponse) {
+ if (QMux->RecvPing &&
+ QMux->RecvPingSequenceNumber >= Frame.SequenceNumber) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Sequence number violation in QX PING frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION);
+ return FALSE;
+ }
+ QMux->RecvPing = TRUE;
+ QMux->RecvPingSequenceNumber = Frame.SequenceNumber;
+ QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_QX_PING_RESPONSE);
+ } else {
+ if (QMux->NextPingSequenceNumber == 0) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Unexpected QX PING response frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION);
+ return FALSE;
+ } else if (QMux->NextPingSequenceNumber -1 < Frame.SequenceNumber) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Sequence number violation in QX PING response frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION);
+ return FALSE;
+ }
+ }
+ }
+
+ case QUIC_FRAME_PADDING: {
+ while (Offset < PayloadLength &&
+ Payload[Offset] == QUIC_FRAME_PADDING) {
+ Offset += sizeof(uint8_t);
+ }
+ break;
+ }
+
+ case QUIC_FRAME_RESET_STREAM:
+ case QUIC_FRAME_STOP_SENDING:
+ case QUIC_FRAME_STREAM:
+ case QUIC_FRAME_STREAM_1:
+ case QUIC_FRAME_STREAM_2:
+ case QUIC_FRAME_STREAM_3:
+ case QUIC_FRAME_STREAM_4:
+ case QUIC_FRAME_STREAM_5:
+ case QUIC_FRAME_STREAM_6:
+ case QUIC_FRAME_STREAM_7:
+ case QUIC_FRAME_MAX_STREAM_DATA:
+ case QUIC_FRAME_STREAM_DATA_BLOCKED:
+ case QUIC_FRAME_RELIABLE_RESET_STREAM: {
+ if (Closed) {
+ if (!QuicStreamFrameSkip(
+ FrameType, PayloadLength, Payload, &Offset)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Skipping closed stream frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+ break; // Ignore frame if we are closed.
+ }
+
+ uint64_t StreamId;
+ if (!QuicStreamFramePeekID(
+ PayloadLength, Payload, Offset, &StreamId)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding stream ID from frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ BOOLEAN PeerOriginatedStream =
+ QuicConnIsServer(Connection) ?
+ STREAM_ID_IS_CLIENT(StreamId) :
+ STREAM_ID_IS_SERVER(StreamId);
+
+ if (STREAM_ID_IS_UNI_DIR(StreamId)) {
+ BOOLEAN IsReceiverSideFrame =
+ FrameType == QUIC_FRAME_MAX_STREAM_DATA ||
+ FrameType == QUIC_FRAME_STOP_SENDING;
+ if (PeerOriginatedStream == IsReceiverSideFrame) {
+ //
+ // For locally initiated unidirectional streams, the peer
+ // should only send receiver frame types, and vice versa
+ // for peer initiated unidirectional streams.
+ //
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Invalid frame on unidirectional stream");
+ QuicConnTransportError(Connection, QUIC_ERROR_STREAM_STATE_ERROR);
+ break;
+ }
+ }
+
+ BOOLEAN FatalError;
+ QUIC_STREAM* Stream =
+ QuicStreamSetGetStreamForPeer(
+ &Connection->Streams,
+ StreamId,
+ FALSE,
+ PeerOriginatedStream,
+ &FatalError);
+
+ if (Stream) {
+ QUIC_STATUS Status =
+ QuicStreamRecv(
+ Stream,
+ NULL,
+ FrameType,
+ PayloadLength,
+ Payload,
+ &Offset,
+ &UpdatedFlowControl);
+ QuicStreamRelease(Stream, QUIC_STREAM_REF_LOOKUP);
+ if (Status == QUIC_STATUS_OUT_OF_MEMORY) {
+ // QuicPacketLogDrop(Connection, Packet, "Stream frame process OOM");
+ return FALSE;
+ }
+
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Invalid stream frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ } else if (FatalError) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Getting stream from ID");
+ return FALSE;
+ } else {
+ //
+ // Didn't find a matching Stream. Skip the frame as the Stream
+ // might have been closed already.
+ //
+ QuicTraceLogConnWarning(
+ IgnoreFrameAfterClose,
+ Connection,
+ "Ignoring frame (%hhu) for already closed stream id = %llu",
+ (uint8_t)FrameType, // This cast is safe because of the switch cases above.
+ StreamId);
+ if (!QuicStreamFrameSkip(
+ FrameType, PayloadLength, Payload, &Offset)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Skipping ignored stream frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+ }
+
+ break;
+ }
+
+ case QUIC_FRAME_MAX_DATA: {
+ QUIC_MAX_DATA_EX Frame;
+ if (!QuicMaxDataFrameDecode(PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding MAX_DATA frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (Closed) {
+ break; // Ignore frame if we are closed.
+ }
+
+ if (Connection->Send.PeerMaxData < Frame.MaximumData) {
+ Connection->Send.PeerMaxData = Frame.MaximumData;
+ //
+ // The peer has given us more allowance. Send packets from
+ // any previously blocked streams.
+ //
+ UpdatedFlowControl = TRUE;
+ QuicConnRemoveOutFlowBlockedReason(
+ Connection, QUIC_FLOW_BLOCKED_CONN_FLOW_CONTROL);
+ QuicSendQueueFlush(
+ &Connection->Send, REASON_CONNECTION_FLOW_CONTROL);
+ }
+
+ break;
+ }
+
+ case QUIC_FRAME_MAX_STREAMS:
+ case QUIC_FRAME_MAX_STREAMS_1: {
+ QUIC_MAX_STREAMS_EX Frame;
+ if (!QuicMaxStreamsFrameDecode(FrameType, PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding MAX_STREAMS frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (Closed) {
+ break; // Ignore frame if we are closed.
+ }
+
+ if (Frame.MaximumStreams > QUIC_TP_MAX_STREAMS_MAX) {
+ QuicConnTransportError(Connection, QUIC_ERROR_STREAM_LIMIT_ERROR);
+ break;
+ }
+
+ QuicStreamSetUpdateMaxStreams(
+ &Connection->Streams,
+ Frame.BidirectionalStreams,
+ Frame.MaximumStreams);
+
+ break;
+ }
+
+ case QUIC_FRAME_DATA_BLOCKED: {
+ QUIC_DATA_BLOCKED_EX Frame;
+ if (!QuicDataBlockedFrameDecode(PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding BLOCKED frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (Closed) {
+ break; // Ignore frame if we are closed.
+ }
+
+ //
+ // TODO - Should we do anything else with this?
+ //
+ QuicTraceLogConnVerbose(
+ PeerConnFCBlocked,
+ Connection,
+ "Peer Connection FC blocked (%llu)",
+ Frame.DataLimit);
+ QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_MAX_DATA);
+
+ break;
+ }
+
+ case QUIC_FRAME_STREAMS_BLOCKED:
+ case QUIC_FRAME_STREAMS_BLOCKED_1: {
+ QUIC_STREAMS_BLOCKED_EX Frame;
+ if (!QuicStreamsBlockedFrameDecode(FrameType, PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding STREAMS_BLOCKED frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ if (Closed) {
+ break; // Ignore frame if we are closed.
+ }
+
+ QuicTraceLogConnVerbose(
+ PeerStreamFCBlocked,
+ Connection,
+ "Peer Streams[%hu] FC blocked (%llu)",
+ Frame.BidirectionalStreams,
+ Frame.StreamLimit);
+
+ uint8_t Type =
+ (QuicConnIsServer(Connection) ? // Peer's role, so flip
+ STREAM_ID_FLAG_IS_CLIENT : STREAM_ID_FLAG_IS_SERVER)
+ |
+ (Frame.BidirectionalStreams ?
+ STREAM_ID_FLAG_IS_BI_DIR : STREAM_ID_FLAG_IS_UNI_DIR);
+
+ const QUIC_STREAM_TYPE_INFO* Info = &Connection->Streams.Types[Type];
+
+ if (Info->MaxTotalStreamCount > Frame.StreamLimit) {
+ break;
+ }
+
+ QUIC_CONNECTION_EVENT Event;
+ Event.Type = QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS;
+ Event.PEER_NEEDS_STREAMS.Bidirectional = Frame.BidirectionalStreams;
+ QuicTraceLogConnVerbose(
+ IndicatePeerNeedStreamsV2,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS type: %s",
+ Frame.BidirectionalStreams ? "Bidi" : "Unidi"
+ );
+ (void)QuicConnIndicateEvent(Connection, &Event);
+
+ break;
+ }
+
+ case QUIC_FRAME_CONNECTION_CLOSE:
+ case QUIC_FRAME_CONNECTION_CLOSE_1: {
+ QUIC_CONNECTION_CLOSE_EX Frame;
+ if (!QuicConnCloseFrameDecode(FrameType, PayloadLength, Payload, &Offset, &Frame)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding CONNECTION_CLOSE frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+
+ uint32_t Flags = QUIC_CLOSE_REMOTE | QUIC_CLOSE_SEND_NOTIFICATION;
+ if (Frame.ApplicationClosed) {
+ Flags |= QUIC_CLOSE_APPLICATION;
+ }
+
+ if (!Frame.ApplicationClosed && Frame.ErrorCode == QUIC_ERROR_APPLICATION_ERROR) {
+ //
+ // The APPLICATION_ERROR transport error should be sent only
+ // when closing the connection before the handshake is
+ // confirmed. In such case, we can also expect peer to send the
+ // application CONNECTION_CLOSE frame in a 1-RTT packet
+ // (presumably also in the same UDP datagram).
+ //
+ // We want to prioritize reporting the application-layer error
+ // code to the application, so we postpone the call to
+ // QuicConnTryClose and check again after processing incoming
+ // datagrams in case it does not arrive.
+ //
+ QuicTraceEvent(
+ ConnDelayCloseApplicationError,
+ "[conn][%p] Received APPLICATION_ERROR error, delaying close in expectation of a 1-RTT CONNECTION_CLOSE frame.",
+ Connection);
+ Connection->State.DelayedApplicationError = TRUE;
+ } else {
+ QuicConnTryClose(
+ Connection,
+ Flags,
+ Frame.ErrorCode,
+ Frame.ReasonPhrase,
+ (uint16_t)Frame.ReasonPhraseLength);
+ }
+
+
+ if (Connection->State.HandleClosed) {
+ //
+ // If we are now closed, we should exit immediately. No need to
+ // parse anything else.
+ //
+ goto Done;
+ }
+ break;
+ }
+
+ case QUIC_FRAME_DATAGRAM:
+ case QUIC_FRAME_DATAGRAM_1: {
+ if (!Connection->Settings.DatagramReceiveEnabled) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Received DATAGRAM frame when not negotiated");
+ QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION);
+ return FALSE;
+ }
+ if (!QuicDatagramProcessFrame(
+ &Connection->Datagram,
+ NULL,
+ FrameType,
+ PayloadLength,
+ Payload,
+ &Offset)) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decoding DATAGRAM frame");
+ QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
+ return FALSE;
+ }
+ break;
+ }
+ default:
+ //
+ // No default case necessary, as we have already validated the frame
+ // type initially, but included for clang the compiler.
+ //
+ break;
+ }
+ }
+
+Done:
+
+ if (UpdatedFlowControl) {
+ QuicConnLogOutFlowStats(Connection);
+ }
+
+ return TRUE;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxOnPacketAcknowledged(
+ _In_ QUIC_QMUX* QMux,
+ _In_ QUIC_SENT_PACKET_METADATA* Packet
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+
+ for (uint8_t i = 0; i < Packet->FrameCount; ++i) {
+ switch (Packet->Frames[i].Type) {
+ case QUIC_FRAME_RESET_STREAM:
+ QuicStreamOnResetAck(Packet->Frames[i].RESET_STREAM.Stream);
+ break;
+ case QUIC_FRAME_RELIABLE_RESET_STREAM:
+ QuicStreamOnResetReliableAck(
+ Packet->Frames[i].RELIABLE_RESET_STREAM.Stream);
+ break;
+ case QUIC_FRAME_STREAM:
+ case QUIC_FRAME_STREAM_1:
+ case QUIC_FRAME_STREAM_2:
+ case QUIC_FRAME_STREAM_3:
+ case QUIC_FRAME_STREAM_4:
+ case QUIC_FRAME_STREAM_5:
+ case QUIC_FRAME_STREAM_6:
+ case QUIC_FRAME_STREAM_7: {
+ QUIC_SEND_PACKET_FLAGS DummyFlags = { 0 };
+ QuicStreamOnAck(
+ Packet->Frames[i].STREAM.Stream,
+ DummyFlags,
+ &Packet->Frames[i]);
+ break;
+ }
+ case QUIC_FRAME_DATAGRAM:
+ case QUIC_FRAME_DATAGRAM_1:
+ QuicDatagramIndicateSendStateChange(
+ Connection,
+ &Packet->Frames[i].DATAGRAM.ClientContext,
+ QUIC_DATAGRAM_SEND_ACKNOWLEDGED);
+ Packet->Frames[i].DATAGRAM.ClientContext = NULL;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxOnPacketsAcknowledged(
+ _In_ QUIC_QMUX* QMux
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+
+ QUIC_SENT_PACKET_METADATA* SentPacket = QMux->SentEarlyDataPackets;
+ while (SentPacket != NULL) {
+ QUIC_SENT_PACKET_METADATA* Next = SentPacket->Next;
+ QuicQMuxOnPacketAcknowledged(QMux, SentPacket);
+ QuicSentPacketPoolReturnPacketMetadata(SentPacket, Connection);
+ SentPacket = Next;
+ }
+ QMux->SentEarlyDataPackets = NULL;
+ QMux->SentEarlyDataPacketsTail = &QMux->SentEarlyDataPackets;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxOnPacketLost(
+ _In_ QUIC_QMUX* QMux,
+ _In_ QUIC_SENT_PACKET_METADATA* Packet
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+
+ for (uint8_t i = 0; i < Packet->FrameCount; ++i) {
+ switch (Packet->Frames[i].Type) {
+ case QUIC_FRAME_RESET_STREAM:
+ QuicSendSetStreamSendFlag(
+ &Connection->Send,
+ Packet->Frames[i].RESET_STREAM.Stream,
+ QUIC_STREAM_SEND_FLAG_SEND_ABORT,
+ FALSE);
+ break;
+ case QUIC_FRAME_RELIABLE_RESET_STREAM:
+ QuicSendSetStreamSendFlag(
+ &Connection->Send,
+ Packet->Frames[i].RELIABLE_RESET_STREAM.Stream,
+ QUIC_STREAM_SEND_FLAG_RELIABLE_ABORT,
+ FALSE);
+ break;
+ case QUIC_FRAME_STOP_SENDING:
+ QuicSendSetStreamSendFlag(
+ &Connection->Send,
+ Packet->Frames[i].STOP_SENDING.Stream,
+ QUIC_STREAM_SEND_FLAG_RECV_ABORT,
+ FALSE);
+ break;
+
+ case QUIC_FRAME_STREAM:
+ case QUIC_FRAME_STREAM_1:
+ case QUIC_FRAME_STREAM_2:
+ case QUIC_FRAME_STREAM_3:
+ case QUIC_FRAME_STREAM_4:
+ case QUIC_FRAME_STREAM_5:
+ case QUIC_FRAME_STREAM_6:
+ case QUIC_FRAME_STREAM_7:
+ QuicStreamOnLoss(
+ Packet->Frames[i].STREAM.Stream,
+ &Packet->Frames[i]);
+ break;
+ case QUIC_FRAME_MAX_DATA:
+ QuicSendSetSendFlag(
+ &Connection->Send,
+ QUIC_CONN_SEND_FLAG_MAX_DATA);
+ break;
+ case QUIC_FRAME_MAX_STREAM_DATA:
+ QuicSendSetStreamSendFlag(
+ &Connection->Send,
+ Packet->Frames[i].MAX_STREAM_DATA.Stream,
+ QUIC_STREAM_SEND_FLAG_MAX_DATA,
+ FALSE);
+ break;
+ case QUIC_FRAME_MAX_STREAMS:
+ QuicSendSetSendFlag(
+ &Connection->Send,
+ QUIC_CONN_SEND_FLAG_MAX_STREAMS_BIDI);
+ break;
+ case QUIC_FRAME_MAX_STREAMS_1:
+ QuicSendSetSendFlag(
+ &Connection->Send,
+ QUIC_CONN_SEND_FLAG_MAX_STREAMS_UNI);
+ break;
+ case QUIC_FRAME_STREAM_DATA_BLOCKED:
+ QuicSendSetStreamSendFlag(
+ &Connection->Send,
+ Packet->Frames[i].STREAM_DATA_BLOCKED.Stream,
+ QUIC_STREAM_SEND_FLAG_DATA_BLOCKED,
+ FALSE);
+ break;
+ case QUIC_FRAME_DATAGRAM:
+ case QUIC_FRAME_DATAGRAM_1:
+ QuicDatagramIndicateSendStateChange(
+ Connection,
+ &Packet->Frames[i].DATAGRAM.ClientContext,
+ QUIC_DATAGRAM_SEND_LOST_SUSPECT);
+ Packet->Frames[i].DATAGRAM.ClientContext = NULL;
+ break;
+ case QX_FRAME_TRANSPORT_PARAMETERS:
+ QuicSendSetSendFlag(
+ &Connection->Send,
+ QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxOnPacketsLost(
+ _In_ QUIC_QMUX* QMux
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+
+ QUIC_SENT_PACKET_METADATA* SentPacket = QMux->SentEarlyDataPackets;
+ while (SentPacket != NULL) {
+ QUIC_SENT_PACKET_METADATA* Next = SentPacket->Next;
+ QuicQMuxOnPacketLost(QMux, SentPacket);
+ QuicSentPacketPoolReturnPacketMetadata(SentPacket, Connection);
+ SentPacket = Next;
+ }
+ QMux->SentEarlyDataPackets = NULL;
+ QMux->SentEarlyDataPacketsTail = &QMux->SentEarlyDataPackets;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxRecvData(
+ _In_ QUIC_QMUX* QMux,
+ _In_ CXPLAT_RECV_DATA* RecvDataChain,
+ _In_ uint32_t RecvDataChainCount,
+ _In_ uint32_t RecvDataChainByteCount
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
+
+ QuicTraceEvent(
+ ConnRecvTcpData,
+ "[conn][%p] Recv %u TCP data, %u bytes",
+ Connection,
+ RecvDataChainCount,
+ RecvDataChainByteCount);
+
+ CXPLAT_FRE_ASSERTMSG(
+ QMux->TLS != NULL,
+ "TLS state should have been initialized before receiving TCP data");
+
+ CXPLAT_RECV_DATA* ReleaseChain = RecvDataChain;
+
+ CXPLAT_RECV_DATA* RecvData;
+ while ((RecvData = RecvDataChain) != NULL) {
+ RecvDataChain = (CXPLAT_RECV_DATA*)RecvData->Next;
+
+ uint32_t RecvDataLength = RecvData->BufferLength;
+ uint32_t RecvDataConsumedLength = RecvDataLength;
+ uint32_t RecvDataOffset = 0;
+ if (!QMux->TlsState.HandshakeComplete) {
+ // If the handshake is not complete, the received data must be handshake data.
+ // Process it through the TLS stack to advance the handshake.
+ Status =
+ QuicQMuxProcessHandshake(
+ QMux,
+ CXPLAT_TLS_CRYPTO_DATA,
+ RecvData->Buffer,
+ &RecvDataConsumedLength);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "Processing handshake data");
+ goto Error;
+ }
+ RecvDataOffset += RecvDataConsumedLength;
+ RecvDataConsumedLength = RecvDataLength - RecvDataOffset;
+ if (QMux->ResultFlags & CXPLAT_TLS_RESULT_HANDSHAKE_COMPLETE) {
+ QuicTraceEvent(
+ ConnHandshakeComplete,
+ "[conn][%p] Handshake complete",
+ Connection);
+
+ if (Connection->State.PeerTPReceived) {
+ // If we have already received the peer's transport parameters,
+ // then we are fully connected now.
+ Connection->State.Connected = TRUE;
+ QuicPerfCounterIncrement(Connection->Partition, QUIC_PERF_COUNTER_CONN_CONNECTED);
+
+ QUIC_CONNECTION_EVENT Event = { 0 };
+ Event.Type = QUIC_CONNECTION_EVENT_CONNECTED;
+ Event.CONNECTED.NegotiatedAlpnLength = QuicConnGetQMux(Connection)->TlsState.NegotiatedAlpn[0];
+ Event.CONNECTED.NegotiatedAlpn = QuicConnGetQMux(Connection)->TlsState.NegotiatedAlpn + 1;
+
+ QuicTraceLogConnVerbose(
+ IndicateConnected,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)",
+ Event.CONNECTED.SessionResumed);
+ (void)QuicConnIndicateEvent(Connection, &Event);
+ }
+
+ if (!Connection->State.LocalTPSent) {
+ // If we haven't sent our transport parameters yet, we should send them.
+ QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS);
+ }
+ }
+
+ if (QMux->TlsState.EarlyDataBufferLength > 0) {
+ // Process any buffered early data now.
+ CXPLAT_DBG_ASSERT(QuicConnIsServer(Connection));
+ QUIC_VAR_INT RecordLength;
+ uint16_t RecordOffset;
+ uint32_t EarlyDataBufferOffset = 0;
+ do {
+ RecordLength = 0;
+ RecordOffset = 0;
+ if (!QuicVarIntDecode(
+ (uint16_t)(QMux->TlsState.EarlyDataBufferLength - EarlyDataBufferOffset),
+ QMux->TlsState.EarlyDataBuffer + EarlyDataBufferOffset,
+ &RecordOffset,
+ &RecordLength)) {
+ break;
+ }
+ if (QMux->TlsState.EarlyDataBufferLength - EarlyDataBufferOffset <
+ RecordOffset + RecordLength) {
+ break;
+ }
+ QuicTraceEvent(
+ ConnRecvPacket,
+ "[conn][%p][RX] %hu bytes",
+ Connection,
+ (uint16_t)RecordLength);
+
+ QuicQMuxRecvFrames(
+ QMux,
+ QMux->TlsState.EarlyDataBuffer + EarlyDataBufferOffset + RecordOffset,
+ (uint16_t)RecordLength);
+ EarlyDataBufferOffset += RecordOffset + (uint16_t)RecordLength;
+ } while (EarlyDataBufferOffset < QMux->TlsState.EarlyDataBufferLength);
+
+ if (EarlyDataBufferOffset > 0 && EarlyDataBufferOffset < QMux->TlsState.EarlyDataBufferLength) {
+ // Move any remaining data to the recv buffer for the next receive.
+ CXPLAT_DBG_ASSERT(QMux->RecvBufferAllocLength >= QMux->TlsState.EarlyDataBufferLength - EarlyDataBufferOffset);
+ CxPlatMoveMemory(
+ QMux->RecvBuffer,
+ QMux->TlsState.EarlyDataBuffer + EarlyDataBufferOffset,
+ QMux->TlsState.EarlyDataBufferLength - EarlyDataBufferOffset);
+ QMux->RecvBufferLength = QMux->TlsState.EarlyDataBufferLength - EarlyDataBufferOffset;
+ }
+ QMux->TlsState.EarlyDataBufferLength = 0;
+ }
+ }
+
+ if (QMux->ResultFlags & CXPLAT_TLS_RESULT_EARLY_DATA_ACCEPT) {
+ QuicTraceEvent(
+ ConnEarlyDataStatus,
+ "[conn][%p] Early data %s",
+ Connection,
+ "accepted");
+ QuicQMuxOnPacketsAcknowledged(QMux);
+ } else if (QMux->ResultFlags & CXPLAT_TLS_RESULT_EARLY_DATA_REJECT) {
+ QuicTraceEvent(
+ ConnEarlyDataStatus,
+ "[conn][%p] Early data %s",
+ Connection,
+ "rejected");
+ QuicQMuxOnPacketsLost(QMux);
+ }
+
+ if (QMux->TlsState.HandshakeComplete) {
+ // If the handshake is complete, we should have application data
+ // that needs to be decrypted and processed.
+ uint32_t RecvBufferAppendedLength;
+ do {
+ RecvBufferAppendedLength = QMux->RecvBufferAllocLength - QMux->RecvBufferLength;
+ QMux->ResultFlags =
+ CxPlatTlsDecrypt(
+ QMux->TLS,
+ RecvData->Buffer + RecvDataOffset,
+ &RecvDataConsumedLength,
+ QMux->RecvBuffer + QMux->RecvBufferLength,
+ &RecvBufferAppendedLength);
+ if (QMux->ResultFlags & CXPLAT_TLS_RESULT_RENEGOTIATE) {
+ // If renegotiation is requested, we need to process the received data through
+ // the TLS stack again to advance the handshake.
+ RecvDataConsumedLength = RecvDataLength - RecvDataOffset;
+ Status =
+ QuicQMuxProcessHandshake(
+ QMux,
+ CXPLAT_TLS_CRYPTO_DATA,
+ RecvData->Buffer + RecvDataOffset,
+ &RecvDataConsumedLength);
+ if (QUIC_FAILED(Status)) {
+ goto Error;
+ }
+ RecvDataOffset += RecvDataConsumedLength;
+ RecvDataConsumedLength = RecvDataLength - RecvDataOffset;
+ continue;
+ } else if (QMux->ResultFlags & CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL) {
+ // The receive buffer is too small to hold the decrypted data.
+ uint32_t RequiredLength = QMux->RecvBufferLength + RecvBufferAppendedLength;
+ uint32_t NewRecvBufferAllocLength = QMux->RecvBufferAllocLength;
+ while (RequiredLength > NewRecvBufferAllocLength) {
+ if (NewRecvBufferAllocLength > UINT32_MAX / 2) {
+ QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Decrypted data exceeds maximum buffer size");
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)Status,
+ NULL);
+ goto Error;
+ }
+ NewRecvBufferAllocLength *= 2;
+ }
+ uint8_t* NewRecvBuffer =
+ CXPLAT_ALLOC_NONPAGED(
+ NewRecvBufferAllocLength,
+ QUIC_POOL_QMUX_RECV_BUFFER);
+ if (NewRecvBuffer == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "QMux receive buffer",
+ NewRecvBufferAllocLength);
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)Status,
+ NULL);
+ goto Error;
+ }
+ if (QMux->RecvBufferLength > 0) {
+ CxPlatCopyMemory(NewRecvBuffer, QMux->RecvBuffer, QMux->RecvBufferLength);
+ }
+ CXPLAT_FREE(QMux->RecvBuffer, QUIC_POOL_QMUX_RECV_BUFFER);
+ QMux->RecvBuffer = NewRecvBuffer;
+ QMux->RecvBufferAllocLength = NewRecvBufferAllocLength;
+
+ RecvDataOffset += RecvDataConsumedLength;
+ RecvDataConsumedLength = RecvDataLength - RecvDataOffset;
+ continue;
+ } else if (QMux->ResultFlags & CXPLAT_TLS_RESULT_ERROR) {
+ Status = QUIC_STATUS_TLS_ERROR;
+ QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "Decrypting TLS data");
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)Status,
+ NULL);
+ goto Error;
+ }
+
+ RecvDataOffset += RecvDataConsumedLength;
+ RecvDataConsumedLength = RecvDataLength - RecvDataOffset;
+ QMux->RecvBufferLength += RecvBufferAppendedLength;
+
+ QUIC_VAR_INT RecordLength;
+ uint16_t RecordOffset;
+ uint32_t RecvBufferOffset = 0;
+ do {
+ RecordLength = 0;
+ RecordOffset = 0;
+ if (!QuicVarIntDecode(
+ (uint16_t)(QMux->RecvBufferLength - RecvBufferOffset),
+ QMux->RecvBuffer + RecvBufferOffset,
+ &RecordOffset,
+ &RecordLength)) {
+ break;
+ }
+ if (QMux->RecvBufferLength - RecvBufferOffset < RecordOffset + RecordLength) {
+ break;
+ }
+ QuicTraceEvent(
+ ConnRecvPacket,
+ "[conn][%p][RX] %hu bytes",
+ Connection,
+ (uint16_t)RecordLength);
+
+ QuicQMuxRecvFrames(
+ QMux,
+ QMux->RecvBuffer + RecvBufferOffset + RecordOffset,
+ (uint16_t)RecordLength);
+ QuicConnResetIdleTimeout(Connection);
+ RecvBufferOffset += RecordOffset + (uint16_t)RecordLength;
+ } while (RecvBufferOffset < QMux->RecvBufferLength);
+
+ if (RecvBufferOffset > 0 && RecvBufferOffset < QMux->RecvBufferLength) {
+ // Move any remaining data to the beginning of the buffer for the next receive.
+ CxPlatMoveMemory(
+ QMux->RecvBuffer,
+ QMux->RecvBuffer + RecvBufferOffset,
+ QMux->RecvBufferLength - RecvBufferOffset);
+ }
+ QMux->RecvBufferLength -= RecvBufferOffset;
+ } while (RecvDataConsumedLength > 0 || RecvBufferAppendedLength > 0);
+ }
+ }
+
+Error:
+ if (ReleaseChain != NULL) {
+ CxPlatRecvDataReturn(ReleaseChain);
+ }
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+QuicQMuxFlushRecv(
+ _In_ QUIC_QMUX* QMux
+ )
+{
+ BOOLEAN FlushedAll;
+ uint32_t TcpReceiveQueueCount, TcpReceiveQueueByteCount;
+ CXPLAT_RECV_DATA* TcpReceiveQueue;
+
+ CxPlatDispatchLockAcquire(&QMux->TcpReceiveQueueLock);
+ TcpReceiveQueue = QMux->TcpReceiveQueue;
+ FlushedAll = TRUE;
+ TcpReceiveQueueCount = QMux->TcpReceiveQueueCount;
+ TcpReceiveQueueByteCount = QMux->TcpReceiveQueueByteCount;
+ QMux->TcpReceiveQueueCount = 0;
+ QMux->TcpReceiveQueueByteCount = 0;
+ QMux->TcpReceiveQueue = NULL;
+ QMux->TcpReceiveQueueTail = &QMux->TcpReceiveQueue;
+ CxPlatDispatchLockRelease(&QMux->TcpReceiveQueueLock);
+
+ QuicQMuxRecvData(
+ QMux, TcpReceiveQueue, TcpReceiveQueueCount, TcpReceiveQueueByteCount);
+
+ return FlushedAll;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxProcessTcpDisconnect(
+ _In_ QUIC_QMUX* QMux
+ )
+{
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ //
+ // Close the connection since the connection was disconnected.
+ //
+ QuicConnCloseLocally(
+ Connection,
+ QUIC_CLOSE_INTERNAL_SILENT | QUIC_CLOSE_QUIC_STATUS,
+ (uint64_t)QUIC_STATUS_ABORTED,
+ NULL);
+
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxTcpAccept(
+ _In_ CXPLAT_SOCKET* ListenerSocket,
+ _In_ void* ListenerContext,
+ _In_ CXPLAT_SOCKET* AcceptSocket,
+ _Out_ void** AcceptClientContext
+ )
+{
+ UNREFERENCED_PARAMETER(ListenerSocket);
+ *AcceptClientContext = NULL;
+ QUIC_LISTENER* Listener = (QUIC_LISTENER*)ListenerContext;
+ QUIC_CONNECTION* Connection;
+ QUIC_STATUS Status =
+ QuicConnQMuxAlloc(
+ Listener->Registration,
+ &MsQuicLib.Partitions[QuicLibraryGetCurrentPartition()->Index],
+ NULL,
+ TRUE,
+ &Connection);
+ if (QUIC_FAILED(Status)) {
+ return Status;
+ }
+ Connection->State.ListenerAccepted = TRUE;
+ Connection->State.ExternalOwner = TRUE;
+
+ QUIC_QMUX* QMux = QuicConnGetQMux(Connection);
+ QMux->Socket = AcceptSocket;
+
+ QUIC_NEW_CONNECTION_INFO Info = {0};
+ Connection->State.LocalAddressSet = TRUE;
+ CxPlatSocketGetLocalAddress(AcceptSocket, &QMux->Route.LocalAddress);
+ Connection->State.RemoteAddressSet = TRUE;
+ CxPlatSocketGetRemoteAddress(AcceptSocket, &QMux->Route.RemoteAddress);
+ Info.LocalAddress = &QMux->Route.LocalAddress;
+ Info.RemoteAddress = &QMux->Route.RemoteAddress;
+
+ QUIC_LISTENER_EVENT Event;
+ Event.Type = QUIC_LISTENER_EVENT_NEW_CONNECTION;
+ Event.NEW_CONNECTION.Info = &Info;
+ Event.NEW_CONNECTION.Connection = (HQUIC)Connection;
+
+ QuicListenerAttachSilo(Listener);
+
+ QuicTraceLogVerbose(
+ ListenerIndicateNewConnection,
+ "[list][%p] Indicating NEW_CONNECTION %p",
+ Listener,
+ Connection);
+
+ Status = QuicListenerIndicateEvent(Listener, &Event);
+
+ QuicListenerDetachSilo();
+
+ if (QUIC_FAILED(Status)) {
+ CXPLAT_FRE_ASSERTMSG(
+ !Connection->State.HandleClosed,
+ "App MUST not close and reject connection!");
+ Connection->State.ExternalOwner = FALSE;
+ QuicTraceEvent(
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "NEW_CONNECTION callback");
+ QuicConnTransportError(
+ Connection,
+ QUIC_ERROR_CONNECTION_REFUSED);
+ return QUIC_STATUS_SUCCESS; // Return success since the connection was handled, just rejected.
+ }
+
+ //
+ // The application layer has accepted the connection.
+ //
+ CXPLAT_FRE_ASSERTMSG(
+ Connection->State.HandleClosed ||
+ Connection->ClientCallbackHandler != NULL,
+ "App MUST set callback handler or close connection!");
+
+ *AcceptClientContext = QMux;
+
+ // start the connection's idle timer
+ QuicConnResetIdleTimeout(Connection);
+
+ return QUIC_STATUS_SUCCESS;
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxTcpConnect(
+ _In_ CXPLAT_SOCKET* Socket,
+ _In_ void* Context,
+ _In_ BOOLEAN Connected
+ )
+{
+ UNREFERENCED_PARAMETER(Socket);
+ QUIC_QMUX* QMux = (QUIC_QMUX*)Context;
+ QUIC_CONNECTION* Connection = QMux->Connection;
+
+ if (Connected) {
+ QuicTraceLogConnInfo(
+ TcpConnected,
+ Connection,
+ "TCP connected");
+ Connection->State.TcpConnected = TRUE;
+ CxPlatEventSet(QMux->ConnectEvent);
+ } else {
+ QuicTraceLogConnInfo(
+ TcpDisconnected,
+ Connection,
+ "TCP disconnected");
+ QUIC_OPERATION* ConnOper =
+ QuicConnAllocOperation(Connection, QUIC_OPER_TYPE_TCP_DISCONNECT);
+ if (ConnOper != NULL) {
+ QuicConnQueueOper(Connection, ConnOper);
+ } else {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "Disconnect TCP operation",
+ 0);
+ }
+ }
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxTcpReceive(
+ _In_ CXPLAT_SOCKET* Socket,
+ _In_ void* Context,
+ _In_ CXPLAT_RECV_DATA* RecvDataChain
+ )
+{
+ UNREFERENCED_PARAMETER(Socket);
+ QUIC_QMUX* QMux = (QUIC_QMUX*)Context;
+ QUIC_CONNECTION* Connection = QMux->Connection;
+ uint32_t TotalChainLength = 0;
+ uint32_t TotalChainByteLength = 0;
+ CXPLAT_RECV_DATA* RecvData = RecvDataChain;
+
+ while (RecvData != NULL) {
+ TotalChainLength++;
+ TotalChainByteLength += RecvData->BufferLength;
+ RecvData = RecvData->Next;
+ }
+
+ QuicTraceLogConnInfo(
+ TcpDataReceived,
+ Connection,
+ "TCP data received: %u bytes in %u segments",
+ TotalChainByteLength,
+ TotalChainLength);
+
+ QuicQMuxQueueRecvData(QMux, RecvDataChain, TotalChainLength, TotalChainByteLength);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxTcpSendComplete(
+ _In_ CXPLAT_SOCKET* Socket,
+ _In_ void* Context,
+ _In_ QUIC_STATUS Status,
+ _In_ uint32_t ByteCount
+ )
+{
+ UNREFERENCED_PARAMETER(Socket);
+ UNREFERENCED_PARAMETER(Context);
+ UNREFERENCED_PARAMETER(Status);
+ UNREFERENCED_PARAMETER(ByteCount);
+}
diff --git a/src/core/qmux.h b/src/core/qmux.h
new file mode 100644
index 0000000000..611574b613
--- /dev/null
+++ b/src/core/qmux.h
@@ -0,0 +1,181 @@
+/*++
+
+ Copyright (c) Microsoft Corporation.
+ Licensed under the MIT License.
+
+--*/
+
+typedef struct QUIC_CONNECTION QUIC_CONNECTION;
+
+typedef struct QUIC_QMUX {
+
+ //
+ // The parent connection for this QMux.
+ //
+ QUIC_CONNECTION* Connection;
+
+ //
+ // The datapath binding for QMUX.
+ //
+ CXPLAT_SOCKET* Socket;
+
+ //
+ // The local and remote addresses for the connection.
+ //
+ CXPLAT_ROUTE Route;
+
+ //
+ // Event to signal when the TCP connection is established.
+ //
+ CXPLAT_EVENT ConnectEvent;
+
+ //
+ // The TLS context.
+ //
+ CXPLAT_TLS* TLS;
+
+ //
+ // Send State
+ //
+ CXPLAT_TLS_PROCESS_STATE TlsState;
+
+ //
+ // Result flags from the last Tls process call.
+ //
+ CXPLAT_TLS_RESULT_FLAGS ResultFlags;
+
+ CXPLAT_RECV_DATA* TcpReceiveQueue;
+ CXPLAT_RECV_DATA** TcpReceiveQueueTail;
+ CXPLAT_DISPATCH_LOCK TcpReceiveQueueLock;
+ uint32_t TcpReceiveQueueCount;
+ uint32_t TcpReceiveQueueByteCount;
+
+ //
+ // Buffer for decrypted data.
+ //
+ uint8_t* RecvBuffer;
+ uint32_t RecvBufferAllocLength;
+ uint32_t RecvBufferLength;
+
+ //
+ // Sequence numbers for QX_PING frames.
+ // The next ping sequence number to send, and the last one received from the peer.
+ //
+ QUIC_VAR_INT NextPingSequenceNumber;
+ QUIC_VAR_INT RecvPingSequenceNumber;
+
+ BOOLEAN RecvPing;
+
+ BOOLEAN PermitEarlyData;
+ BOOLEAN ReadEarlyData;
+
+ //
+ // Buffer for early data.
+ //
+ uint8_t* EarlyDataBuffer;
+ uint32_t EarlyDataBufferAllocLength;
+ uint32_t EarlyDataBufferLength;
+
+ //
+ // Outstanding packets.
+ //
+ QUIC_SENT_PACKET_METADATA* SentEarlyDataPackets;
+ QUIC_SENT_PACKET_METADATA** SentEarlyDataPacketsTail;
+
+ //
+ // Indicates Resumption ticket validation is under validation asynchronously
+ //
+ BOOLEAN TicketValidationPending : 1;
+ BOOLEAN TicketValidationRejecting : 1;
+ uint32_t PendingValidationBufferLength;
+
+ //
+ // Resumption ticket to send to server.
+ //
+ uint8_t* ResumptionTicket;
+ uint32_t ResumptionTicketLength;
+
+} QUIC_QMUX;
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxInitialize(
+ _In_ QUIC_CONNECTION* Connection,
+ _Out_ QUIC_QMUX** NewQMux
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxUninitialize(
+ _In_ QUIC_QMUX* QMux
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxInitializeTls(
+ _Inout_ QUIC_QMUX* QMux,
+ _In_ CXPLAT_SEC_CONFIG* SecConfig
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxProcessHandshake(
+ _In_ QUIC_QMUX* QMux,
+ _In_ CXPLAT_TLS_DATA_TYPE DataType,
+ _In_reads_bytes_(*BufferLength)
+ const uint8_t* Buffer,
+ _Inout_ uint32_t* BufferLength
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+QuicQMuxFlushRecv(
+ _In_ QUIC_QMUX* QMux
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxOnPacketAcknowledged(
+ _In_ QUIC_QMUX* QMux,
+ _In_ QUIC_SENT_PACKET_METADATA* Packet
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+QuicQMuxProcessTcpDisconnect(
+ _In_ QUIC_QMUX* QMux
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicQMuxTcpAccept(
+ _In_ CXPLAT_SOCKET* ListenerSocket,
+ _In_ void* ListenerContext,
+ _In_ CXPLAT_SOCKET* AcceptSocket,
+ _Out_ void** AcceptClientContext
+ );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxTcpConnect(
+ _In_ CXPLAT_SOCKET* Socket,
+ _In_ void* Context,
+ _In_ BOOLEAN Connected
+ );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxTcpReceive(
+ _In_ CXPLAT_SOCKET* Socket,
+ _In_ void* Context,
+ _In_ CXPLAT_RECV_DATA* RecvDataChain
+ );
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+void
+QuicQMuxTcpSendComplete(
+ _In_ CXPLAT_SOCKET* Socket,
+ _In_ void* Context,
+ _In_ QUIC_STATUS Status,
+ _In_ uint32_t ByteCount
+ );
diff --git a/src/core/quicdef.h b/src/core/quicdef.h
index 4491b5c599..a6aee64ddd 100644
--- a/src/core/quicdef.h
+++ b/src/core/quicdef.h
@@ -233,6 +233,7 @@ typedef struct QUIC_RX_PACKET QUIC_RX_PACKET;
//
#define QUIC_DEFAULT_STREAM_RECV_BUFFER_SIZE 0x1000 // 4096
+#define QX_DEFAULT_SEND_BUFFER_SIZE (0x4000 - 2) // 16382
//
// The default connection flow control window value, in bytes.
//
@@ -622,6 +623,7 @@ CXPLAT_STATIC_ASSERT(
#define QUIC_TP_FLAG_TIMESTAMP_RECV_ENABLED 0x01000000
#define QUIC_TP_FLAG_TIMESTAMP_SEND_ENABLED 0x02000000
#define QUIC_TP_FLAG_TIMESTAMP_SHIFT 24
+#define QX_TP_FLAG_MAX_RECORD_SIZE 0x20000000
#define QUIC_TP_MAX_PACKET_SIZE_DEFAULT 65527
#define QUIC_TP_MAX_UDP_PAYLOAD_SIZE_MIN 1200
@@ -637,6 +639,7 @@ CXPLAT_STATIC_ASSERT(
#define QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT_DEFAULT 2
#define QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT_MIN 2
+#define QX_TP_MAX_RECORD_SIZE_DEFAULT (0x4000 - 2)
//
// Max allowed value of a MAX_STREAMS frame or transport parameter.
// Any larger value would allow a max stream ID that cannot be expressed
diff --git a/src/core/send.c b/src/core/send.c
index acae74661d..35ead9a69b 100644
--- a/src/core/send.c
+++ b/src/core/send.c
@@ -118,14 +118,16 @@ QuicSendCanSendFlagsNow(
)
{
QUIC_CONNECTION* Connection = QuicSendGetConnection(Send);
- if (Connection->Crypto.TlsState.WriteKey < QUIC_PACKET_KEY_1_RTT) {
- if (Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_0_RTT] != NULL &&
- CxPlatListIsEmpty(&Send->SendStreams)) {
- return TRUE;
- }
- if ((!Connection->State.Started && QuicConnIsClient(Connection)) ||
- !(Send->SendFlags & QUIC_CONN_SEND_FLAG_ALLOWED_HANDSHAKE)) {
- return FALSE;
+ if (!QuicConnIsQMux(Connection)) {
+ if (Connection->Crypto.TlsState.WriteKey < QUIC_PACKET_KEY_1_RTT) {
+ if (Connection->Crypto.TlsState.WriteKeys[QUIC_PACKET_KEY_0_RTT] != NULL &&
+ CxPlatListIsEmpty(&Send->SendStreams)) {
+ return TRUE;
+ }
+ if ((!Connection->State.Started && QuicConnIsClient(Connection)) ||
+ !(Send->SendFlags & QUIC_CONN_SEND_FLAG_ALLOWED_HANDSHAKE)) {
+ return FALSE;
+ }
}
}
return TRUE;
@@ -303,7 +305,8 @@ QuicSendSetSendFlag(
!!(SendFlags & (QUIC_CONN_SEND_FLAG_CONNECTION_CLOSE | QUIC_CONN_SEND_FLAG_APPLICATION_CLOSE));
const BOOLEAN CanSetFlag =
- !QuicConnIsClosed(Connection) || (!Connection->State.ClosedSilently && IsCloseFrame);
+ (!QuicConnIsQMux(Connection) && (!QuicConnIsClosed(Connection) || (!Connection->State.ClosedSilently && IsCloseFrame))) ||
+ (QuicConnIsQMux(Connection) && (QuicConnGetQMux(Connection)->TlsState.HandshakeComplete || QuicConnGetQMux(Connection)->PermitEarlyData));
if (SendFlags & QUIC_CONN_SEND_FLAG_ACK && Send->DelayedAckTimerActive) {
QuicConnTimerCancel(Connection, QUIC_CONN_TIMER_ACK_DELAY);
@@ -494,8 +497,11 @@ QuicSendWriteFrames(
uint8_t PrevFrameCount = Builder->Metadata->FrameCount;
BOOLEAN RanOutOfRoom = FALSE;
- QUIC_PACKET_SPACE* Packets = Connection->Packets[Builder->EncryptLevel];
- CXPLAT_DBG_ASSERT(Packets != NULL);
+ QUIC_PACKET_SPACE* Packets =
+ !QuicConnIsQMux(Connection) ?
+ Connection->Packets[Builder->EncryptLevel] :
+ NULL;
+ CXPLAT_DBG_ASSERT(QuicConnIsQMux(Connection) || Packets != NULL);
BOOLEAN IsCongestionControlBlocked = !QuicPacketBuilderHasAllowance(Builder);
@@ -514,7 +520,8 @@ QuicSendWriteFrames(
uint8_t ZeroRttPacketType =
Connection->Stats.QuicVersion == QUIC_VERSION_2 ?
QUIC_0_RTT_PROTECTED_V2 : QUIC_0_RTT_PROTECTED_V1;
- if (Builder->PacketType != ZeroRttPacketType &&
+ if (!QuicConnIsQMux(Connection) &&
+ Builder->PacketType != ZeroRttPacketType &&
QuicAckTrackerHasPacketsToAck(&Packets->AckTracker)) {
if (!QuicAckTrackerAckFrameEncode(&Packets->AckTracker, Builder)) {
RanOutOfRoom = TRUE;
@@ -522,7 +529,85 @@ QuicSendWriteFrames(
}
}
- if (!IsCongestionControlBlocked &&
+ if (QuicConnIsQMux(Connection) &&
+ Send->SendFlags & QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS
+ ) {
+ QUIC_TRANSPORT_PARAMETERS LocalTP = { 0 };
+ QUIC_STATUS Status = QuicConnGenerateLocalTransportParameters(Connection, &LocalTP);
+ if (QUIC_FAILED(Status)) {
+ goto Exit;
+ }
+ QX_TRANSPORT_PARAMETERS_EX Frame = { 0 };
+ Frame.TP = QuicCryptoTlsEncodeTransportParameters(
+ Connection,
+ QuicConnIsServer(Connection),
+ &LocalTP,
+ NULL,
+ (uint32_t *)&Frame.Length);
+ if (Frame.TP == NULL) {
+ // XXX - This is a failure case that we should handle better.
+ QuicCryptoTlsCleanupTransportParameters(&LocalTP);
+ goto Exit;
+ }
+ if (QxTransportParametersFrameEncode(
+ &Frame,
+ &Builder->DatagramLength,
+ AvailableBufferLength,
+ Builder->Datagram->Buffer)) {
+ if (QuicPacketBuilderAddFrame(Builder, QX_FRAME_TRANSPORT_PARAMETERS, FALSE)) {
+ CXPLAT_FREE(Frame.TP, QUIC_POOL_TLS_TRANSPARAMS);
+ QuicCryptoTlsCleanupTransportParameters(&LocalTP);
+ return TRUE;
+ }
+ Connection->State.LocalTPSent = TRUE;
+ Send->SendFlags &= ~QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS;
+ } else {
+ RanOutOfRoom = TRUE;
+ }
+ CXPLAT_FREE(Frame.TP, QUIC_POOL_TLS_TRANSPARAMS);
+ QuicCryptoTlsCleanupTransportParameters(&LocalTP);
+ }
+
+ if (QuicConnIsQMux(Connection) &&
+ Send->SendFlags & QUIC_CONN_SEND_FLAG_QX_PING
+ ) {
+ QUIC_QMUX* QMux = QuicConnGetQMux(Connection);
+ QX_PING_EX Frame = { QMux->NextPingSequenceNumber++, FALSE };
+ if (QxPingFrameEncode(
+ &Frame,
+ &Builder->DatagramLength,
+ AvailableBufferLength,
+ Builder->Datagram->Buffer)) {
+ if (QuicPacketBuilderAddFrame(Builder, QX_FRAME_PING, FALSE)) {
+ return TRUE;
+ }
+ Send->SendFlags &= ~QUIC_CONN_SEND_FLAG_QX_PING;
+ } else {
+ RanOutOfRoom = TRUE;
+ }
+ }
+
+ if (QuicConnIsQMux(Connection) &&
+ Send->SendFlags & QUIC_CONN_SEND_FLAG_QX_PING_RESPONSE
+ ) {
+ QUIC_QMUX* QMux = QuicConnGetQMux(Connection);
+ QX_PING_EX Frame = { QMux->RecvPingSequenceNumber, TRUE };
+ if (QxPingFrameEncode(
+ &Frame,
+ &Builder->DatagramLength,
+ AvailableBufferLength,
+ Builder->Datagram->Buffer)) {
+ if (QuicPacketBuilderAddFrame(Builder, QX_FRAME_PING_1, FALSE)) {
+ return TRUE;
+ }
+ Send->SendFlags &= ~QUIC_CONN_SEND_FLAG_QX_PING_RESPONSE;
+ } else {
+ RanOutOfRoom = TRUE;
+ }
+ }
+
+ if (!QuicConnIsQMux(Connection) &&
+ !IsCongestionControlBlocked &&
Send->SendFlags & QUIC_CONN_SEND_FLAG_CRYPTO) {
if (QuicCryptoWriteFrames(&Connection->Crypto, Builder)) {
if (Builder->Metadata->FrameCount == QUIC_MAX_FRAMES_PER_PACKET) {
@@ -533,7 +618,7 @@ QuicSendWriteFrames(
}
}
- if (Send->SendFlags & (QUIC_CONN_SEND_FLAG_CONNECTION_CLOSE | QUIC_CONN_SEND_FLAG_APPLICATION_CLOSE)) {
+ if ((Send->SendFlags & (QUIC_CONN_SEND_FLAG_CONNECTION_CLOSE | QUIC_CONN_SEND_FLAG_APPLICATION_CLOSE))) {
BOOLEAN IsApplicationClose =
!!(Send->SendFlags & QUIC_CONN_SEND_FLAG_APPLICATION_CLOSE);
if (Connection->State.ClosedRemotely) {
@@ -548,7 +633,7 @@ QuicSendWriteFrames(
QUIC_VAR_INT CloseErrorCode = Connection->CloseErrorCode;
char* CloseReasonPhrase = Connection->CloseReasonPhrase;
- if (IsApplicationClose && ! Is1RttEncryptionLevel) {
+ if (!QuicConnIsQMux(Connection) && IsApplicationClose && ! Is1RttEncryptionLevel) {
//
// A CONNECTION_CLOSE of type 0x1d MUST be replaced by a CONNECTION_CLOSE of
// type 0x1c when sending the frame in Initial or Handshake packets. Otherwise,
@@ -582,7 +667,7 @@ QuicSendWriteFrames(
// We send in increasing encryption level so clear the flag only once
// we send on the current protection level.
//
- if (Builder->Key->Type == Connection->Crypto.TlsState.WriteKey) {
+ if (QuicConnIsQMux(Connection) || Builder->Key->Type == Connection->Crypto.TlsState.WriteKey) {
Send->SendFlags &= ~(QUIC_CONN_SEND_FLAG_CONNECTION_CLOSE | QUIC_CONN_SEND_FLAG_APPLICATION_CLOSE);
}
@@ -595,7 +680,7 @@ QuicSendWriteFrames(
return TRUE;
}
- if (IsCongestionControlBlocked) {
+ if (!QuicConnIsQMux(Connection) && IsCongestionControlBlocked) {
//
// Everything below this is not allowed to be sent while CC blocked.
//
@@ -603,7 +688,7 @@ QuicSendWriteFrames(
goto Exit;
}
- if (Send->SendFlags & QUIC_CONN_SEND_FLAG_PATH_RESPONSE) {
+ if (!QuicConnIsQMux(Connection) && Send->SendFlags & QUIC_CONN_SEND_FLAG_PATH_RESPONSE) {
uint8_t i;
for (i = 0; i < Connection->PathsCount; ++i) {
@@ -645,9 +730,10 @@ QuicSendWriteFrames(
}
}
- if (Is1RttEncryptionLevel) {
- if (Builder->Metadata->Flags.KeyType == QUIC_PACKET_KEY_1_RTT &&
- Send->SendFlags & QUIC_CONN_SEND_FLAG_HANDSHAKE_DONE) {
+ if (QuicConnIsQMux(Connection) || Is1RttEncryptionLevel) {
+ if (!QuicConnIsQMux(Connection) &&
+ Builder->Metadata->Flags.KeyType == QUIC_PACKET_KEY_1_RTT &&
+ (Send->SendFlags & QUIC_CONN_SEND_FLAG_HANDSHAKE_DONE)) {
if (Builder->DatagramLength < AvailableBufferLength) {
Builder->Datagram->Buffer[Builder->DatagramLength++] = QUIC_FRAME_HANDSHAKE_DONE;
@@ -791,7 +877,8 @@ QuicSendWriteFrames(
}
}
- if ((Send->SendFlags & QUIC_CONN_SEND_FLAG_NEW_CONNECTION_ID)) {
+ if (!QuicConnIsQMux(Connection) &&
+ (Send->SendFlags & QUIC_CONN_SEND_FLAG_NEW_CONNECTION_ID)) {
BOOLEAN HasMoreCidsToSend = FALSE;
BOOLEAN MaxFrameLimitHit = FALSE;
@@ -857,7 +944,8 @@ QuicSendWriteFrames(
}
}
- if ((Send->SendFlags & QUIC_CONN_SEND_FLAG_RETIRE_CONNECTION_ID)) {
+ if (!QuicConnIsQMux(Connection) &&
+ (Send->SendFlags & QUIC_CONN_SEND_FLAG_RETIRE_CONNECTION_ID)) {
BOOLEAN HasMoreCidsToSend = FALSE;
BOOLEAN MaxFrameLimitHit = FALSE;
@@ -908,7 +996,8 @@ QuicSendWriteFrames(
}
}
- if (Send->SendFlags & QUIC_CONN_SEND_FLAG_ACK_FREQUENCY) {
+ if (!QuicConnIsQMux(Connection) &&
+ (Send->SendFlags & QUIC_CONN_SEND_FLAG_ACK_FREQUENCY)) {
QUIC_ACK_FREQUENCY_EX Frame;
Frame.SequenceNumber = Connection->SendAckFreqSeqNum;
@@ -986,7 +1075,7 @@ QuicSendCanSendStreamNow(
QUIC_CONNECTION* Connection = Stream->Connection;
- if (Connection->Crypto.TlsState.WriteKey == QUIC_PACKET_KEY_1_RTT) {
+ if (QuicConnIsQMux(Connection) || Connection->Crypto.TlsState.WriteKey == QUIC_PACKET_KEY_1_RTT) {
return QuicStreamCanSendNow(Stream, FALSE);
}
@@ -1016,7 +1105,6 @@ QuicSendGetNextStream(
//
QUIC_STREAM* Stream = CXPLAT_CONTAINING_RECORD(Entry, QUIC_STREAM, SendLink);
-
//
// Make sure, given the current state of the connection and the stream,
// that we can use the stream to frame a packet.
@@ -1210,7 +1298,7 @@ QuicSendFlush(
CXPLAT_DBG_ASSERT(!Connection->State.HandleClosed);
- if (!CxPlatIsRouteReady(Connection, Path)) {
+ if (!QuicConnIsQMux(Connection) && !CxPlatIsRouteReady(Connection, Path)) {
return TRUE;
}
@@ -1218,7 +1306,7 @@ QuicSendFlush(
QuicConnRemoveOutFlowBlockedReason(
Connection, QUIC_FLOW_BLOCKED_SCHEDULING | QUIC_FLOW_BLOCKED_PACING);
- if (Path->DestCid == NULL) {
+ if (!QuicConnIsQMux(Connection) && Path->DestCid == NULL) {
return TRUE;
}
@@ -1228,7 +1316,7 @@ QuicSendFlush(
//
// If path is active without being peer validated, disable MTU flag if set.
//
- if (!Path->IsPeerValidated) {
+ if (!QuicConnIsQMux(Connection) && !Path->IsPeerValidated) {
Send->SendFlags &= ~QUIC_CONN_SEND_FLAG_DPLPMTUD;
}
@@ -1236,10 +1324,17 @@ QuicSendFlush(
return TRUE;
}
+ if (QuicConnIsQMux(Connection) &&
+ !QuicConnGetQMux(Connection)->TlsState.HandshakeComplete &&
+ !QuicConnGetQMux(Connection)->PermitEarlyData) {
+ return TRUE;
+ }
+
//
// Connection CID changes on idle state after an amount of time
//
- if (Connection->Settings.DestCidUpdateIdleTimeoutMs != 0 &&
+ if (!QuicConnIsQMux(Connection) &&
+ Connection->Settings.DestCidUpdateIdleTimeoutMs != 0 &&
Send->LastFlushTimeValid &&
CxPlatTimeDiff64(Send->LastFlushTime, TimeNow) >= MS_TO_US(Connection->Settings.DestCidUpdateIdleTimeoutMs)) {
(void)QuicConnRetireCurrentDestCid(Connection, Path);
@@ -1249,7 +1344,7 @@ QuicSendFlush(
// Send path challenges.
// `QuicSendPathChallenges` might re-queue a path challenge immediately.
//
- if (Send->SendFlags & QUIC_CONN_SEND_FLAG_PATH_CHALLENGE) {
+ if (!QuicConnIsQMux(Connection) && Send->SendFlags & QUIC_CONN_SEND_FLAG_PATH_CHALLENGE) {
Send->SendFlags &= ~QUIC_CONN_SEND_FLAG_PATH_CHALLENGE;
QuicSendPathChallenges(Send);
}
@@ -1265,26 +1360,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.");
+ if (!QuicConnIsQMux(Connection)) {
+ 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 {
+ uint64_t ThreePtosInUs =
+ QuicLossDetectionComputeProbeTimeout(
+ &Connection->LossDetection,
+ &Connection->Paths[0],
+ QUIC_CLOSE_PTO_COUNT);
+ Builder.Path->EcnTestingEndingTime = TimeNow + ThreePtosInUs;
}
- } else {
- uint64_t ThreePtosInUs =
- QuicLossDetectionComputeProbeTimeout(
- &Connection->LossDetection,
- &Connection->Paths[0],
- QUIC_CLOSE_PTO_COUNT);
- Builder.Path->EcnTestingEndingTime = TimeNow + ThreePtosInUs;
+ Builder.EcnEctSet = TRUE;
}
- Builder.EcnEctSet = TRUE;
}
QuicTraceEvent(
@@ -1304,7 +1401,7 @@ QuicSendFlush(
uint32_t StreamPacketCount = 0;
do {
- if (Path->Allowance < QUIC_MIN_SEND_ALLOWANCE) {
+ if (!QuicConnIsQMux(Connection) && Path->Allowance < QUIC_MIN_SEND_ALLOWANCE) {
QuicTraceLogConnVerbose(
AmplificationProtectionBlocked,
Connection,
@@ -1314,10 +1411,10 @@ QuicSendFlush(
}
uint32_t SendFlags = Send->SendFlags;
- if (Connection->Crypto.TlsState.WriteKey < QUIC_PACKET_KEY_1_RTT) {
+ if (!QuicConnIsQMux(Connection) && Connection->Crypto.TlsState.WriteKey < QUIC_PACKET_KEY_1_RTT) {
SendFlags &= QUIC_CONN_SEND_FLAG_ALLOWED_HANDSHAKE;
}
- if (Path->Allowance != UINT32_MAX) {
+ if (!QuicConnIsQMux(Connection) && Path->Allowance != UINT32_MAX) {
//
// Don't try to send datagrams until the peer's source address has
// been validated because they might not fit in the limited space.
@@ -1325,7 +1422,7 @@ QuicSendFlush(
SendFlags &= ~QUIC_CONN_SEND_FLAG_DATAGRAM;
}
- if (!QuicPacketBuilderHasAllowance(&Builder)) {
+ if (!QuicConnIsQMux(Connection) && !QuicPacketBuilderHasAllowance(&Builder)) {
//
// While we are CC blocked, very few things are still allowed to
// be sent. If those are queued then we can still send.
@@ -1362,7 +1459,7 @@ QuicSendFlush(
// 3. Stream (control and application) data.
//
- BOOLEAN WrotePacketFrames;
+ BOOLEAN WrotePacketFrames = FALSE;
BOOLEAN FlushBatchedDatagrams = FALSE;
BOOLEAN SendConnectionControlData =
(SendFlags & ~(QUIC_CONN_SEND_FLAG_DPLPMTUD |
@@ -1402,17 +1499,19 @@ QuicSendFlush(
break;
}
- //
- // Write any ACK frames if we have them.
- //
- QUIC_PACKET_SPACE* Packets = Connection->Packets[Builder.EncryptLevel];
- uint8_t ZeroRttPacketType =
- Connection->Stats.QuicVersion == QUIC_VERSION_2 ?
- QUIC_0_RTT_PROTECTED_V2 : QUIC_0_RTT_PROTECTED_V1;
- WrotePacketFrames =
- Builder.PacketType != ZeroRttPacketType &&
- QuicAckTrackerHasPacketsToAck(&Packets->AckTracker) &&
- QuicAckTrackerAckFrameEncode(&Packets->AckTracker, &Builder);
+ if (!QuicConnIsQMux(Connection)) {
+ //
+ // Write any ACK frames if we have them.
+ //
+ QUIC_PACKET_SPACE* Packets = Connection->Packets[Builder.EncryptLevel];
+ uint8_t ZeroRttPacketType =
+ Connection->Stats.QuicVersion == QUIC_VERSION_2 ?
+ QUIC_0_RTT_PROTECTED_V2 : QUIC_0_RTT_PROTECTED_V1;
+ WrotePacketFrames =
+ Builder.PacketType != ZeroRttPacketType &&
+ QuicAckTrackerHasPacketsToAck(&Packets->AckTracker) &&
+ QuicAckTrackerAckFrameEncode(&Packets->AckTracker, &Builder);
+ }
//
// Write the stream frames.
@@ -1455,7 +1554,8 @@ QuicSendFlush(
// We now have enough data in the current packet that we should
// finalize it.
//
- if (!QuicPacketBuilderFinalize(&Builder, !WrotePacketFrames || FlushBatchedDatagrams)) {
+ if ((!QuicConnIsQMux(Connection) && !QuicPacketBuilderFinalize(&Builder, !WrotePacketFrames || FlushBatchedDatagrams)) ||
+ (QuicConnIsQMux(Connection) && !QuicPacketBuilderQMuxFinalize(&Builder, !WrotePacketFrames || FlushBatchedDatagrams))) {
//
// Don't have any more space to send.
//
@@ -1477,7 +1577,11 @@ QuicSendFlush(
//
// Final send, if there is anything left over.
//
- QuicPacketBuilderFinalize(&Builder, TRUE);
+ if (!QuicConnIsQMux(Connection)) {
+ QuicPacketBuilderFinalize(&Builder, TRUE);
+ } else {
+ QuicPacketBuilderQMuxFinalize(&Builder, TRUE);
+ }
CXPLAT_DBG_ASSERT(Builder.SendData == NULL);
}
diff --git a/src/core/send.h b/src/core/send.h
index 7bdebf0679..fe46adc974 100644
--- a/src/core/send.h
+++ b/src/core/send.h
@@ -145,6 +145,9 @@ QuicPacketTypeToEncryptLevelV2(
#define QUIC_CONN_SEND_FLAG_ACK_FREQUENCY 0x00008000U
#define QUIC_CONN_SEND_FLAG_BIDI_STREAMS_BLOCKED 0x00010000U
#define QUIC_CONN_SEND_FLAG_UNI_STREAMS_BLOCKED 0x00020000U
+#define QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS 0x00040000U
+#define QUIC_CONN_SEND_FLAG_QX_PING 0x00080000U
+#define QUIC_CONN_SEND_FLAG_QX_PING_RESPONSE 0x00100000U
#define QUIC_CONN_SEND_FLAG_DPLPMTUD 0x80000000U
//
@@ -176,7 +179,10 @@ QuicPacketTypeToEncryptLevelV2(
QUIC_CONN_SEND_FLAG_ACK_FREQUENCY | \
QUIC_CONN_SEND_FLAG_DPLPMTUD | \
QUIC_CONN_SEND_FLAG_BIDI_STREAMS_BLOCKED | \
- QUIC_CONN_SEND_FLAG_UNI_STREAMS_BLOCKED \
+ QUIC_CONN_SEND_FLAG_UNI_STREAMS_BLOCKED | \
+ QUIC_CONN_SEND_FLAG_QX_TRANSPORT_PARAMETERS | \
+ QUIC_CONN_SEND_FLAG_QX_PING | \
+ QUIC_CONN_SEND_FLAG_QX_PING_RESPONSE \
)
//
diff --git a/src/core/sent_packet_metadata.h b/src/core/sent_packet_metadata.h
index 5851694e01..55783f44e6 100644
--- a/src/core/sent_packet_metadata.h
+++ b/src/core/sent_packet_metadata.h
@@ -75,7 +75,7 @@ typedef struct QUIC_SENT_FRAME_METADATA {
//
uint64_t StreamOffset;
uint16_t StreamLength;
- uint16_t Type; // QUIC_FRAME_*
+ QUIC_VAR_INT Type; // QUIC_FRAME_*
uint8_t Flags; // QUIC_SENT_FRAME_FLAG_*
} QUIC_SENT_FRAME_METADATA;
@@ -199,7 +199,7 @@ typedef union QUIC_MAX_SENT_PACKET_METADATA
} QUIC_MAX_SENT_PACKET_METADATA;
CXPLAT_STATIC_ASSERT(
- sizeof(QUIC_MAX_SENT_PACKET_METADATA) < 512,
+ sizeof(QUIC_MAX_SENT_PACKET_METADATA) < 1024,
"Max Send Packet Metadata should be small enough to be allocated on the stack");
//
diff --git a/src/core/stream.h b/src/core/stream.h
index 5a985692e1..fdb9fa74a5 100644
--- a/src/core/stream.h
+++ b/src/core/stream.h
@@ -1017,7 +1017,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
QuicStreamRecv(
_In_ QUIC_STREAM* Stream,
- _In_ QUIC_RX_PACKET* Packet,
+ _In_opt_ QUIC_RX_PACKET* Packet,
_In_ QUIC_FRAME_TYPE FrameType,
_In_ uint16_t BufferLength,
_In_reads_bytes_(BufferLength)
diff --git a/src/core/stream_recv.c b/src/core/stream_recv.c
index 3844262332..9114016963 100644
--- a/src/core/stream_recv.c
+++ b/src/core/stream_recv.c
@@ -615,7 +615,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
QuicStreamRecv(
_In_ QUIC_STREAM* Stream,
- _In_ QUIC_RX_PACKET* Packet,
+ _In_opt_ QUIC_RX_PACKET* Packet,
_In_ QUIC_FRAME_TYPE FrameType,
_In_ uint16_t BufferLength,
_In_reads_bytes_(BufferLength)
@@ -630,7 +630,7 @@ QuicStreamRecv(
StreamReceiveFrame,
"[strm][%p] Processing frame in packet %llu",
Stream,
- Packet->PacketId);
+ Packet ? Packet->PacketId : 0);
switch (FrameType) {
@@ -750,7 +750,9 @@ QuicStreamRecv(
Status =
QuicStreamProcessStreamFrame(
- Stream, Packet->EncryptedWith0Rtt, &Frame);
+ Stream,
+ Packet != NULL ? Packet->EncryptedWith0Rtt : FALSE,
+ &Frame);
break;
}
diff --git a/src/core/stream_send.c b/src/core/stream_send.c
index 0ac7dd1017..eb37dfd00e 100644
--- a/src/core/stream_send.c
+++ b/src/core/stream_send.c
@@ -1103,6 +1103,7 @@ QuicStreamSendWrite(
CXPLAT_DBG_ASSERT(Stream->SendFlags != 0);
CXPLAT_DBG_ASSERT(
+ QuicConnIsQMux(Stream->Connection) ||
Builder->Metadata->Flags.KeyType == QUIC_PACKET_KEY_1_RTT ||
Builder->Metadata->Flags.KeyType == QUIC_PACKET_KEY_0_RTT);
CXPLAT_DBG_ASSERT(QuicStreamAllowedByPeer(Stream));
@@ -1446,7 +1447,8 @@ QuicStreamOnAck(
Offset,
FrameMetadata->Flags);
- if (PacketFlags.KeyType == QUIC_PACKET_KEY_0_RTT &&
+ if (!QuicConnIsQMux(Stream->Connection) &&
+ PacketFlags.KeyType == QUIC_PACKET_KEY_0_RTT &&
Stream->Sent0Rtt < FollowingOffset) {
Stream->Sent0Rtt = FollowingOffset;
QuicTraceLogStreamVerbose(
diff --git a/src/core/transport_params.h b/src/core/transport_params.h
index e510554f85..0117bbce01 100644
--- a/src/core/transport_params.h
+++ b/src/core/transport_params.h
@@ -152,6 +152,8 @@ typedef struct QUIC_TRANSPORT_PARAMETERS {
uint32_t VersionInfoLength;
const uint8_t* VersionInfo;
+ QUIC_VAR_INT MaxRecordSize;
+
} QUIC_TRANSPORT_PARAMETERS;
//
diff --git a/src/core/unittest/SpinFrame.cpp b/src/core/unittest/SpinFrame.cpp
index 0c114bd70e..ad4b02284b 100644
--- a/src/core/unittest/SpinFrame.cpp
+++ b/src/core/unittest/SpinFrame.cpp
@@ -51,9 +51,6 @@ TEST(SpinFrame, SpinFrame1000000)
uint8_t BufferLength = 0;
uint16_t FrameType;
- CXPLAT_STATIC_ASSERT(
- QUIC_FRAME_MAX_SUPPORTED <= (uint64_t)UINT16_MAX,
- "Tests below assumes frames fit in 16-bits");
QuicRangeInitialize(QUIC_MAX_RANGE_DECODE_ACKS, &AckBlocks);
diff --git a/src/generated/linux/crypto_tls.c.clog.h b/src/generated/linux/crypto_tls.c.clog.h
index 21242b0576..b2f1d41de8 100644
--- a/src/generated/linux/crypto_tls.c.clog.h
+++ b/src/generated/linux/crypto_tls.c.clog.h
@@ -619,6 +619,26 @@ tracepoint(CLOG_CRYPTO_TLS_C, EncodeTPTimestamp , arg1, arg3);\
+/*----------------------------------------------------------
+// Decoder Ring for EncodeTPMaxRecordSize
+// [conn][%p] TP: Max Record Size (%llu)
+// QuicTraceLogConnVerbose(
+ EncodeTPMaxRecordSize,
+ Connection,
+ "TP: Max Record Size (%llu)",
+ TransportParams->MaxRecordSize);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = TransportParams->MaxRecordSize = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_EncodeTPMaxRecordSize
+#define _clog_4_ARGS_TRACE_EncodeTPMaxRecordSize(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_CRYPTO_TLS_C, EncodeTPMaxRecordSize , arg1, arg3);\
+
+#endif
+
+
+
+
/*----------------------------------------------------------
// Decoder Ring for EncodeTPTest
// [conn][%p] TP: TEST TP (Type %hu, Length %hu)
@@ -1171,6 +1191,26 @@ tracepoint(CLOG_CRYPTO_TLS_C, DecodeTPReliableReset , arg1);\
+/*----------------------------------------------------------
+// Decoder Ring for DecodeTPMaxRecordSize
+// [conn][%p] TP: Max Record Size (%llu)
+// QuicTraceLogConnVerbose(
+ DecodeTPMaxRecordSize,
+ Connection,
+ "TP: Max Record Size (%llu)",
+ TransportParams->MaxRecordSize);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = TransportParams->MaxRecordSize = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_DecodeTPMaxRecordSize
+#define _clog_4_ARGS_TRACE_DecodeTPMaxRecordSize(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_CRYPTO_TLS_C, DecodeTPMaxRecordSize , arg1, arg3);\
+
+#endif
+
+
+
+
/*----------------------------------------------------------
// Decoder Ring for ConnError
// [conn][%p] ERROR, %s.
diff --git a/src/generated/linux/crypto_tls.c.clog.h.lttng.h b/src/generated/linux/crypto_tls.c.clog.h.lttng.h
index 06eb22974f..f28e6639d2 100644
--- a/src/generated/linux/crypto_tls.c.clog.h.lttng.h
+++ b/src/generated/linux/crypto_tls.c.clog.h.lttng.h
@@ -672,6 +672,29 @@ TRACEPOINT_EVENT(CLOG_CRYPTO_TLS_C, EncodeTPTimestamp,
+/*----------------------------------------------------------
+// Decoder Ring for EncodeTPMaxRecordSize
+// [conn][%p] TP: Max Record Size (%llu)
+// QuicTraceLogConnVerbose(
+ EncodeTPMaxRecordSize,
+ Connection,
+ "TP: Max Record Size (%llu)",
+ TransportParams->MaxRecordSize);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = TransportParams->MaxRecordSize = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_CRYPTO_TLS_C, EncodeTPMaxRecordSize,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned long long, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(uint64_t, arg3, arg3)
+ )
+)
+
+
+
/*----------------------------------------------------------
// Decoder Ring for EncodeTPTest
// [conn][%p] TP: TEST TP (Type %hu, Length %hu)
@@ -1301,6 +1324,29 @@ TRACEPOINT_EVENT(CLOG_CRYPTO_TLS_C, DecodeTPReliableReset,
+/*----------------------------------------------------------
+// Decoder Ring for DecodeTPMaxRecordSize
+// [conn][%p] TP: Max Record Size (%llu)
+// QuicTraceLogConnVerbose(
+ DecodeTPMaxRecordSize,
+ Connection,
+ "TP: Max Record Size (%llu)",
+ TransportParams->MaxRecordSize);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = TransportParams->MaxRecordSize = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_CRYPTO_TLS_C, DecodeTPMaxRecordSize,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned long long, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(uint64_t, arg3, arg3)
+ )
+)
+
+
+
/*----------------------------------------------------------
// Decoder Ring for ConnError
// [conn][%p] ERROR, %s.
diff --git a/src/generated/linux/frame.c.clog.h b/src/generated/linux/frame.c.clog.h
index e8c8b30384..a9060e7410 100644
--- a/src/generated/linux/frame.c.clog.h
+++ b/src/generated/linux/frame.c.clog.h
@@ -1321,6 +1321,98 @@ tracepoint(CLOG_FRAME_C, FrameLogReliableResetStream , arg2, arg3, arg4, arg5, a
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxTransportParametersInvalid
+// [%c][%cX][%llu] QX TRANSPORT_PARAMETERS [Invalid]
+// QuicTraceLogVerbose(
+ FrameLogQxTransportParametersInvalid,
+ "[%c][%cX][%llu] QX TRANSPORT_PARAMETERS [Invalid]",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_FrameLogQxTransportParametersInvalid
+#define _clog_5_ARGS_TRACE_FrameLogQxTransportParametersInvalid(uniqueId, encoded_arg_string, arg2, arg3, arg4)\
+tracepoint(CLOG_FRAME_C, FrameLogQxTransportParametersInvalid , arg2, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxTransportParameters
+// [%c][%cX][%llu] QX TRANSPORT_PARAMETERS
+// QuicTraceLogVerbose(
+ FrameLogQxTransportParameters,
+ "[%c][%cX][%llu] QX TRANSPORT_PARAMETERS",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_FrameLogQxTransportParameters
+#define _clog_5_ARGS_TRACE_FrameLogQxTransportParameters(uniqueId, encoded_arg_string, arg2, arg3, arg4)\
+tracepoint(CLOG_FRAME_C, FrameLogQxTransportParameters , arg2, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxPingInvalid
+// [%c][%cX][%llu] QX PING [Invalid]
+// QuicTraceLogVerbose(
+ FrameLogQxPingInvalid,
+ "[%c][%cX][%llu] QX PING [Invalid]",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_FrameLogQxPingInvalid
+#define _clog_5_ARGS_TRACE_FrameLogQxPingInvalid(uniqueId, encoded_arg_string, arg2, arg3, arg4)\
+tracepoint(CLOG_FRAME_C, FrameLogQxPingInvalid , arg2, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxPing
+// [%c][%cX][%llu] QX PING %hu %llu
+// QuicTraceLogVerbose(
+ FrameLogQxPing,
+ "[%c][%cX][%llu] QX PING %hu %llu",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber,
+ Frame.IsResponse,
+ Frame.SequenceNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+// arg5 = arg5 = Frame.IsResponse = arg5
+// arg6 = arg6 = Frame.SequenceNumber = arg6
+----------------------------------------------------------*/
+#ifndef _clog_7_ARGS_TRACE_FrameLogQxPing
+#define _clog_7_ARGS_TRACE_FrameLogQxPing(uniqueId, encoded_arg_string, arg2, arg3, arg4, arg5, arg6)\
+tracepoint(CLOG_FRAME_C, FrameLogQxPing , arg2, arg3, arg4, arg5, arg6);\
+
+#endif
+
+
+
+
/*----------------------------------------------------------
// Decoder Ring for ConnError
// [conn][%p] ERROR, %s.
diff --git a/src/generated/linux/frame.c.clog.h.lttng.h b/src/generated/linux/frame.c.clog.h.lttng.h
index df916db905..2f94fbe277 100644
--- a/src/generated/linux/frame.c.clog.h.lttng.h
+++ b/src/generated/linux/frame.c.clog.h.lttng.h
@@ -1675,6 +1675,122 @@ TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogReliableResetStream,
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxTransportParametersInvalid
+// [%c][%cX][%llu] QX TRANSPORT_PARAMETERS [Invalid]
+// QuicTraceLogVerbose(
+ FrameLogQxTransportParametersInvalid,
+ "[%c][%cX][%llu] QX TRANSPORT_PARAMETERS [Invalid]",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogQxTransportParametersInvalid,
+ TP_ARGS(
+ unsigned char, arg2,
+ unsigned char, arg3,
+ unsigned long long, arg4),
+ TP_FIELDS(
+ ctf_integer(unsigned char, arg2, arg2)
+ ctf_integer(unsigned char, arg3, arg3)
+ ctf_integer(uint64_t, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxTransportParameters
+// [%c][%cX][%llu] QX TRANSPORT_PARAMETERS
+// QuicTraceLogVerbose(
+ FrameLogQxTransportParameters,
+ "[%c][%cX][%llu] QX TRANSPORT_PARAMETERS",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogQxTransportParameters,
+ TP_ARGS(
+ unsigned char, arg2,
+ unsigned char, arg3,
+ unsigned long long, arg4),
+ TP_FIELDS(
+ ctf_integer(unsigned char, arg2, arg2)
+ ctf_integer(unsigned char, arg3, arg3)
+ ctf_integer(uint64_t, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxPingInvalid
+// [%c][%cX][%llu] QX PING [Invalid]
+// QuicTraceLogVerbose(
+ FrameLogQxPingInvalid,
+ "[%c][%cX][%llu] QX PING [Invalid]",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogQxPingInvalid,
+ TP_ARGS(
+ unsigned char, arg2,
+ unsigned char, arg3,
+ unsigned long long, arg4),
+ TP_FIELDS(
+ ctf_integer(unsigned char, arg2, arg2)
+ ctf_integer(unsigned char, arg3, arg3)
+ ctf_integer(uint64_t, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for FrameLogQxPing
+// [%c][%cX][%llu] QX PING %hu %llu
+// QuicTraceLogVerbose(
+ FrameLogQxPing,
+ "[%c][%cX][%llu] QX PING %hu %llu",
+ PtkConnPre(Connection),
+ PktRxPre(Rx),
+ PacketNumber,
+ Frame.IsResponse,
+ Frame.SequenceNumber);
+// arg2 = arg2 = PtkConnPre(Connection) = arg2
+// arg3 = arg3 = PktRxPre(Rx) = arg3
+// arg4 = arg4 = PacketNumber = arg4
+// arg5 = arg5 = Frame.IsResponse = arg5
+// arg6 = arg6 = Frame.SequenceNumber = arg6
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogQxPing,
+ TP_ARGS(
+ unsigned char, arg2,
+ unsigned char, arg3,
+ unsigned long long, arg4,
+ unsigned short, arg5,
+ unsigned long long, arg6),
+ TP_FIELDS(
+ ctf_integer(unsigned char, arg2, arg2)
+ ctf_integer(unsigned char, arg3, arg3)
+ ctf_integer(uint64_t, arg4, arg4)
+ ctf_integer(unsigned short, arg5, arg5)
+ ctf_integer(uint64_t, arg6, arg6)
+ )
+)
+
+
+
/*----------------------------------------------------------
// Decoder Ring for ConnError
// [conn][%p] ERROR, %s.
diff --git a/src/generated/linux/listener.c.clog.h b/src/generated/linux/listener.c.clog.h
index f66fd36c76..330947a2e4 100644
--- a/src/generated/linux/listener.c.clog.h
+++ b/src/generated/linux/listener.c.clog.h
@@ -259,11 +259,11 @@ tracepoint(CLOG_LISTENER_C, ApiExit );\
// Decoder Ring for ListenerErrorStatus
// [list][%p] ERROR, %u, %s.
// QuicTraceEvent(
- ListenerErrorStatus,
- "[list][%p] ERROR, %u, %s.",
- Listener,
- Status,
- "Get binding");
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "Get binding");
// arg2 = arg2 = Listener = arg2
// arg3 = arg3 = Status = arg3
// arg4 = arg4 = "Get binding" = arg4
diff --git a/src/generated/linux/listener.c.clog.h.lttng.h b/src/generated/linux/listener.c.clog.h.lttng.h
index ab4c4c655e..4c6301bb59 100644
--- a/src/generated/linux/listener.c.clog.h.lttng.h
+++ b/src/generated/linux/listener.c.clog.h.lttng.h
@@ -263,11 +263,11 @@ TRACEPOINT_EVENT(CLOG_LISTENER_C, ApiExit,
// Decoder Ring for ListenerErrorStatus
// [list][%p] ERROR, %u, %s.
// QuicTraceEvent(
- ListenerErrorStatus,
- "[list][%p] ERROR, %u, %s.",
- Listener,
- Status,
- "Get binding");
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "Get binding");
// arg2 = arg2 = Listener = arg2
// arg3 = arg3 = Status = arg3
// arg4 = arg4 = "Get binding" = arg4
diff --git a/src/generated/linux/packet_builder.c.clog.h b/src/generated/linux/packet_builder.c.clog.h
index f7b36614db..f11e7915fc 100644
--- a/src/generated/linux/packet_builder.c.clog.h
+++ b/src/generated/linux/packet_builder.c.clog.h
@@ -33,9 +33,9 @@ extern "C" {
// Decoder Ring for NoSrcCidAvailable
// [conn][%p] No src CID to send with
// QuicTraceLogConnWarning(
- NoSrcCidAvailable,
- Connection,
- "No src CID to send with");
+ NoSrcCidAvailable,
+ Connection,
+ "No src CID to send with");
// arg1 = arg1 = Connection = arg1
----------------------------------------------------------*/
#ifndef _clog_3_ARGS_TRACE_NoSrcCidAvailable
@@ -107,6 +107,28 @@ tracepoint(CLOG_PACKET_BUILDER_C, PacketBuilderSendBatch , arg1, arg3);\
+/*----------------------------------------------------------
+// Decoder Ring for PacketBuilderQMuxSendBatch
+// [conn][%p] Sending batch. %hu datagrams %u bytes
+// QuicTraceLogConnVerbose(
+ PacketBuilderQMuxSendBatch,
+ Builder->Connection,
+ "Sending batch. %hu datagrams %u bytes",
+ (uint16_t)Builder->TotalCountDatagrams,
+ Builder->TotalDatagramsLength);
+// arg1 = arg1 = Builder->Connection = arg1
+// arg3 = arg3 = (uint16_t)Builder->TotalCountDatagrams = arg3
+// arg4 = arg4 = Builder->TotalDatagramsLength = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_PacketBuilderQMuxSendBatch
+#define _clog_5_ARGS_TRACE_PacketBuilderQMuxSendBatch(uniqueId, arg1, encoded_arg_string, arg3, arg4)\
+tracepoint(CLOG_PACKET_BUILDER_C, PacketBuilderQMuxSendBatch , arg1, arg3, arg4);\
+
+#endif
+
+
+
+
/*----------------------------------------------------------
// Decoder Ring for ConnError
// [conn][%p] ERROR, %s.
diff --git a/src/generated/linux/packet_builder.c.clog.h.lttng.h b/src/generated/linux/packet_builder.c.clog.h.lttng.h
index 50314aa9e6..91d8f6aba6 100644
--- a/src/generated/linux/packet_builder.c.clog.h.lttng.h
+++ b/src/generated/linux/packet_builder.c.clog.h.lttng.h
@@ -5,9 +5,9 @@
// Decoder Ring for NoSrcCidAvailable
// [conn][%p] No src CID to send with
// QuicTraceLogConnWarning(
- NoSrcCidAvailable,
- Connection,
- "No src CID to send with");
+ NoSrcCidAvailable,
+ Connection,
+ "No src CID to send with");
// arg1 = arg1 = Connection = arg1
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_PACKET_BUILDER_C, NoSrcCidAvailable,
@@ -89,6 +89,33 @@ TRACEPOINT_EVENT(CLOG_PACKET_BUILDER_C, PacketBuilderSendBatch,
+/*----------------------------------------------------------
+// Decoder Ring for PacketBuilderQMuxSendBatch
+// [conn][%p] Sending batch. %hu datagrams %u bytes
+// QuicTraceLogConnVerbose(
+ PacketBuilderQMuxSendBatch,
+ Builder->Connection,
+ "Sending batch. %hu datagrams %u bytes",
+ (uint16_t)Builder->TotalCountDatagrams,
+ Builder->TotalDatagramsLength);
+// arg1 = arg1 = Builder->Connection = arg1
+// arg3 = arg3 = (uint16_t)Builder->TotalCountDatagrams = arg3
+// arg4 = arg4 = Builder->TotalDatagramsLength = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_PACKET_BUILDER_C, PacketBuilderQMuxSendBatch,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned short, arg3,
+ unsigned int, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned short, arg3, arg3)
+ ctf_integer(unsigned int, arg4, arg4)
+ )
+)
+
+
+
/*----------------------------------------------------------
// Decoder Ring for ConnError
// [conn][%p] ERROR, %s.
diff --git a/src/generated/linux/qmux.c.clog.h b/src/generated/linux/qmux.c.clog.h
new file mode 100644
index 0000000000..8995c2f001
--- /dev/null
+++ b/src/generated/linux/qmux.c.clog.h
@@ -0,0 +1,451 @@
+#ifndef CLOG_DO_NOT_INCLUDE_HEADER
+#include
+#endif
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER CLOG_QMUX_C
+#undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "qmux.c.clog.h.lttng.h"
+#if !defined(DEF_CLOG_QMUX_C) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define DEF_CLOG_QMUX_C
+#include
+#define __int64 __int64_t
+#include "qmux.c.clog.h.lttng.h"
+#endif
+#include
+#ifndef _clog_MACRO_QuicTraceLogVerbose
+#define _clog_MACRO_QuicTraceLogVerbose 1
+#define QuicTraceLogVerbose(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
+#endif
+#ifndef _clog_MACRO_QuicTraceLogConnWarning
+#define _clog_MACRO_QuicTraceLogConnWarning 1
+#define QuicTraceLogConnWarning(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__)))
+#endif
+#ifndef _clog_MACRO_QuicTraceEvent
+#define _clog_MACRO_QuicTraceEvent 1
+#define QuicTraceEvent(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__)))
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*----------------------------------------------------------
+// Decoder Ring for ListenerIndicateNewConnection
+// [list][%p] Indicating NEW_CONNECTION %p
+// QuicTraceLogVerbose(
+ ListenerIndicateNewConnection,
+ "[list][%p] Indicating NEW_CONNECTION %p",
+ Listener,
+ Connection);
+// arg2 = arg2 = Listener = arg2
+// arg3 = arg3 = Connection = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_ListenerIndicateNewConnection
+#define _clog_4_ARGS_TRACE_ListenerIndicateNewConnection(uniqueId, encoded_arg_string, arg2, arg3)\
+tracepoint(CLOG_QMUX_C, ListenerIndicateNewConnection , arg2, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for IgnoreFrameAfterClose
+// [conn][%p] Ignoring frame (%hhu) for already closed stream id = %llu
+// QuicTraceLogConnWarning(
+ IgnoreFrameAfterClose,
+ Connection,
+ "Ignoring frame (%hhu) for already closed stream id = %llu",
+ (uint8_t)FrameType, // This cast is safe because of the switch cases above.
+ StreamId);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = (uint8_t)FrameType = arg3
+// arg4 = arg4 = // This cast is safe because of the switch cases above.
+ StreamId = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_IgnoreFrameAfterClose
+#define _clog_5_ARGS_TRACE_IgnoreFrameAfterClose(uniqueId, arg1, encoded_arg_string, arg3, arg4)\
+tracepoint(CLOG_QMUX_C, IgnoreFrameAfterClose , arg1, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TcpConnected
+// [conn][%p] TCP connected
+// QuicTraceLogConnInfo(
+ TcpConnected,
+ Connection,
+ "TCP connected");
+// arg1 = arg1 = Connection = arg1
+----------------------------------------------------------*/
+#ifndef _clog_3_ARGS_TRACE_TcpConnected
+#define _clog_3_ARGS_TRACE_TcpConnected(uniqueId, arg1, encoded_arg_string)\
+tracepoint(CLOG_QMUX_C, TcpConnected , arg1);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TcpDisconnected
+// [conn][%p] TCP disconnected
+// QuicTraceLogConnInfo(
+ TcpDisconnected,
+ Connection,
+ "TCP disconnected");
+// arg1 = arg1 = Connection = arg1
+----------------------------------------------------------*/
+#ifndef _clog_3_ARGS_TRACE_TcpDisconnected
+#define _clog_3_ARGS_TRACE_TcpDisconnected(uniqueId, arg1, encoded_arg_string)\
+tracepoint(CLOG_QMUX_C, TcpDisconnected , arg1);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TcpDataReceived
+// [conn][%p] TCP data received: %u bytes in %u segments
+// QuicTraceLogConnInfo(
+ TcpDataReceived,
+ Connection,
+ "TCP data received: %u bytes in %u segments",
+ TotalChainByteLength,
+ TotalChainLength);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = TotalChainByteLength = arg3
+// arg4 = arg4 = TotalChainLength = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_TcpDataReceived
+#define _clog_5_ARGS_TRACE_TcpDataReceived(uniqueId, arg1, encoded_arg_string, arg3, arg4)\
+tracepoint(CLOG_QMUX_C, TcpDataReceived , arg1, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TlsHandshakeData
+// [conn][%p] TLS handshake data ready to send, length=%u
+// QuicTraceLogConnVerbose(
+ TlsHandshakeData,
+ Connection,
+ "TLS handshake data ready to send, length=%u",
+ OutputBuffers[i].Length);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = OutputBuffers[i].Length = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_TlsHandshakeData
+#define _clog_4_ARGS_TRACE_TlsHandshakeData(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_QMUX_C, TlsHandshakeData , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for QueueDatagrams
+// [conn][%p] Queuing %u TCP data
+// QuicTraceLogConnVerbose(
+ QueueDatagrams,
+ Connection,
+ "Queuing %u TCP data",
+ RecvDataChainLength);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = RecvDataChainLength = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_QueueDatagrams
+#define _clog_4_ARGS_TRACE_QueueDatagrams(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_QMUX_C, QueueDatagrams , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for IndicateConnected
+// [conn][%p] Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)
+// QuicTraceLogConnVerbose(
+ IndicateConnected,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)",
+ Event.CONNECTED.SessionResumed);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Event.CONNECTED.SessionResumed = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_IndicateConnected
+#define _clog_4_ARGS_TRACE_IndicateConnected(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_QMUX_C, IndicateConnected , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for PeerConnFCBlocked
+// [conn][%p] Peer Connection FC blocked (%llu)
+// QuicTraceLogConnVerbose(
+ PeerConnFCBlocked,
+ Connection,
+ "Peer Connection FC blocked (%llu)",
+ Frame.DataLimit);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Frame.DataLimit = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_PeerConnFCBlocked
+#define _clog_4_ARGS_TRACE_PeerConnFCBlocked(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_QMUX_C, PeerConnFCBlocked , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for PeerStreamFCBlocked
+// [conn][%p] Peer Streams[%hu] FC blocked (%llu)
+// QuicTraceLogConnVerbose(
+ PeerStreamFCBlocked,
+ Connection,
+ "Peer Streams[%hu] FC blocked (%llu)",
+ Frame.BidirectionalStreams,
+ Frame.StreamLimit);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Frame.BidirectionalStreams = arg3
+// arg4 = arg4 = Frame.StreamLimit = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_PeerStreamFCBlocked
+#define _clog_5_ARGS_TRACE_PeerStreamFCBlocked(uniqueId, arg1, encoded_arg_string, arg3, arg4)\
+tracepoint(CLOG_QMUX_C, PeerStreamFCBlocked , arg1, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for IndicatePeerNeedStreamsV2
+// [conn][%p] Indicating QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS type: %s
+// QuicTraceLogConnVerbose(
+ IndicatePeerNeedStreamsV2,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS type: %s",
+ Frame.BidirectionalStreams ? "Bidi" : "Unidi"
+ );
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Frame.BidirectionalStreams ? "Bidi" : "Unidi" = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_IndicatePeerNeedStreamsV2
+#define _clog_4_ARGS_TRACE_IndicatePeerNeedStreamsV2(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_QMUX_C, IndicatePeerNeedStreamsV2 , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for AllocFailure
+// Allocation of '%s' failed. (%llu bytes)
+// QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "connection QMux",
+ sizeof(QUIC_QMUX));
+// arg2 = arg2 = "connection QMux" = arg2
+// arg3 = arg3 = sizeof(QUIC_QMUX) = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_AllocFailure
+#define _clog_4_ARGS_TRACE_AllocFailure(uniqueId, encoded_arg_string, arg2, arg3)\
+tracepoint(CLOG_QMUX_C, AllocFailure , arg2, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnErrorStatus
+// [conn][%p] ERROR, %u, %s.
+// QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "CxPlatTlsInitialize");
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = Status = arg3
+// arg4 = arg4 = "CxPlatTlsInitialize" = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_ConnErrorStatus
+#define _clog_5_ARGS_TRACE_ConnErrorStatus(uniqueId, encoded_arg_string, arg2, arg3, arg4)\
+tracepoint(CLOG_QMUX_C, ConnErrorStatus , arg2, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnError
+// [conn][%p] ERROR, %s.
+// QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Frame type decode failure");
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = "Frame type decode failure" = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_ConnError
+#define _clog_4_ARGS_TRACE_ConnError(uniqueId, encoded_arg_string, arg2, arg3)\
+tracepoint(CLOG_QMUX_C, ConnError , arg2, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnDelayCloseApplicationError
+// [conn][%p] Received APPLICATION_ERROR error, delaying close in expectation of a 1-RTT CONNECTION_CLOSE frame.
+// QuicTraceEvent(
+ ConnDelayCloseApplicationError,
+ "[conn][%p] Received APPLICATION_ERROR error, delaying close in expectation of a 1-RTT CONNECTION_CLOSE frame.",
+ Connection);
+// arg2 = arg2 = Connection = arg2
+----------------------------------------------------------*/
+#ifndef _clog_3_ARGS_TRACE_ConnDelayCloseApplicationError
+#define _clog_3_ARGS_TRACE_ConnDelayCloseApplicationError(uniqueId, encoded_arg_string, arg2)\
+tracepoint(CLOG_QMUX_C, ConnDelayCloseApplicationError , arg2);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnRecvTcpData
+// [conn][%p] Recv %u TCP data, %u bytes
+// QuicTraceEvent(
+ ConnRecvTcpData,
+ "[conn][%p] Recv %u TCP data, %u bytes",
+ Connection,
+ RecvDataChainCount,
+ RecvDataChainByteCount);
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = RecvDataChainCount = arg3
+// arg4 = arg4 = RecvDataChainByteCount = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_ConnRecvTcpData
+#define _clog_5_ARGS_TRACE_ConnRecvTcpData(uniqueId, encoded_arg_string, arg2, arg3, arg4)\
+tracepoint(CLOG_QMUX_C, ConnRecvTcpData , arg2, arg3, arg4);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnHandshakeComplete
+// [conn][%p] Handshake complete
+// QuicTraceEvent(
+ ConnHandshakeComplete,
+ "[conn][%p] Handshake complete",
+ Connection);
+// arg2 = arg2 = Connection = arg2
+----------------------------------------------------------*/
+#ifndef _clog_3_ARGS_TRACE_ConnHandshakeComplete
+#define _clog_3_ARGS_TRACE_ConnHandshakeComplete(uniqueId, encoded_arg_string, arg2)\
+tracepoint(CLOG_QMUX_C, ConnHandshakeComplete , arg2);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnRecvPacket
+// [conn][%p][RX] %hu bytes
+// QuicTraceEvent(
+ ConnRecvPacket,
+ "[conn][%p][RX] %hu bytes",
+ Connection,
+ (uint16_t)RecordLength);
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = (uint16_t)RecordLength = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_ConnRecvPacket
+#define _clog_4_ARGS_TRACE_ConnRecvPacket(uniqueId, encoded_arg_string, arg2, arg3)\
+tracepoint(CLOG_QMUX_C, ConnRecvPacket , arg2, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnEarlyDataStatus
+// [conn][%p] Early data %s
+// QuicTraceEvent(
+ ConnEarlyDataStatus,
+ "[conn][%p] Early data %s",
+ Connection,
+ "accepted");
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = "accepted" = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_ConnEarlyDataStatus
+#define _clog_4_ARGS_TRACE_ConnEarlyDataStatus(uniqueId, encoded_arg_string, arg2, arg3)\
+tracepoint(CLOG_QMUX_C, ConnEarlyDataStatus , arg2, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ListenerErrorStatus
+// [list][%p] ERROR, %u, %s.
+// QuicTraceEvent(
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "NEW_CONNECTION callback");
+// arg2 = arg2 = Listener = arg2
+// arg3 = arg3 = Status = arg3
+// arg4 = arg4 = "NEW_CONNECTION callback" = arg4
+----------------------------------------------------------*/
+#ifndef _clog_5_ARGS_TRACE_ListenerErrorStatus
+#define _clog_5_ARGS_TRACE_ListenerErrorStatus(uniqueId, encoded_arg_string, arg2, arg3, arg4)\
+tracepoint(CLOG_QMUX_C, ListenerErrorStatus , arg2, arg3, arg4);\
+
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#ifdef CLOG_INLINE_IMPLEMENTATION
+#include "quic.clog_qmux.c.clog.h.c"
+#endif
diff --git a/src/generated/linux/qmux.c.clog.h.lttng.h b/src/generated/linux/qmux.c.clog.h.lttng.h
new file mode 100644
index 0000000000..e23066d77b
--- /dev/null
+++ b/src/generated/linux/qmux.c.clog.h.lttng.h
@@ -0,0 +1,470 @@
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ListenerIndicateNewConnection
+// [list][%p] Indicating NEW_CONNECTION %p
+// QuicTraceLogVerbose(
+ ListenerIndicateNewConnection,
+ "[list][%p] Indicating NEW_CONNECTION %p",
+ Listener,
+ Connection);
+// arg2 = arg2 = Listener = arg2
+// arg3 = arg3 = Connection = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ListenerIndicateNewConnection,
+ TP_ARGS(
+ const void *, arg2,
+ const void *, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_integer_hex(uint64_t, arg3, (uint64_t)arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for IgnoreFrameAfterClose
+// [conn][%p] Ignoring frame (%hhu) for already closed stream id = %llu
+// QuicTraceLogConnWarning(
+ IgnoreFrameAfterClose,
+ Connection,
+ "Ignoring frame (%hhu) for already closed stream id = %llu",
+ (uint8_t)FrameType, // This cast is safe because of the switch cases above.
+ StreamId);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = (uint8_t)FrameType = arg3
+// arg4 = arg4 = // This cast is safe because of the switch cases above.
+ StreamId = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, IgnoreFrameAfterClose,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned char, arg3,
+ unsigned long long, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned char, arg3, arg3)
+ ctf_integer(uint64_t, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TcpConnected
+// [conn][%p] TCP connected
+// QuicTraceLogConnInfo(
+ TcpConnected,
+ Connection,
+ "TCP connected");
+// arg1 = arg1 = Connection = arg1
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, TcpConnected,
+ TP_ARGS(
+ const void *, arg1),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TcpDisconnected
+// [conn][%p] TCP disconnected
+// QuicTraceLogConnInfo(
+ TcpDisconnected,
+ Connection,
+ "TCP disconnected");
+// arg1 = arg1 = Connection = arg1
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, TcpDisconnected,
+ TP_ARGS(
+ const void *, arg1),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TcpDataReceived
+// [conn][%p] TCP data received: %u bytes in %u segments
+// QuicTraceLogConnInfo(
+ TcpDataReceived,
+ Connection,
+ "TCP data received: %u bytes in %u segments",
+ TotalChainByteLength,
+ TotalChainLength);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = TotalChainByteLength = arg3
+// arg4 = arg4 = TotalChainLength = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, TcpDataReceived,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned int, arg3,
+ unsigned int, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned int, arg3, arg3)
+ ctf_integer(unsigned int, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for TlsHandshakeData
+// [conn][%p] TLS handshake data ready to send, length=%u
+// QuicTraceLogConnVerbose(
+ TlsHandshakeData,
+ Connection,
+ "TLS handshake data ready to send, length=%u",
+ OutputBuffers[i].Length);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = OutputBuffers[i].Length = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, TlsHandshakeData,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for QueueDatagrams
+// [conn][%p] Queuing %u TCP data
+// QuicTraceLogConnVerbose(
+ QueueDatagrams,
+ Connection,
+ "Queuing %u TCP data",
+ RecvDataChainLength);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = RecvDataChainLength = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, QueueDatagrams,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for IndicateConnected
+// [conn][%p] Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)
+// QuicTraceLogConnVerbose(
+ IndicateConnected,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_CONNECTED (Resume=%hhu)",
+ Event.CONNECTED.SessionResumed);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Event.CONNECTED.SessionResumed = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, IndicateConnected,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned char, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned char, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for PeerConnFCBlocked
+// [conn][%p] Peer Connection FC blocked (%llu)
+// QuicTraceLogConnVerbose(
+ PeerConnFCBlocked,
+ Connection,
+ "Peer Connection FC blocked (%llu)",
+ Frame.DataLimit);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Frame.DataLimit = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, PeerConnFCBlocked,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned long long, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(uint64_t, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for PeerStreamFCBlocked
+// [conn][%p] Peer Streams[%hu] FC blocked (%llu)
+// QuicTraceLogConnVerbose(
+ PeerStreamFCBlocked,
+ Connection,
+ "Peer Streams[%hu] FC blocked (%llu)",
+ Frame.BidirectionalStreams,
+ Frame.StreamLimit);
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Frame.BidirectionalStreams = arg3
+// arg4 = arg4 = Frame.StreamLimit = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, PeerStreamFCBlocked,
+ TP_ARGS(
+ const void *, arg1,
+ unsigned short, arg3,
+ unsigned long long, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(unsigned short, arg3, arg3)
+ ctf_integer(uint64_t, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for IndicatePeerNeedStreamsV2
+// [conn][%p] Indicating QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS type: %s
+// QuicTraceLogConnVerbose(
+ IndicatePeerNeedStreamsV2,
+ Connection,
+ "Indicating QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS type: %s",
+ Frame.BidirectionalStreams ? "Bidi" : "Unidi"
+ );
+// arg1 = arg1 = Connection = arg1
+// arg3 = arg3 = Frame.BidirectionalStreams ? "Bidi" : "Unidi" = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, IndicatePeerNeedStreamsV2,
+ TP_ARGS(
+ const void *, arg1,
+ const char *, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_string(arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for AllocFailure
+// Allocation of '%s' failed. (%llu bytes)
+// QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "connection QMux",
+ sizeof(QUIC_QMUX));
+// arg2 = arg2 = "connection QMux" = arg2
+// arg3 = arg3 = sizeof(QUIC_QMUX) = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, AllocFailure,
+ TP_ARGS(
+ const char *, arg2,
+ unsigned long long, arg3),
+ TP_FIELDS(
+ ctf_string(arg2, arg2)
+ ctf_integer(uint64_t, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnErrorStatus
+// [conn][%p] ERROR, %u, %s.
+// QuicTraceEvent(
+ ConnErrorStatus,
+ "[conn][%p] ERROR, %u, %s.",
+ Connection,
+ Status,
+ "CxPlatTlsInitialize");
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = Status = arg3
+// arg4 = arg4 = "CxPlatTlsInitialize" = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnErrorStatus,
+ TP_ARGS(
+ const void *, arg2,
+ unsigned int, arg3,
+ const char *, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_integer(unsigned int, arg3, arg3)
+ ctf_string(arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnError
+// [conn][%p] ERROR, %s.
+// QuicTraceEvent(
+ ConnError,
+ "[conn][%p] ERROR, %s.",
+ Connection,
+ "Frame type decode failure");
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = "Frame type decode failure" = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnError,
+ TP_ARGS(
+ const void *, arg2,
+ const char *, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_string(arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnDelayCloseApplicationError
+// [conn][%p] Received APPLICATION_ERROR error, delaying close in expectation of a 1-RTT CONNECTION_CLOSE frame.
+// QuicTraceEvent(
+ ConnDelayCloseApplicationError,
+ "[conn][%p] Received APPLICATION_ERROR error, delaying close in expectation of a 1-RTT CONNECTION_CLOSE frame.",
+ Connection);
+// arg2 = arg2 = Connection = arg2
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnDelayCloseApplicationError,
+ TP_ARGS(
+ const void *, arg2),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnRecvTcpData
+// [conn][%p] Recv %u TCP data, %u bytes
+// QuicTraceEvent(
+ ConnRecvTcpData,
+ "[conn][%p] Recv %u TCP data, %u bytes",
+ Connection,
+ RecvDataChainCount,
+ RecvDataChainByteCount);
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = RecvDataChainCount = arg3
+// arg4 = arg4 = RecvDataChainByteCount = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnRecvTcpData,
+ TP_ARGS(
+ const void *, arg2,
+ unsigned int, arg3,
+ unsigned int, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_integer(unsigned int, arg3, arg3)
+ ctf_integer(unsigned int, arg4, arg4)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnHandshakeComplete
+// [conn][%p] Handshake complete
+// QuicTraceEvent(
+ ConnHandshakeComplete,
+ "[conn][%p] Handshake complete",
+ Connection);
+// arg2 = arg2 = Connection = arg2
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnHandshakeComplete,
+ TP_ARGS(
+ const void *, arg2),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnRecvPacket
+// [conn][%p][RX] %hu bytes
+// QuicTraceEvent(
+ ConnRecvPacket,
+ "[conn][%p][RX] %hu bytes",
+ Connection,
+ (uint16_t)RecordLength);
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = (uint16_t)RecordLength = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnRecvPacket,
+ TP_ARGS(
+ const void *, arg2,
+ unsigned short, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_integer(unsigned short, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ConnEarlyDataStatus
+// [conn][%p] Early data %s
+// QuicTraceEvent(
+ ConnEarlyDataStatus,
+ "[conn][%p] Early data %s",
+ Connection,
+ "accepted");
+// arg2 = arg2 = Connection = arg2
+// arg3 = arg3 = "accepted" = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ConnEarlyDataStatus,
+ TP_ARGS(
+ const void *, arg2,
+ const char *, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_string(arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for ListenerErrorStatus
+// [list][%p] ERROR, %u, %s.
+// QuicTraceEvent(
+ ListenerErrorStatus,
+ "[list][%p] ERROR, %u, %s.",
+ Listener,
+ Status,
+ "NEW_CONNECTION callback");
+// arg2 = arg2 = Listener = arg2
+// arg3 = arg3 = Status = arg3
+// arg4 = arg4 = "NEW_CONNECTION callback" = arg4
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_QMUX_C, ListenerErrorStatus,
+ TP_ARGS(
+ const void *, arg2,
+ unsigned int, arg3,
+ const char *, arg4),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2)
+ ctf_integer(unsigned int, arg3, arg3)
+ ctf_string(arg4, arg4)
+ )
+)
diff --git a/src/generated/linux/quic.clog_qmux.c.clog.h.c b/src/generated/linux/quic.clog_qmux.c.clog.h.c
new file mode 100644
index 0000000000..23c3907d06
--- /dev/null
+++ b/src/generated/linux/quic.clog_qmux.c.clog.h.c
@@ -0,0 +1,7 @@
+#include
+#ifdef BUILDING_TRACEPOINT_PROVIDER
+#define TRACEPOINT_CREATE_PROBES
+#else
+#define TRACEPOINT_DEFINE
+#endif
+#include "qmux.c.clog.h"
diff --git a/src/generated/linux/send.c.clog.h b/src/generated/linux/send.c.clog.h
index 40f498ec22..cb5a059ead 100644
--- a/src/generated/linux/send.c.clog.h
+++ b/src/generated/linux/send.c.clog.h
@@ -79,9 +79,9 @@ tracepoint(CLOG_SEND_C, ClearSendFlags , arg1, arg3);\
// Decoder Ring for EcnValidationUnknown
// [conn][%p] ECN unknown.
// QuicTraceLogConnInfo(
- EcnValidationUnknown,
- Connection,
- "ECN unknown.");
+ EcnValidationUnknown,
+ Connection,
+ "ECN unknown.");
// arg1 = arg1 = Connection = arg1
----------------------------------------------------------*/
#ifndef _clog_3_ARGS_TRACE_EcnValidationUnknown
diff --git a/src/generated/linux/send.c.clog.h.lttng.h b/src/generated/linux/send.c.clog.h.lttng.h
index abf45f41f4..f46918c665 100644
--- a/src/generated/linux/send.c.clog.h.lttng.h
+++ b/src/generated/linux/send.c.clog.h.lttng.h
@@ -55,9 +55,9 @@ TRACEPOINT_EVENT(CLOG_SEND_C, ClearSendFlags,
// Decoder Ring for EcnValidationUnknown
// [conn][%p] ECN unknown.
// QuicTraceLogConnInfo(
- EcnValidationUnknown,
- Connection,
- "ECN unknown.");
+ EcnValidationUnknown,
+ Connection,
+ "ECN unknown.");
// arg1 = arg1 = Connection = arg1
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_SEND_C, EcnValidationUnknown,
diff --git a/src/generated/linux/stream_recv.c.clog.h b/src/generated/linux/stream_recv.c.clog.h
index 57e768e3f0..186802a2e0 100644
--- a/src/generated/linux/stream_recv.c.clog.h
+++ b/src/generated/linux/stream_recv.c.clog.h
@@ -500,9 +500,9 @@ tracepoint(CLOG_STREAM_RECV_C, StreamError , arg2, arg3);\
StreamReceiveFrame,
"[strm][%p] Processing frame in packet %llu",
Stream,
- Packet->PacketId);
+ Packet ? Packet->PacketId : 0);
// arg2 = arg2 = Stream = arg2
-// arg3 = arg3 = Packet->PacketId = arg3
+// arg3 = arg3 = Packet ? Packet->PacketId : 0 = arg3
----------------------------------------------------------*/
#ifndef _clog_4_ARGS_TRACE_StreamReceiveFrame
#define _clog_4_ARGS_TRACE_StreamReceiveFrame(uniqueId, encoded_arg_string, arg2, arg3)\
diff --git a/src/generated/linux/stream_recv.c.clog.h.lttng.h b/src/generated/linux/stream_recv.c.clog.h.lttng.h
index f750fe9c00..6a70ee964b 100644
--- a/src/generated/linux/stream_recv.c.clog.h.lttng.h
+++ b/src/generated/linux/stream_recv.c.clog.h.lttng.h
@@ -520,9 +520,9 @@ TRACEPOINT_EVENT(CLOG_STREAM_RECV_C, StreamError,
StreamReceiveFrame,
"[strm][%p] Processing frame in packet %llu",
Stream,
- Packet->PacketId);
+ Packet ? Packet->PacketId : 0);
// arg2 = arg2 = Stream = arg2
-// arg3 = arg3 = Packet->PacketId = arg3
+// arg3 = arg3 = Packet ? Packet->PacketId : 0 = arg3
----------------------------------------------------------*/
TRACEPOINT_EVENT(CLOG_STREAM_RECV_C, StreamReceiveFrame,
TP_ARGS(
diff --git a/src/generated/linux/tls_openssl.c.clog.h b/src/generated/linux/tls_openssl.c.clog.h
index c630102b5e..d109741961 100644
--- a/src/generated/linux/tls_openssl.c.clog.h
+++ b/src/generated/linux/tls_openssl.c.clog.h
@@ -153,6 +153,86 @@ tracepoint(CLOG_TLS_OPENSSL_C, OpenSslNoMatchingAlpn , arg1);\
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOWriteError
+// [conn][%p] BIO_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslBIOWriteError
+#define _clog_4_ARGS_TRACE_OpenSslBIOWriteError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_OPENSSL_C, OpenSslBIOWriteError , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOReadError
+// [conn][%p] BIO_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslBIOReadError
+#define _clog_4_ARGS_TRACE_OpenSslBIOReadError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_OPENSSL_C, OpenSslBIOReadError , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLWriteError
+// [conn][%p] SSL_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLWriteError,
+ TlsContext->Connection,
+ "SSL_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslSSLWriteError
+#define _clog_4_ARGS_TRACE_OpenSslSSLWriteError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_OPENSSL_C, OpenSslSSLWriteError , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLReadError
+// [conn][%p] SSL_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLReadError,
+ TlsContext->Connection,
+ "SSL_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslSSLReadError
+#define _clog_4_ARGS_TRACE_OpenSslSSLReadError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_OPENSSL_C, OpenSslSSLReadError , arg1, arg3);\
+
+#endif
+
+
+
+
/*----------------------------------------------------------
// Decoder Ring for OpenSslHandshakeDataStart
// [conn][%p] Writing Handshake data starts at %u
diff --git a/src/generated/linux/tls_openssl.c.clog.h.lttng.h b/src/generated/linux/tls_openssl.c.clog.h.lttng.h
index a259121e02..8d476dd54a 100644
--- a/src/generated/linux/tls_openssl.c.clog.h.lttng.h
+++ b/src/generated/linux/tls_openssl.c.clog.h.lttng.h
@@ -139,6 +139,98 @@ TRACEPOINT_EVENT(CLOG_TLS_OPENSSL_C, OpenSslNoMatchingAlpn,
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOWriteError
+// [conn][%p] BIO_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_OPENSSL_C, OpenSslBIOWriteError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOReadError
+// [conn][%p] BIO_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_OPENSSL_C, OpenSslBIOReadError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLWriteError
+// [conn][%p] SSL_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLWriteError,
+ TlsContext->Connection,
+ "SSL_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_OPENSSL_C, OpenSslSSLWriteError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLReadError
+// [conn][%p] SSL_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLReadError,
+ TlsContext->Connection,
+ "SSL_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_OPENSSL_C, OpenSslSSLReadError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
/*----------------------------------------------------------
// Decoder Ring for OpenSslHandshakeDataStart
// [conn][%p] Writing Handshake data starts at %u
diff --git a/src/generated/linux/tls_quictls.c.clog.h b/src/generated/linux/tls_quictls.c.clog.h
index 80c1158ffa..70cbcb8521 100644
--- a/src/generated/linux/tls_quictls.c.clog.h
+++ b/src/generated/linux/tls_quictls.c.clog.h
@@ -191,6 +191,86 @@ tracepoint(CLOG_TLS_QUICTLS_C, OpenSslMissingTransportParameters , arg1);\
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOWriteError
+// [conn][%p] BIO_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslBIOWriteError
+#define _clog_4_ARGS_TRACE_OpenSslBIOWriteError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_QUICTLS_C, OpenSslBIOWriteError , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOReadError
+// [conn][%p] BIO_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslBIOReadError
+#define _clog_4_ARGS_TRACE_OpenSslBIOReadError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_QUICTLS_C, OpenSslBIOReadError , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLWriteError
+// [conn][%p] SSL_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLWriteError,
+ TlsContext->Connection,
+ "SSL_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslSSLWriteError
+#define _clog_4_ARGS_TRACE_OpenSslSSLWriteError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_QUICTLS_C, OpenSslSSLWriteError , arg1, arg3);\
+
+#endif
+
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLReadError
+// [conn][%p] SSL_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLReadError,
+ TlsContext->Connection,
+ "SSL_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+#ifndef _clog_4_ARGS_TRACE_OpenSslSSLReadError
+#define _clog_4_ARGS_TRACE_OpenSslSSLReadError(uniqueId, arg1, encoded_arg_string, arg3)\
+tracepoint(CLOG_TLS_QUICTLS_C, OpenSslSSLReadError , arg1, arg3);\
+
+#endif
+
+
+
+
/*----------------------------------------------------------
// Decoder Ring for OpenSslHandshakeDataStart
// [conn][%p] Writing Handshake data starts at %u
diff --git a/src/generated/linux/tls_quictls.c.clog.h.lttng.h b/src/generated/linux/tls_quictls.c.clog.h.lttng.h
index c51146dc61..65ddfc461c 100644
--- a/src/generated/linux/tls_quictls.c.clog.h.lttng.h
+++ b/src/generated/linux/tls_quictls.c.clog.h.lttng.h
@@ -181,6 +181,98 @@ TRACEPOINT_EVENT(CLOG_TLS_QUICTLS_C, OpenSslMissingTransportParameters,
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOWriteError
+// [conn][%p] BIO_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_QUICTLS_C, OpenSslBIOWriteError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslBIOReadError
+// [conn][%p] BIO_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_QUICTLS_C, OpenSslBIOReadError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLWriteError
+// [conn][%p] SSL_write failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLWriteError,
+ TlsContext->Connection,
+ "SSL_write failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_QUICTLS_C, OpenSslSSLWriteError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
+/*----------------------------------------------------------
+// Decoder Ring for OpenSslSSLReadError
+// [conn][%p] SSL_read failed, error: %d
+// QuicTraceLogConnError(
+ OpenSslSSLReadError,
+ TlsContext->Connection,
+ "SSL_read failed, error: %d",
+ Err);
+// arg1 = arg1 = TlsContext->Connection = arg1
+// arg3 = arg3 = Err = arg3
+----------------------------------------------------------*/
+TRACEPOINT_EVENT(CLOG_TLS_QUICTLS_C, OpenSslSSLReadError,
+ TP_ARGS(
+ const void *, arg1,
+ int, arg3),
+ TP_FIELDS(
+ ctf_integer_hex(uint64_t, arg1, (uint64_t)arg1)
+ ctf_integer(int, arg3, arg3)
+ )
+)
+
+
+
/*----------------------------------------------------------
// Decoder Ring for OpenSslHandshakeDataStart
// [conn][%p] Writing Handshake data starts at %u
diff --git a/src/inc/msquic.h b/src/inc/msquic.h
index a60a58892c..3dbe811040 100644
--- a/src/inc/msquic.h
+++ b/src/inc/msquic.h
@@ -1882,6 +1882,11 @@ typedef struct QUIC_API_TABLE {
QUIC_EXECUTION_POLL_FN ExecutionPoll; // Available from v2.5
#endif // _KERNEL_MODE
QUIC_REGISTRATION_CLOSE2_FN RegistrationClose2; // Available from v2.6
+ QUIC_CONNECTION_OPEN_FN ConnectionQmuxOpen;
+ QUIC_CONNECTION_OPEN_IN_PARTITION_FN
+ ConnectionQmuxOpenInPartition;
+ QUIC_LISTENER_OPEN_FN ListenerQmuxOpen;
+
#endif // QUIC_API_ENABLE_PREVIEW_FEATURES
} QUIC_API_TABLE;
diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp
index 0e9b558af0..2d0cbb9e2e 100644
--- a/src/inc/msquic.hpp
+++ b/src/inc/msquic.hpp
@@ -1014,6 +1014,42 @@ struct MsQuicListener {
}
}
+#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
+ MsQuicListener(
+ _In_ const MsQuicRegistration& Registration,
+ _In_ bool IsQmux,
+ _In_ MsQuicCleanUpMode CleanUpMode,
+ _In_ MsQuicListenerCallback* Callback,
+ _In_ void* Context = nullptr
+ ) noexcept : CleanUpMode(CleanUpMode), Callback(Callback), Context(Context) {
+ if (!Registration.IsValid()) {
+ InitStatus = Registration.GetInitStatus();
+ return;
+ }
+ if (!IsQmux) {
+ if (QUIC_FAILED(
+ InitStatus =
+ MsQuic->ListenerOpen(
+ Registration,
+ (QUIC_LISTENER_CALLBACK_HANDLER)MsQuicCallback,
+ this,
+ &Handle))) {
+ Handle = nullptr;
+ }
+ } else {
+ if (QUIC_FAILED(
+ InitStatus =
+ MsQuic->ListenerQmuxOpen(
+ Registration,
+ (QUIC_LISTENER_CALLBACK_HANDLER)MsQuicCallback,
+ this,
+ &Handle))) {
+ Handle = nullptr;
+ }
+ }
+ }
+#endif // QUIC_API_ENABLE_PREVIEW_FEATURES
+
~MsQuicListener() noexcept {
Close();
}
@@ -1207,6 +1243,42 @@ struct MsQuicConnection {
}
}
+#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
+ MsQuicConnection(
+ _In_ const MsQuicRegistration& Registration,
+ _In_ bool IsQmux,
+ _In_ MsQuicCleanUpMode CleanUpMode = CleanUpManual,
+ _In_ MsQuicConnectionCallback* Callback = NoOpCallback,
+ _In_ void* Context = nullptr
+ ) noexcept : CleanUpMode(CleanUpMode), Callback(Callback), Context(Context) {
+ if (!Registration.IsValid()) {
+ InitStatus = Registration.GetInitStatus();
+ return;
+ }
+ if (!IsQmux) {
+ if (QUIC_FAILED(
+ InitStatus =
+ MsQuic->ConnectionOpen(
+ Registration,
+ (QUIC_CONNECTION_CALLBACK_HANDLER)MsQuicCallback,
+ this,
+ &Handle))) {
+ Handle = nullptr;
+ }
+ } else {
+ if (QUIC_FAILED(
+ InitStatus =
+ MsQuic->ConnectionQmuxOpen(
+ Registration,
+ (QUIC_CONNECTION_CALLBACK_HANDLER)MsQuicCallback,
+ this,
+ &Handle))) {
+ Handle = nullptr;
+ }
+ }
+ }
+#endif // QUIC_API_ENABLE_PREVIEW_FEATURES
+
MsQuicConnection(
_In_ HQUIC ConnectionHandle,
_In_ MsQuicCleanUpMode CleanUpMode,
diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h
index edb38047a1..d79f6bcd97 100644
--- a/src/inc/quic_platform.h
+++ b/src/inc/quic_platform.h
@@ -161,6 +161,10 @@ typedef struct CXPLAT_SLIST_ENTRY {
#define QUIC_POOL_DATAPATH_RSS_CONFIG 'F4cQ' // Qc4F - QUIC Datapath RSS configuration
#define QUIC_POOL_TLS_AUX_DATA '05cQ' // Qc50 - QUIC TLS Backing Aux data
#define QUIC_POOL_TLS_RECORD_ENTRY '15cQ' // Qc51 - QUIC TLS Backing Record storage
+#define QUIC_POOL_CONN_QMUX '25cQ' // Qc52 - QUIC connection QMux
+#define QUIC_POOL_QMUX_RECV_BUFFER '35cQ' // Qc53 - QUIC Qmux Receive Buffer
+#define QUIC_POOL_QMUX_EARLY_DATA_BUFFER '45cQ' // Qc54 - QUIC Qmux Early Data Buffer
+#define QUIC_POOL_TLS_EARLY_DATA_BUFFER '55cQ' // Qc55 - QUIC TLS Early Data Buffer
typedef enum CXPLAT_THREAD_FLAGS {
CXPLAT_THREAD_FLAG_NONE = 0x0000,
diff --git a/src/inc/quic_tls.h b/src/inc/quic_tls.h
index d4913a845c..296d524267 100644
--- a/src/inc/quic_tls.h
+++ b/src/inc/quic_tls.h
@@ -143,6 +143,8 @@ typedef struct CXPLAT_TLS_CALLBACKS {
//
typedef struct CXPLAT_TLS_CONFIG {
+ BOOLEAN IsQMux;
+
BOOLEAN IsServer;
//
@@ -212,6 +214,8 @@ typedef enum CXPLAT_TLS_RESULT_FLAGS {
CXPLAT_TLS_RESULT_EARLY_DATA_ACCEPT = 0x0010, // The server accepted the early (0-RTT) data.
CXPLAT_TLS_RESULT_EARLY_DATA_REJECT = 0x0020, // The server rejected the early (0-RTT) data.
CXPLAT_TLS_RESULT_HANDSHAKE_COMPLETE = 0x0040, // Handshake complete.
+ CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL = 0x0080, // Provided buffer was too small for the data.
+ CXPLAT_TLS_RESULT_RENEGOTIATE = 0x0100, // Renegotiation requested by the peer (Used internally to schannel).
CXPLAT_TLS_RESULT_ERROR = 0x8000 // An error occured.
} CXPLAT_TLS_RESULT_FLAGS;
@@ -331,9 +335,48 @@ typedef struct CXPLAT_TLS_PROCESS_STATE {
//
const uint8_t* ClientAlpnList;
uint16_t ClientAlpnListLength;
+
+ BOOLEAN ReadEarlyData;
+ BOOLEAN ReadEarlyDataSuccess;
+
+ //
+ // Total written length in Buffer.
+ //
+ uint32_t EarlyDataBufferLength;
+
+ //
+ // Total allocation length of Buffer.
+ //
+ uint32_t EarlyDataBufferAllocLength;
+
+ //
+ // Holds the early data. Use CXPLAT_ALLOC_NONPAGED and CXPLAT_FREE
+ // to allocate and free the memory.
+ //
+ uint8_t* EarlyDataBuffer;
+
+ uint8_t Count;
} CXPLAT_TLS_PROCESS_STATE;
+typedef struct CXPLAT_TLS_ENCRYPT_BUFFER {
+
+ uint8_t* Base;
+ size_t Capacity;
+
+ size_t DataOffset;
+ size_t DataLength;
+
+} CXPLAT_TLS_ENCRYPT_BUFFER;
+
+
+typedef struct CXPLAT_TLS_RECORD_OVERHEAD {
+
+ size_t MaxHeader;
+ size_t MaxTrailer;
+
+} CXPLAT_TLS_RECORD_OVERHEAD;
+
typedef
_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(CXPLAT_SEC_CONFIG_CREATE_COMPLETE)
@@ -437,6 +480,54 @@ CxPlatTlsProcessData(
_Inout_ CXPLAT_TLS_PROCESS_STATE* State
);
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsHandshake(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_ CXPLAT_TLS_DATA_TYPE DataType,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_ QUIC_BUFFER* OutputBuffers,
+ _Inout_ uint32_t OutputBuffersCount,
+ _Inout_ CXPLAT_TLS_PROCESS_STATE* State
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsWriteEarlyData(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ size_t * InputBufferLength
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsEncrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Inout_ CXPLAT_TLS_ENCRYPT_BUFFER* Buffer
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsDecrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_updates_bytes_opt_(*OutputBufferLength)
+ uint8_t* OutputBuffer,
+ _Inout_ uint32_t* OutputBufferLength
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+CxPlatTlsGetRecordOverhead(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Out_ CXPLAT_TLS_RECORD_OVERHEAD* Overhead
+ );
+
//
// Sets a Security Configuration parameter.
//
diff --git a/src/manifest/MsQuicEtw.man b/src/manifest/MsQuicEtw.man
index 5087dec9e6..c164c44455 100644
--- a/src/manifest/MsQuicEtw.man
+++ b/src/manifest/MsQuicEtw.man
@@ -2276,6 +2276,26 @@
name="Count2"
/>
+
+
+
+
+
+
+
+
@@ -3449,6 +3469,33 @@
template="tid_CONN"
value="5195"
/>
+
+
+
+
+
+
IsQMux) {
+ //
+ // QUIC already parsed and picked the ALPN to use and set it in the
+ // NegotiatedAlpn variable.
+ //
- CXPLAT_DBG_ASSERT(TlsContext->State->NegotiatedAlpn != NULL);
- *OutLen = TlsContext->State->NegotiatedAlpn[0];
- *Out = TlsContext->State->NegotiatedAlpn + 1;
+ CXPLAT_DBG_ASSERT(TlsContext->State->NegotiatedAlpn != NULL);
+ *OutLen = TlsContext->State->NegotiatedAlpn[0];
+ *Out = TlsContext->State->NegotiatedAlpn + 1;
+ return SSL_TLSEXT_ERR_OK;
+ } else {
+ const uint8_t* AlpnList = TlsContext->AlpnBuffer;
+ uint16_t AlpnListLength = TlsContext->AlpnBufferLength;
+
+ //
+ // We want to respect the server's ALPN preference order (i.e. Listener) and
+ // not the client's. So we loop over every ALPN in the listener and then see
+ // if there is a match in the client's list.
+ //
- return SSL_TLSEXT_ERR_OK;
+ while (AlpnListLength != 0) {
+ CXPLAT_ANALYSIS_ASSUME(AlpnList[0] + 1 <= AlpnListLength);
+ const uint8_t* Result =
+ CxPlatTlsAlpnFindInList(
+ (uint16_t)InLen,
+ In,
+ AlpnList[0],
+ AlpnList + 1);
+ if (Result != NULL) {
+ *Out = AlpnList + 1;
+ *OutLen = AlpnList[0];
+ TlsContext->State->NegotiatedAlpn = AlpnList;
+ return SSL_TLSEXT_ERR_OK;
+ }
+ AlpnListLength -= AlpnList[0] + 1;
+ AlpnList += AlpnList[0] + 1;
+ }
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
}
//
@@ -1198,7 +1235,7 @@ CxPlatTlsClientHelloCallback(
const uint8_t* TransportParams;
size_t TransportParamLen;
- if (!SSL_client_hello_get0_ext(
+ if (!TlsContext->IsQMux && !SSL_client_hello_get0_ext(
Ssl,
TlsContext->QuicTpExtType,
&TransportParams,
@@ -2420,6 +2457,136 @@ static long FreeBioAuxData(BIO *B, int Oper,
return Ret;
}
+static int Hexval(char c)
+{
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return c - 'a' + 10;
+ if ('A' <= c && c <= 'F') return c - 'A' + 10;
+ return -1;
+}
+
+static int HexToBytes(const char *hex, uint8_t *out, size_t outlen)
+{
+ size_t i = 0;
+
+ while (hex[0] && hex[1]) {
+ int hi = Hexval(hex[0]);
+ int lo = Hexval(hex[1]);
+ if (hi < 0 || lo < 0 || i >= outlen)
+ return -1;
+
+ out[i++] = ((uint8_t)hi << 4) | (uint8_t)lo;
+ hex += 2;
+ }
+ return (int)i;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+static
+void
+CxPlatTlsKeyLogCallback(const SSL* Ssl, const char* Line)
+{
+ CXPLAT_TLS* TlsContext = (CXPLAT_TLS*)SSL_get_app_data(Ssl);
+ char Label[64];
+ char RandomHex[65];
+ char SecretHex[129];
+
+ if (TlsContext->TlsSecrets == NULL) {
+ return;
+ }
+
+#pragma warning(push)
+#pragma warning(disable : 4996) // sscanf is safe here because the format string limits the number of characters read, preventing buffer overflows.
+ if (sscanf(Line, "%63s %64s %128s", Label, RandomHex, SecretHex) != 3) {
+ return;
+ }
+#pragma warning(pop)
+
+ if (memcmp(Label, "CLIENT_HANDSHAKE_TRAFFIC_SECRET", sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ClientHandshakeTrafficSecret) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ClientHandshakeTrafficSecret, sizeof(TlsContext->TlsSecrets->ClientHandshakeTrafficSecret));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientHandshakeTrafficSecret = TRUE;
+ }
+ }
+
+ if (memcmp(Label, "SERVER_HANDSHAKE_TRAFFIC_SECRET", sizeof("SERVER_HANDSHAKE_TRAFFIC_SECRET") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ServerHandshakeTrafficSecret) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ServerHandshakeTrafficSecret, sizeof(TlsContext->TlsSecrets->ServerHandshakeTrafficSecret));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ServerHandshakeTrafficSecret = TRUE;
+ }
+ }
+
+ if (memcmp(Label, "CLIENT_TRAFFIC_SECRET_0", sizeof("CLIENT_TRAFFIC_SECRET_0") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ClientTrafficSecret0) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ClientTrafficSecret0, sizeof(TlsContext->TlsSecrets->ClientTrafficSecret0));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientTrafficSecret0 = TRUE;
+ }
+ }
+
+ if (memcmp(Label, "SERVER_TRAFFIC_SECRET_0", sizeof("SERVER_TRAFFIC_SECRET_0") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ServerTrafficSecret0) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ServerTrafficSecret0, sizeof(TlsContext->TlsSecrets->ServerTrafficSecret0));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ServerTrafficSecret0 = TRUE;
+ }
+ }
+}
+
//
// @brief Initializes a new QUIC TLS context using OpenSSL.
//
@@ -2472,10 +2639,8 @@ CxPlatTlsInitialize(
CXPLAT_TLS* TlsContext = NULL;
uint16_t ServerNameLength = 0;
UNREFERENCED_PARAMETER(State);
- struct AUX_DATA *AData;
- BIO *ossl_bio = NULL;
- CXPLAT_DBG_ASSERT(Config->HkdfLabels);
+ CXPLAT_DBG_ASSERT(Config->IsQMux || Config->HkdfLabels);
if (Config->SecConfig == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
goto Exit;
@@ -2494,6 +2659,7 @@ CxPlatTlsInitialize(
CxPlatZeroMemory(TlsContext, sizeof(CXPLAT_TLS));
+ TlsContext->IsQMux = Config->IsQMux;
TlsContext->Connection = Config->Connection;
TlsContext->HkdfLabels = Config->HkdfLabels;
TlsContext->IsServer = Config->IsServer;
@@ -2538,6 +2704,9 @@ CxPlatTlsInitialize(
}
}
+ if (TlsContext->IsQMux) {
+ SSL_CTX_set_keylog_callback(TlsContext->SecConfig->SSLCtx, CxPlatTlsKeyLogCallback);
+ }
//
// Create a SSL object for the connection.
//
@@ -2565,46 +2734,87 @@ CxPlatTlsInitialize(
//
SSL_set1_groups_list(TlsContext->Ssl, "secp256r1:x25519");
- if (!SSL_set_quic_tls_cbs(TlsContext->Ssl, OpenSslQuicDispatch, NULL)) {
- QuicTraceEvent(
- TlsError,
- "[ tls][%p] ERROR, %s. ",
- TlsContext->Connection,
- "SSL_set_quic_tls_cbs failed");
- Status = QUIC_STATUS_INTERNAL_ERROR;
- goto Exit;
- }
+ if (!TlsContext->IsQMux) {
+ if (!SSL_set_quic_tls_cbs(TlsContext->Ssl, OpenSslQuicDispatch, NULL)) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s. ",
+ TlsContext->Connection,
+ "SSL_set_quic_tls_cbs failed");
+ Status = QUIC_STATUS_INTERNAL_ERROR;
+ goto Exit;
+ }
- //
- // Note This has to happen after the SSL_set_quic_tls_cbs call
- // as it installs null bios for us
- //
- AData = CXPLAT_ALLOC_NONPAGED(sizeof(struct AUX_DATA), QUIC_POOL_TLS_AUX_DATA);
- if (AData == NULL) {
- QuicTraceEvent(
- AllocFailure,
- "Allocation of '%s' failed. (%llu bytes)",
- "Adata",
- sizeof(struct AUX_DATA));
- Status = QUIC_STATUS_OUT_OF_MEMORY;
- goto Exit;
- }
- memset(AData, 0, sizeof(struct AUX_DATA));
- CxPlatListInitializeHead(&AData->RecordList);
- ossl_bio = BIO_new(BIO_s_null());
- if (ossl_bio == NULL) {
- QuicTraceEvent(
- LibraryError,
- "[ lib] ERROR, %s.",
- "Unable to allocate BIO");
- Status = QUIC_STATUS_OUT_OF_MEMORY;
- goto Exit;
- }
- BIO_set_app_data(ossl_bio, AData);
- BIO_set_callback_ex(ossl_bio, FreeBioAuxData);
- SSL_set_bio(TlsContext->Ssl, ossl_bio, ossl_bio);
+ //
+ // Note This has to happen after the SSL_set_quic_tls_cbs call
+ // as it installs null bios for us
+ //
+ struct AUX_DATA *AData = CXPLAT_ALLOC_NONPAGED(sizeof(struct AUX_DATA), QUIC_POOL_TLS_AUX_DATA);
+ if (AData == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "Adata",
+ sizeof(struct AUX_DATA));
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
+ memset(AData, 0, sizeof(struct AUX_DATA));
+ CxPlatListInitializeHead(&AData->RecordList);
+ BIO *ossl_bio = BIO_new(BIO_s_null());
+ if (ossl_bio == NULL) {
+ QuicTraceEvent(
+ LibraryError,
+ "[ lib] ERROR, %s.",
+ "Unable to allocate BIO");
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
+ BIO_set_app_data(ossl_bio, AData);
+ BIO_set_callback_ex(ossl_bio, FreeBioAuxData);
+ SSL_set_bio(TlsContext->Ssl, ossl_bio, ossl_bio);
- SSL_set_app_data(TlsContext->Ssl, TlsContext);
+ SSL_set_app_data(TlsContext->Ssl, TlsContext);
+
+ if (SSL_set_quic_tls_transport_params(
+ TlsContext->Ssl,
+ Config->LocalTPBuffer,
+ Config->LocalTPLength) != 1) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "SSL_set_quic_transport_params failed");
+ Status = QUIC_STATUS_TLS_ERROR;
+ goto Exit;
+ }
+ AData->Tp = Config->LocalTPBuffer;
+ } else {
+ TlsContext->rbio = BIO_new(BIO_s_mem());
+ if (TlsContext->rbio == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "BIO_new failed");
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
+
+ TlsContext->wbio = BIO_new(BIO_s_mem());
+ if (TlsContext->wbio == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "BIO_new failed");
+ BIO_free(TlsContext->rbio);
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
+ SSL_set_bio(TlsContext->Ssl, TlsContext->rbio, TlsContext->wbio);
+ SSL_set_app_data(TlsContext->Ssl, TlsContext);
+ }
if (Config->IsServer) {
SSL_set_accept_state(TlsContext->Ssl);
@@ -2659,24 +2869,11 @@ CxPlatTlsInitialize(
}
}
- if (Config->IsServer || (Config->ResumptionTicketLength != 0)) {
+ if (!Config->IsQMux && (Config->IsServer || (Config->ResumptionTicketLength != 0))) {
SSL_set_quic_tls_early_data_enabled(TlsContext->Ssl, 1);
}
}
- if (SSL_set_quic_tls_transport_params(
- TlsContext->Ssl,
- Config->LocalTPBuffer,
- Config->LocalTPLength) != 1) {
- QuicTraceEvent(
- TlsError,
- "[ tls][%p] ERROR, %s.",
- TlsContext->Connection,
- "SSL_set_quic_transport_params failed");
- Status = QUIC_STATUS_TLS_ERROR;
- goto Exit;
- }
- AData->Tp = Config->LocalTPBuffer;
if (Config->ResumptionTicketBuffer) {
CXPLAT_FREE(Config->ResumptionTicketBuffer, QUIC_POOL_CRYPTO_RESUMPTION_TICKET);
@@ -3101,6 +3298,7 @@ CxPlatTlsProcessData(
RECORD_ENTRY *entry;
CXPLAT_LIST_ENTRY* lentry;
size_t Consumed = 0;
+ CXPLAT_DBG_ASSERT(!TlsContext->IsQMux);
CXPLAT_DBG_ASSERT(Buffer != NULL || *BufferLength == 0);
TlsContext->State = State;
@@ -3347,6 +3545,583 @@ CxPlatTlsProcessData(
return TlsContext->ResultFlags;
}
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsHandshake(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_ CXPLAT_TLS_DATA_TYPE DataType,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_ QUIC_BUFFER* OutputBuffers,
+ _Inout_ uint32_t OutputBuffersCount,
+ _Inout_ CXPLAT_TLS_PROCESS_STATE* State
+ )
+{
+ int Ret;
+ CXPLAT_DBG_ASSERT(InputBuffer != NULL || *InputBufferLength == 0);
+ CXPLAT_DBG_ASSERT(OutputBuffers != NULL && OutputBuffersCount > 0);
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+
+ TlsContext->State = State;
+ TlsContext->ResultFlags = 0;
+
+ if (DataType == CXPLAT_TLS_TICKET_DATA) {
+ QuicTraceLogConnVerbose(
+ OpenSslSendTicketData,
+ TlsContext->Connection,
+ "Sending ticket data, %u bytes",
+ *InputBufferLength);
+
+ SSL_SESSION* Session = SSL_get_session(TlsContext->Ssl);
+ if (Session == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "SSL_get_session failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ if (!SSL_SESSION_set1_ticket_appdata(Session, InputBuffer, *InputBufferLength)) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ ERR_get_error(),
+ "SSL_SESSION_set1_ticket_appdata failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+
+ if (!SSL_new_session_ticket(TlsContext->Ssl)) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ ERR_get_error(),
+ "SSL_new_session_ticket failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ Ret = SSL_do_handshake(TlsContext->Ssl);
+ if (Ret != 1) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SSL_get_error(TlsContext->Ssl, Ret),
+ "SSL_do_handshake failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+
+ goto Send;
+ }
+
+ if (InputBuffer == NULL && *InputBufferLength == 0) {
+ goto Handshake;
+ }
+
+ if (InputBuffer != NULL && *InputBufferLength > 0) {
+ Ret = BIO_write(TlsContext->rbio, InputBuffer, (int)*InputBufferLength);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ }
+ *InputBufferLength = Ret;
+ }
+
+ // After feeding incoming TLS records into OpenSSL via BIO_write(rbio),
+ // do NOT immediately advance the handshake.
+ //
+ // Instead, prioritize reading TLS 1.3 early data and repeatedly call
+ // SSL_read_early_data() until one of the following conditions is reached:
+ //
+ // (1) SSL_READ_EARLY_DATA_FINISH is returned:
+ // -> No early data is present (or early data is already complete).
+ //
+ // (2) SSL_READ_EARLY_DATA_SUCCESS is returned at least once, and then
+ // SSL_read_early_data() returns SSL_ERROR_WANT_READ:
+ // -> All available early data has been fully consumed.
+ //
+ // (3) SSL_read_early_data() returns SSL_ERROR_WANT_WRITE:
+ // -> OpenSSL requires the server handshake flight (e.g. ServerHello)
+ // to be sent in order to make further progress.
+ //
+ // Handling:
+ // - In case (1), exit the early data phase and transition to the normal
+ // handshake phase by calling SSL_do_handshake() and draining wbio.
+ //
+ // - In cases (2) and (3), advance the handshake by exactly one step
+ // (SSL_do_handshake() + BIO_read(wbio)) to satisfy OpenSSL's state
+ // requirements, but do not complete the handshake.
+ // When additional TLS records are received, resume calling
+ // SSL_read_early_data() with priority.
+ //
+ // This ensures that early data is never skipped or lost, while preventing
+ // the TLS handshake from completing prematurely.
+
+ if (State->ReadEarlyData) {
+ CXPLAT_DBG_ASSERT(TlsContext->IsServer);
+ CXPLAT_DBG_ASSERT(State->EarlyDataBuffer != NULL && State->EarlyDataBufferAllocLength > 0);
+ size_t ReadLength;
+ do {
+ if (State->EarlyDataBufferLength >= State->EarlyDataBufferAllocLength) {
+ uint32_t NewEarlyDataBufferAllocLength = State->EarlyDataBufferAllocLength * 2;
+ uint8_t* NewEarlyDataBuffer =
+ CXPLAT_ALLOC_NONPAGED(
+ NewEarlyDataBufferAllocLength,
+ QUIC_POOL_TLS_EARLY_DATA_BUFFER);
+ if (NewEarlyDataBuffer == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS early data buffer",
+ NewEarlyDataBufferAllocLength);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ memcpy(NewEarlyDataBuffer, State->EarlyDataBuffer, State->EarlyDataBufferLength);
+ CXPLAT_FREE(State->EarlyDataBuffer, QUIC_POOL_TLS_EARLY_DATA_BUFFER);
+ State->EarlyDataBuffer = NewEarlyDataBuffer;
+ State->EarlyDataBufferAllocLength = NewEarlyDataBufferAllocLength;
+ }
+ ReadLength = 0;
+ Ret =
+ SSL_read_early_data(
+ TlsContext->Ssl,
+ State->EarlyDataBuffer + State->EarlyDataBufferLength,
+ State->EarlyDataBufferAllocLength - State->EarlyDataBufferLength,
+ &ReadLength);
+ State->EarlyDataBufferLength += (uint32_t)ReadLength;
+ switch (Ret) {
+ case SSL_READ_EARLY_DATA_FINISH:
+ // No more early data is available, or early data is already complete.
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ case SSL_READ_EARLY_DATA_SUCCESS:
+ // Successfully read some early data.
+ // Stay in this state until SSL_read_early_data() indicates completion.
+ State->ReadEarlyDataSuccess = TRUE;
+ break;
+ case SSL_READ_EARLY_DATA_ERROR: {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ switch (Err) {
+ case SSL_ERROR_WANT_READ:
+ if (State->ReadEarlyDataSuccess) {
+ // All available early data has been consumed,
+ // so execute the handshake to move past the early data phase.
+ goto Handshake;
+ }
+ goto Exit;
+
+ case SSL_ERROR_WANT_WRITE:
+ // OpenSSL requires the server handshake flight to be sent before
+ // more early data can be processed.
+ goto Handshake;
+
+ case SSL_ERROR_SSL: {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "SSL_read_early_data error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ }
+
+ default:
+ QuicTraceLogConnError(
+ OpenSslHandshakeError,
+ TlsContext->Connection,
+ "SSL_read_early_data error: %d",
+ Err);
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ }
+ }
+ default:
+ QuicTraceLogConnError(
+ OpenSslHandshakeError,
+ TlsContext->Connection,
+ "Unexpected SSL_read_early_data return value: %d",
+ Ret);
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ }
+ } while (Ret == SSL_READ_EARLY_DATA_SUCCESS && ReadLength > 0);
+ }
+
+Handshake:
+ if (!State->HandshakeComplete) {
+ Ret = SSL_do_handshake(TlsContext->Ssl);
+ if (Ret <= 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ switch (Err) {
+ case SSL_ERROR_WANT_READ:
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ break;
+
+ case SSL_ERROR_SSL: {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "TLS handshake error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+
+ default:
+ QuicTraceLogConnError(
+ OpenSslHandshakeError,
+ TlsContext->Connection,
+ "TLS handshake error: %d",
+ Err);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ }
+
+ if (SSL_is_init_finished(TlsContext->Ssl)) {
+ QuicTraceLogConnInfo(
+ OpenSslHandshakeComplete,
+ TlsContext->Connection,
+ "TLS Handshake complete");
+ State->HandshakeComplete = TRUE;
+ if (SSL_session_reused(TlsContext->Ssl)) {
+ QuicTraceLogConnInfo(
+ OpenSslHandshakeResumed,
+ TlsContext->Connection,
+ "TLS Handshake resumed");
+ State->SessionResumed = TRUE;
+ }
+ if (!TlsContext->IsServer) {
+ int EarlyDataStatus = SSL_get_early_data_status(TlsContext->Ssl);
+ if (EarlyDataStatus == SSL_EARLY_DATA_ACCEPTED) {
+ State->EarlyDataState = CXPLAT_TLS_EARLY_DATA_ACCEPTED;
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_EARLY_DATA_ACCEPT;
+
+ } else if (EarlyDataStatus == SSL_EARLY_DATA_REJECTED) {
+ State->EarlyDataState = CXPLAT_TLS_EARLY_DATA_REJECTED;
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_EARLY_DATA_REJECT;
+ }
+ }
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_HANDSHAKE_COMPLETE;
+
+ if (!TlsContext->IsServer) {
+ const uint8_t* NegotiatedAlpn;
+ uint32_t NegotiatedAlpnLength;
+ SSL_get0_alpn_selected(TlsContext->Ssl, &NegotiatedAlpn, &NegotiatedAlpnLength);
+ if (NegotiatedAlpnLength == 0) {
+ QuicTraceLogConnError(
+ OpenSslAlpnNegotiationFailure,
+ TlsContext->Connection,
+ "Failed to negotiate ALPN");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ if (NegotiatedAlpnLength > UINT8_MAX) {
+ QuicTraceLogConnError(
+ OpenSslInvalidAlpnLength,
+ TlsContext->Connection,
+ "Invalid negotiated ALPN length");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ TlsContext->State->NegotiatedAlpn =
+ CxPlatTlsAlpnFindInList(
+ TlsContext->AlpnBufferLength,
+ TlsContext->AlpnBuffer,
+ (uint8_t)NegotiatedAlpnLength,
+ NegotiatedAlpn);
+ if (TlsContext->State->NegotiatedAlpn == NULL) {
+ QuicTraceLogConnError(
+ OpenSslNoMatchingAlpn,
+ TlsContext->Connection,
+ "Failed to find a matching ALPN");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ } else if ((TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED) &&
+ !TlsContext->PeerCertReceived) {
+ QUIC_STATUS ValidationResult =
+ (!(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION) &&
+ (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION ||
+ TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION)) ?
+ QUIC_STATUS_CERT_NO_CERT :
+ QUIC_STATUS_SUCCESS;
+
+ if (!TlsContext->SecConfig->Callbacks.CertificateReceived(
+ TlsContext->Connection,
+ NULL,
+ NULL,
+ 0,
+ ValidationResult)) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "Indicate null certificate received failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ TlsContext->State->AlertCode = CXPLAT_TLS_ALERT_CODE_REQUIRED_CERTIFICATE;
+ goto Exit;
+ }
+ }
+ }
+ }
+
+Send:
+ uint32_t OutputBufferCount = 0;
+ QUIC_BUFFER *OutputBuffer = &OutputBuffers[OutputBufferCount];
+ size_t OutputBufferOffset = 0;
+ while (BIO_pending(TlsContext->wbio)) {
+ if (OutputBufferOffset == OutputBuffer->Length) {
+ OutputBufferCount++;
+ if (OutputBufferCount == OutputBuffersCount) {
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL;
+ break;
+ }
+ OutputBuffer = &OutputBuffers[OutputBufferCount];
+ OutputBufferOffset = 0;
+ }
+ Ret =
+ BIO_read(
+ TlsContext->wbio,
+ OutputBuffer->Buffer + OutputBufferOffset,
+ (int)(OutputBuffer->Length - OutputBufferOffset));
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ if (Err == SSL_ERROR_SSL) {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "BIO_read error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ } else {
+ QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+ }
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ } else {
+ OutputBufferOffset += (size_t)Ret;
+ OutputBuffer->Length = (uint32_t)OutputBufferOffset;
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_DATA;
+ }
+ }
+
+ for (uint32_t i = OutputBufferCount + 1; i < OutputBuffersCount; ++i) {
+ OutputBuffers[i].Length = 0;
+ }
+
+Exit:
+
+ return TlsContext->ResultFlags;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsWriteEarlyData(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ size_t * InputBufferLength
+ )
+{
+ CXPLAT_DBG_ASSERT(InputBuffer != NULL && *InputBufferLength > 0);
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+ int Ret =
+ SSL_write_early_data(
+ TlsContext->Ssl,
+ InputBuffer,
+ *InputBufferLength,
+ InputBufferLength);
+ return Ret == 1;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsEncrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Inout_ CXPLAT_TLS_ENCRYPT_BUFFER* Buffer
+ )
+{
+ int Ret;
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+ CXPLAT_TLS_RECORD_OVERHEAD Overhead;
+ CxPlatTlsGetRecordOverhead(TlsContext, &Overhead);
+
+ if (Buffer->Capacity < Overhead.MaxHeader + Buffer->DataLength + Overhead.MaxTrailer) {
+ // Not enough room for encryption overhead, which can be up to 5 bytes for the TLS record
+ // header and up to 512 bytes for the trailer.
+ return FALSE;
+ }
+
+ if (Buffer->DataLength > 16384) {
+ // TLS record plaintext cannot exceed 2^14 bytes.
+ return FALSE;
+ }
+
+ const uint8_t* Plaintext =
+ Buffer->Base + Buffer->DataOffset;
+ size_t PlaintextLength = Buffer->DataLength;
+
+ Ret = SSL_write(
+ TlsContext->Ssl,
+ Plaintext,
+ (int)PlaintextLength);
+ if (Ret <= 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ if (Err == SSL_ERROR_SSL) {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "TLS handshake error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ } else {
+ QuicTraceLogConnError(
+ OpenSslSSLWriteError,
+ TlsContext->Connection,
+ "SSL_write failed, error: %d",
+ Err);
+ }
+ return FALSE;
+ }
+
+ size_t Offset = 0;
+ Buffer->DataLength = 0;
+ while (BIO_pending(TlsContext->wbio) > 0) {
+ CXPLAT_DBG_ASSERT(Offset <= Buffer->Capacity);
+ Ret = BIO_read(TlsContext->wbio, Buffer->Base + Offset, (int)(Buffer->Capacity - Offset));
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+ return FALSE;
+ } else if (Ret == 0) {
+ break;
+ }
+ Offset += (size_t)Ret;
+ Buffer->DataLength += (size_t)Ret;
+ }
+
+ return TRUE;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsDecrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_updates_bytes_opt_(*OutputBufferLength)
+ uint8_t* OutputBuffer,
+ _Inout_ uint32_t* OutputBufferLength
+ )
+{
+ CXPLAT_TLS_RESULT_FLAGS Result = 0;
+ int Ret;
+ CXPLAT_DBG_ASSERT(InputBuffer != NULL || *InputBufferLength == 0);
+ CXPLAT_DBG_ASSERT(OutputBuffer != NULL && *OutputBufferLength > 0);
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+
+ if (InputBuffer != NULL && *InputBufferLength > 0) {
+ Ret = BIO_write(TlsContext->rbio, InputBuffer, (int)*InputBufferLength);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ return Result;
+ }
+ *InputBufferLength = Ret;
+ }
+
+ size_t OutputBufferOffset = 0;
+ do {
+ Ret =
+ SSL_read(TlsContext->Ssl,
+ OutputBuffer + OutputBufferOffset,
+ (int)*OutputBufferLength - (int)OutputBufferOffset);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+
+ if (Err == SSL_ERROR_WANT_READ || Err == SSL_ERROR_WANT_WRITE) {
+ break;
+ } else if (Err == SSL_ERROR_ZERO_RETURN) {
+ break;
+ } else {
+ QuicTraceLogConnError(
+ OpenSslSSLReadError,
+ TlsContext->Connection,
+ "SSL_read failed, error: %d",
+ Err);
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ return Result;
+ }
+ }
+ OutputBufferOffset += (size_t)Ret;
+ } while (Ret > 0 && OutputBufferOffset < *OutputBufferLength);
+ *OutputBufferLength = (uint32_t)OutputBufferOffset;
+
+ return Result;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+CxPlatTlsGetRecordOverhead(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Out_ CXPLAT_TLS_RECORD_OVERHEAD* Overhead
+ )
+{
+ UNREFERENCED_PARAMETER(TlsContext);
+ Overhead->MaxHeader = 5; // TLS record header is always 5 bytes
+ Overhead->MaxTrailer = 512; // Maximum possible trailer length for TLS 1.3 with AEAD ciphers
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
CxPlatSecConfigParamSet(
diff --git a/src/platform/tls_quictls.c b/src/platform/tls_quictls.c
index 0cbbcbb61c..261d6b34f9 100644
--- a/src/platform/tls_quictls.c
+++ b/src/platform/tls_quictls.c
@@ -95,6 +95,11 @@ typedef struct CXPLAT_TLS {
//
const QUIC_HKDF_LABELS* HkdfLabels;
+ //
+ // Indicates if this TLS session is for a QMUX connection.
+ //
+ BOOLEAN IsQMux : 1;
+
//
// Indicates if this context belongs to server side or client side
// connection.
@@ -151,6 +156,10 @@ typedef struct CXPLAT_TLS {
//
QUIC_TLS_SECRETS* TlsSecrets;
+ BIO *rbio;
+
+ BIO *wbio;
+
} CXPLAT_TLS;
//
@@ -200,22 +209,51 @@ CxPlatTlsAlpnSelectCallback(
_In_ void *Arg
)
{
- UNREFERENCED_PARAMETER(In);
- UNREFERENCED_PARAMETER(InLen);
UNREFERENCED_PARAMETER(Arg);
-
CXPLAT_TLS* TlsContext = SSL_get_app_data(Ssl);
- //
- // QUIC already parsed and picked the ALPN to use and set it in the
- // NegotiatedAlpn variable.
- //
+ if (!TlsContext->IsQMux) {
+ UNREFERENCED_PARAMETER(In);
+ UNREFERENCED_PARAMETER(InLen);
+ //
+ // QUIC already parsed and picked the ALPN to use and set it in the
+ // NegotiatedAlpn variable.
+ //
- CXPLAT_DBG_ASSERT(TlsContext->State->NegotiatedAlpn != NULL);
- *OutLen = TlsContext->State->NegotiatedAlpn[0];
- *Out = TlsContext->State->NegotiatedAlpn + 1;
+ CXPLAT_DBG_ASSERT(TlsContext->State->NegotiatedAlpn != NULL);
+ *OutLen = TlsContext->State->NegotiatedAlpn[0];
+ *Out = TlsContext->State->NegotiatedAlpn + 1;
+ return SSL_TLSEXT_ERR_OK;
+ } else {
+ const uint8_t* AlpnList = TlsContext->AlpnBuffer;
+ uint16_t AlpnListLength = TlsContext->AlpnBufferLength;
- return SSL_TLSEXT_ERR_OK;
+ //
+ // We want to respect the server's ALPN preference order (i.e. Listener) and
+ // not the client's. So we loop over every ALPN in the listener and then see
+ // if there is a match in the client's list.
+ //
+
+ while (AlpnListLength != 0) {
+ CXPLAT_ANALYSIS_ASSUME(AlpnList[0] + 1 <= AlpnListLength);
+ const uint8_t* Result =
+ CxPlatTlsAlpnFindInList(
+ (uint16_t)InLen,
+ In,
+ AlpnList[0],
+ AlpnList + 1);
+ if (Result != NULL) {
+ *Out = AlpnList + 1;
+ *OutLen = AlpnList[0];
+ TlsContext->State->NegotiatedAlpn = AlpnList;
+ return SSL_TLSEXT_ERR_OK;
+ }
+ AlpnListLength -= AlpnList[0] + 1;
+ AlpnList += AlpnList[0] + 1;
+ }
+ *OutLen = 0;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
}
static
@@ -683,7 +721,7 @@ CxPlatTlsClientHelloCallback(
const uint8_t* TransportParams;
size_t TransportParamLen;
- if (!SSL_client_hello_get0_ext(
+ if (!TlsContext->IsQMux && !SSL_client_hello_get0_ext(
Ssl,
TlsContext->QuicTpExtType,
&TransportParams,
@@ -1225,17 +1263,6 @@ CxPlatTlsSecConfigCreate(
}
}
- Ret = SSL_CTX_set_quic_method(SecurityConfig->SSLCtx, &OpenSslQuicCallbacks);
- if (Ret != 1) {
- QuicTraceEvent(
- LibraryErrorStatus,
- "[ lib] ERROR, %u, %s.",
- ERR_get_error(),
- "SSL_CTX_set_quic_method failed");
- Status = QUIC_STATUS_TLS_ERROR;
- goto Exit;
- }
-
if ((CredConfigFlags & QUIC_CREDENTIAL_FLAG_CLIENT) &&
!(TlsCredFlags & CXPLAT_TLS_CREDENTIAL_FLAG_DISABLE_RESUMPTION)) {
SSL_CTX_set_session_cache_mode(
@@ -1647,6 +1674,136 @@ CxPlatTlsSecConfigSetTicketKeys(
return QUIC_STATUS_SUCCESS;
}
+static int Hexval(char c)
+{
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return c - 'a' + 10;
+ if ('A' <= c && c <= 'F') return c - 'A' + 10;
+ return -1;
+}
+
+static int HexToBytes(const char *hex, uint8_t *out, size_t outlen)
+{
+ size_t i = 0;
+
+ while (hex[0] && hex[1]) {
+ int hi = Hexval(hex[0]);
+ int lo = Hexval(hex[1]);
+ if (hi < 0 || lo < 0 || i >= outlen)
+ return -1;
+
+ out[i++] = ((uint8_t)hi << 4) | (uint8_t)lo;
+ hex += 2;
+ }
+ return (int)i;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+static
+void
+CxPlatTlsKeyLogCallback(const SSL* Ssl, const char* Line)
+{
+ CXPLAT_TLS* TlsContext = (CXPLAT_TLS*)SSL_get_app_data(Ssl);
+ char Label[64];
+ char RandomHex[65];
+ char SecretHex[129];
+
+ if (TlsContext->TlsSecrets == NULL) {
+ return;
+ }
+
+#pragma warning(push)
+#pragma warning(disable : 4996) // sscanf is safe here because the format string limits the number of characters read, preventing buffer overflows.
+ if (sscanf(Line, "%63s %64s %128s", Label, RandomHex, SecretHex) != 3) {
+ return;
+ }
+#pragma warning(pop)
+
+ if (memcmp(Label, "CLIENT_HANDSHAKE_TRAFFIC_SECRET", sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ClientHandshakeTrafficSecret) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ClientHandshakeTrafficSecret, sizeof(TlsContext->TlsSecrets->ClientHandshakeTrafficSecret));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientHandshakeTrafficSecret = TRUE;
+ }
+ }
+
+ if (memcmp(Label, "SERVER_HANDSHAKE_TRAFFIC_SECRET", sizeof("SERVER_HANDSHAKE_TRAFFIC_SECRET") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ServerHandshakeTrafficSecret) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ServerHandshakeTrafficSecret, sizeof(TlsContext->TlsSecrets->ServerHandshakeTrafficSecret));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ServerHandshakeTrafficSecret = TRUE;
+ }
+ }
+
+ if (memcmp(Label, "CLIENT_TRAFFIC_SECRET_0", sizeof("CLIENT_TRAFFIC_SECRET_0") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ClientTrafficSecret0) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ClientTrafficSecret0, sizeof(TlsContext->TlsSecrets->ClientTrafficSecret0));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientTrafficSecret0 = TRUE;
+ }
+ }
+
+ if (memcmp(Label, "SERVER_TRAFFIC_SECRET_0", sizeof("SERVER_TRAFFIC_SECRET_0") - 1) == 0) {
+ if (!TlsContext->TlsSecrets->IsSet.ClientRandom) {
+ if (HexToBytes(RandomHex, TlsContext->TlsSecrets->ClientRandom, sizeof(TlsContext->TlsSecrets->ClientRandom)) != sizeof(TlsContext->TlsSecrets->ClientRandom)) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ClientRandom = TRUE;
+ }
+ if (!TlsContext->TlsSecrets->IsSet.ServerTrafficSecret0) {
+ int SecretLen = HexToBytes(SecretHex, TlsContext->TlsSecrets->ServerTrafficSecret0, sizeof(TlsContext->TlsSecrets->ServerTrafficSecret0));
+ if (SecretLen < 0) {
+ return;
+ }
+ if (TlsContext->TlsSecrets->SecretLength == 0) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)SecretLen;
+ } else if (TlsContext->TlsSecrets->SecretLength != (uint8_t)SecretLen) {
+ return;
+ }
+ TlsContext->TlsSecrets->IsSet.ServerTrafficSecret0 = TRUE;
+ }
+ }
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
CxPlatTlsInitialize(
@@ -1660,7 +1817,7 @@ CxPlatTlsInitialize(
uint16_t ServerNameLength = 0;
UNREFERENCED_PARAMETER(State);
- CXPLAT_DBG_ASSERT(Config->HkdfLabels);
+ CXPLAT_DBG_ASSERT(Config->IsQMux || Config->HkdfLabels);
if (Config->SecConfig == NULL) {
Status = QUIC_STATUS_INVALID_PARAMETER;
goto Exit;
@@ -1679,6 +1836,7 @@ CxPlatTlsInitialize(
CxPlatZeroMemory(TlsContext, sizeof(CXPLAT_TLS));
+ TlsContext->IsQMux = Config->IsQMux;
TlsContext->Connection = Config->Connection;
TlsContext->HkdfLabels = Config->HkdfLabels;
TlsContext->IsServer = Config->IsServer;
@@ -1723,6 +1881,10 @@ CxPlatTlsInitialize(
}
}
+ if (TlsContext->IsQMux) {
+ SSL_CTX_set_keylog_callback(TlsContext->SecConfig->SSLCtx, CxPlatTlsKeyLogCallback);
+ }
+
//
// Create a SSL object for the connection.
//
@@ -1748,6 +1910,44 @@ CxPlatTlsInitialize(
SSL_set_alpn_protos(TlsContext->Ssl, TlsContext->AlpnBuffer, TlsContext->AlpnBufferLength);
}
+ if (!Config->IsQMux) {
+ int Ret = SSL_set_quic_method(TlsContext->Ssl, &OpenSslQuicCallbacks);
+ if (Ret != 1) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ ERR_get_error(),
+ "SSL_set_quic_method failed");
+ Status = QUIC_STATUS_TLS_ERROR;
+ goto Exit;
+ }
+ } else {
+ TlsContext->rbio = BIO_new(BIO_s_mem());
+ if (TlsContext->rbio == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "BIO_new failed");
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
+
+ TlsContext->wbio = BIO_new(BIO_s_mem());
+ if (TlsContext->wbio == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "BIO_new failed");
+ BIO_free(TlsContext->rbio);
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Exit;
+ }
+ SSL_set_bio(TlsContext->Ssl, TlsContext->rbio, TlsContext->wbio);
+ }
+
if (!(Config->SecConfig->TlsFlags & CXPLAT_TLS_CREDENTIAL_FLAG_DISABLE_RESUMPTION)) {
if (Config->ResumptionTicketLength != 0) {
@@ -1793,28 +1993,30 @@ CxPlatTlsInitialize(
}
}
- if (Config->IsServer || (Config->ResumptionTicketLength != 0)) {
+ if (!Config->IsQMux && (Config->IsServer || (Config->ResumptionTicketLength != 0))) {
SSL_set_quic_early_data_enabled(TlsContext->Ssl, 1);
}
}
- SSL_set_quic_use_legacy_codepoint(
- TlsContext->Ssl,
- TlsContext->QuicTpExtType == TLS_EXTENSION_TYPE_QUIC_TRANSPORT_PARAMETERS_DRAFT);
-
- if (SSL_set_quic_transport_params(
+ if (!Config->IsQMux) {
+ SSL_set_quic_use_legacy_codepoint(
TlsContext->Ssl,
- Config->LocalTPBuffer,
- Config->LocalTPLength) != 1) {
- QuicTraceEvent(
- TlsError,
- "[ tls][%p] ERROR, %s.",
- TlsContext->Connection,
- "SSL_set_quic_transport_params failed");
- Status = QUIC_STATUS_TLS_ERROR;
- goto Exit;
+ TlsContext->QuicTpExtType == TLS_EXTENSION_TYPE_QUIC_TRANSPORT_PARAMETERS_DRAFT);
+
+ if (SSL_set_quic_transport_params(
+ TlsContext->Ssl,
+ Config->LocalTPBuffer,
+ Config->LocalTPLength) != 1) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "SSL_set_quic_transport_params failed");
+ Status = QUIC_STATUS_TLS_ERROR;
+ goto Exit;
+ }
+ CXPLAT_FREE(Config->LocalTPBuffer, QUIC_POOL_TLS_TRANSPARAMS);
}
- CXPLAT_FREE(Config->LocalTPBuffer, QUIC_POOL_TLS_TRANSPARAMS);
if (Config->ResumptionTicketBuffer) {
CXPLAT_FREE(Config->ResumptionTicketBuffer, QUIC_POOL_CRYPTO_RESUMPTION_TICKET);
}
@@ -1879,6 +2081,7 @@ CxPlatTlsProcessData(
_Inout_ CXPLAT_TLS_PROCESS_STATE* State
)
{
+ CXPLAT_DBG_ASSERT(!TlsContext->IsQMux);
CXPLAT_DBG_ASSERT(Buffer != NULL || *BufferLength == 0);
TlsContext->State = State;
@@ -2171,6 +2374,572 @@ CxPlatTlsProcessData(
return TlsContext->ResultFlags;
}
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsHandshake(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_ CXPLAT_TLS_DATA_TYPE DataType,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_ QUIC_BUFFER* OutputBuffers,
+ _Inout_ uint32_t OutputBuffersCount,
+ _Inout_ CXPLAT_TLS_PROCESS_STATE* State
+ )
+{
+ int Ret;
+ CXPLAT_DBG_ASSERT(InputBuffer != NULL || *InputBufferLength == 0);
+ CXPLAT_DBG_ASSERT(OutputBuffers != NULL && OutputBuffersCount > 0);
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+
+ TlsContext->State = State;
+ TlsContext->ResultFlags = 0;
+
+ if (DataType == CXPLAT_TLS_TICKET_DATA) {
+ QuicTraceLogConnVerbose(
+ OpenSslSendTicketData,
+ TlsContext->Connection,
+ "Sending ticket data, %u bytes",
+ *InputBufferLength);
+
+ SSL_SESSION* Session = SSL_get_session(TlsContext->Ssl);
+ if (Session == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "SSL_get_session failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ if (!SSL_SESSION_set1_ticket_appdata(Session, InputBuffer, *InputBufferLength)) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ ERR_get_error(),
+ "SSL_SESSION_set1_ticket_appdata failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+
+ if (!SSL_new_session_ticket(TlsContext->Ssl)) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ ERR_get_error(),
+ "SSL_new_session_ticket failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ Ret = SSL_do_handshake(TlsContext->Ssl);
+ if (Ret != 1) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SSL_get_error(TlsContext->Ssl, Ret),
+ "SSL_do_handshake failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+
+ goto Send;
+ }
+
+ if (InputBuffer == NULL && *InputBufferLength == 0) {
+ goto Handshake;
+ }
+
+ if (InputBuffer != NULL && *InputBufferLength > 0) {
+ Ret = BIO_write(TlsContext->rbio, InputBuffer, (int)*InputBufferLength);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ }
+ *InputBufferLength = Ret;
+ }
+
+ // After feeding incoming TLS records into OpenSSL via BIO_write(rbio),
+ // do NOT immediately advance the handshake.
+ //
+ // Instead, prioritize reading TLS 1.3 early data and repeatedly call
+ // SSL_read_early_data() until one of the following conditions is reached:
+ //
+ // (1) SSL_READ_EARLY_DATA_FINISH is returned:
+ // -> No early data is present (or early data is already complete).
+ //
+ // (2) SSL_READ_EARLY_DATA_SUCCESS is returned at least once, and then
+ // SSL_read_early_data() returns SSL_ERROR_WANT_READ:
+ // -> All available early data has been fully consumed.
+ //
+ // (3) SSL_read_early_data() returns SSL_ERROR_WANT_WRITE:
+ // -> OpenSSL requires the server handshake flight (e.g. ServerHello)
+ // to be sent in order to make further progress.
+ //
+ // Handling:
+ // - In case (1), exit the early data phase and transition to the normal
+ // handshake phase by calling SSL_do_handshake() and draining wbio.
+ //
+ // - In cases (2) and (3), advance the handshake by exactly one step
+ // (SSL_do_handshake() + BIO_read(wbio)) to satisfy OpenSSL's state
+ // requirements, but do not complete the handshake.
+ // When additional TLS records are received, resume calling
+ // SSL_read_early_data() with priority.
+ //
+ // This ensures that early data is never skipped or lost, while preventing
+ // the TLS handshake from completing prematurely.
+
+ if (State->ReadEarlyData) {
+ CXPLAT_DBG_ASSERT(TlsContext->IsServer);
+ CXPLAT_DBG_ASSERT(State->EarlyDataBuffer != NULL && State->EarlyDataBufferAllocLength > 0);
+ size_t ReadLength;
+ do {
+ if (State->EarlyDataBufferLength >= State->EarlyDataBufferAllocLength) {
+ uint32_t NewEarlyDataBufferAllocLength = State->EarlyDataBufferAllocLength * 2;
+ uint8_t* NewEarlyDataBuffer =
+ CXPLAT_ALLOC_NONPAGED(
+ NewEarlyDataBufferAllocLength,
+ QUIC_POOL_TLS_EARLY_DATA_BUFFER);
+ if (NewEarlyDataBuffer == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS early data buffer",
+ NewEarlyDataBufferAllocLength);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ memcpy(NewEarlyDataBuffer, State->EarlyDataBuffer, State->EarlyDataBufferLength);
+ CXPLAT_FREE(State->EarlyDataBuffer, QUIC_POOL_TLS_EARLY_DATA_BUFFER);
+ State->EarlyDataBuffer = NewEarlyDataBuffer;
+ State->EarlyDataBufferAllocLength = NewEarlyDataBufferAllocLength;
+ }
+ ReadLength = 0;
+ Ret =
+ SSL_read_early_data(
+ TlsContext->Ssl,
+ State->EarlyDataBuffer + State->EarlyDataBufferLength,
+ State->EarlyDataBufferAllocLength - State->EarlyDataBufferLength,
+ &ReadLength);
+ CXPLAT_DBG_ASSERT(ReadLength <= State->EarlyDataBufferAllocLength - State->EarlyDataBufferLength);
+ State->EarlyDataBufferLength += (uint32_t)ReadLength;
+ switch (Ret) {
+ case SSL_READ_EARLY_DATA_FINISH:
+ // No more early data is available, or early data is already complete.
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ case SSL_READ_EARLY_DATA_SUCCESS:
+ // Successfully read some early data.
+ // Stay in this state until SSL_read_early_data() indicates completion.
+ State->ReadEarlyDataSuccess = TRUE;
+ break;
+ case SSL_READ_EARLY_DATA_ERROR: {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ switch (Err) {
+ case SSL_ERROR_WANT_READ:
+ if (State->ReadEarlyDataSuccess) {
+ // All available early data has been consumed,
+ // so execute the handshake to move past the early data phase.
+ goto Handshake;
+ }
+ goto Exit;
+
+ case SSL_ERROR_WANT_WRITE:
+ // OpenSSL requires the server handshake flight to be sent before
+ // more early data can be processed.
+ goto Handshake;
+
+ case SSL_ERROR_SSL: {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "SSL_read_early_data error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ }
+
+ default:
+ QuicTraceLogConnError(
+ OpenSslHandshakeError,
+ TlsContext->Connection,
+ "SSL_read_early_data error: %d",
+ Err);
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ }
+ }
+ default:
+ QuicTraceLogConnError(
+ OpenSslHandshakeError,
+ TlsContext->Connection,
+ "Unexpected SSL_read_early_data return value: %d",
+ Ret);
+ State->ReadEarlyData = FALSE;
+ goto Handshake;
+ }
+ } while (Ret == SSL_READ_EARLY_DATA_SUCCESS && ReadLength > 0);
+ }
+
+Handshake:
+ if (!State->HandshakeComplete) {
+ Ret = SSL_do_handshake(TlsContext->Ssl);
+ if (Ret <= 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ switch (Err) {
+ case SSL_ERROR_WANT_READ:
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ break;
+
+ case SSL_ERROR_SSL: {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "TLS handshake error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+
+ default:
+ QuicTraceLogConnError(
+ OpenSslHandshakeError,
+ TlsContext->Connection,
+ "TLS handshake error: %d",
+ Err);
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ }
+
+ if (SSL_is_init_finished(TlsContext->Ssl)) {
+ QuicTraceLogConnInfo(
+ OpenSslHandshakeComplete,
+ TlsContext->Connection,
+ "TLS Handshake complete");
+ State->HandshakeComplete = TRUE;
+ if (SSL_session_reused(TlsContext->Ssl)) {
+ QuicTraceLogConnInfo(
+ OpenSslHandshakeResumed,
+ TlsContext->Connection,
+ "TLS Handshake resumed");
+ State->SessionResumed = TRUE;
+ }
+ if (!TlsContext->IsServer) {
+ int EarlyDataStatus = SSL_get_early_data_status(TlsContext->Ssl);
+ if (EarlyDataStatus == SSL_EARLY_DATA_ACCEPTED) {
+ State->EarlyDataState = CXPLAT_TLS_EARLY_DATA_ACCEPTED;
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_EARLY_DATA_ACCEPT;
+
+ } else if (EarlyDataStatus == SSL_EARLY_DATA_REJECTED) {
+ State->EarlyDataState = CXPLAT_TLS_EARLY_DATA_REJECTED;
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_EARLY_DATA_REJECT;
+ }
+ }
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_HANDSHAKE_COMPLETE;
+
+ if (!TlsContext->IsServer) {
+ const uint8_t* NegotiatedAlpn;
+ uint32_t NegotiatedAlpnLength;
+ SSL_get0_alpn_selected(TlsContext->Ssl, &NegotiatedAlpn, &NegotiatedAlpnLength);
+ if (NegotiatedAlpnLength == 0) {
+ QuicTraceLogConnError(
+ OpenSslAlpnNegotiationFailure,
+ TlsContext->Connection,
+ "Failed to negotiate ALPN");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ if (NegotiatedAlpnLength > UINT8_MAX) {
+ QuicTraceLogConnError(
+ OpenSslInvalidAlpnLength,
+ TlsContext->Connection,
+ "Invalid negotiated ALPN length");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ TlsContext->State->NegotiatedAlpn =
+ CxPlatTlsAlpnFindInList(
+ TlsContext->AlpnBufferLength,
+ TlsContext->AlpnBuffer,
+ (uint8_t)NegotiatedAlpnLength,
+ NegotiatedAlpn);
+ if (TlsContext->State->NegotiatedAlpn == NULL) {
+ QuicTraceLogConnError(
+ OpenSslNoMatchingAlpn,
+ TlsContext->Connection,
+ "Failed to find a matching ALPN");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ goto Exit;
+ }
+ } else if ((TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED) &&
+ !TlsContext->PeerCertReceived) {
+ QUIC_STATUS ValidationResult =
+ (!(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION) &&
+ (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION ||
+ TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION)) ?
+ QUIC_STATUS_CERT_NO_CERT :
+ QUIC_STATUS_SUCCESS;
+
+ if (!TlsContext->SecConfig->Callbacks.CertificateReceived(
+ TlsContext->Connection,
+ NULL,
+ NULL,
+ 0,
+ ValidationResult)) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "Indicate null certificate received failed");
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ TlsContext->State->AlertCode = CXPLAT_TLS_ALERT_CODE_REQUIRED_CERTIFICATE;
+ goto Exit;
+ }
+ }
+ }
+ }
+
+Send:
+ uint32_t OutputBufferCount = 0;
+ QUIC_BUFFER *OutputBuffer = &OutputBuffers[OutputBufferCount];
+ size_t OutputBufferOffset = 0;
+ while (BIO_pending(TlsContext->wbio)) {
+ if (OutputBufferOffset == OutputBuffer->Length) {
+ OutputBufferCount++;
+ if (OutputBufferCount == OutputBuffersCount) {
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL;
+ break;
+ }
+ OutputBuffer = &OutputBuffers[OutputBufferCount];
+ OutputBufferOffset = 0;
+ }
+ Ret =
+ BIO_read(
+ TlsContext->wbio,
+ OutputBuffer->Buffer + OutputBufferOffset,
+ (int)(OutputBuffer->Length - OutputBufferOffset));
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ if (Err == SSL_ERROR_SSL) {
+ char buf[256];
+ const char* file;
+ int line;
+ ERR_error_string_n(ERR_get_error_all(&file, &line, NULL, NULL, NULL), buf, sizeof(buf));
+ QuicTraceLogConnError(
+ OpenSslHandshakeErrorStr,
+ TlsContext->Connection,
+ "BIO_read error: %s, file:%s:%d",
+ buf,
+ (strlen(file) > OpenSslFilePrefixLength ? file + OpenSslFilePrefixLength : file),
+ line);
+ } else {
+ QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+ }
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ } else {
+ OutputBufferOffset += (size_t)Ret;
+ OutputBuffer->Length = (uint32_t)OutputBufferOffset;
+ TlsContext->ResultFlags |= CXPLAT_TLS_RESULT_DATA;
+ }
+ }
+
+ for (uint32_t i = OutputBufferCount + 1; i < OutputBuffersCount; ++i) {
+ OutputBuffers[i].Length = 0;
+ }
+
+Exit:
+
+ return TlsContext->ResultFlags;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsWriteEarlyData(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ size_t * InputBufferLength
+ )
+{
+ CXPLAT_DBG_ASSERT(InputBuffer != NULL || *InputBufferLength == 0);
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+ int Ret =
+ SSL_write_early_data(
+ TlsContext->Ssl,
+ InputBuffer,
+ *InputBufferLength,
+ InputBufferLength);
+ return Ret == 1;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsEncrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Inout_ CXPLAT_TLS_ENCRYPT_BUFFER* Buffer
+ )
+{
+ int Ret;
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+ CXPLAT_TLS_RECORD_OVERHEAD Overhead;
+ CxPlatTlsGetRecordOverhead(TlsContext, &Overhead);
+
+ if (Buffer->Capacity < Overhead.MaxHeader + Buffer->DataLength + Overhead.MaxTrailer) {
+ // Not enough room for encryption overhead, which can be up to 5 bytes for the TLS record
+ // header and up to 512 bytes for the trailer.
+ return FALSE;
+ }
+
+ if (Buffer->DataLength > 16384) {
+ // TLS record plaintext cannot exceed 2^14 bytes.
+ return FALSE;
+ }
+
+ const uint8_t* Plaintext =
+ Buffer->Base + Buffer->DataOffset;
+ size_t PlaintextLength = Buffer->DataLength;
+
+ Ret = SSL_write(
+ TlsContext->Ssl,
+ Plaintext,
+ (int)PlaintextLength);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslSSLWriteError,
+ TlsContext->Connection,
+ "SSL_write failed, error: %d",
+ Err);
+ return FALSE;
+ } else if (Ret == 0) {
+ return FALSE;
+ }
+
+ size_t Offset = 0;
+ Buffer->DataLength = 0;
+ while (BIO_pending(TlsContext->wbio) > 0) {
+ CXPLAT_DBG_ASSERT(Offset < Buffer->Capacity);
+ Ret = BIO_read(TlsContext->wbio, Buffer->Base + Offset, (int)(Buffer->Capacity - Offset));
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslBIOReadError,
+ TlsContext->Connection,
+ "BIO_read failed, error: %d",
+ Err);
+ return FALSE;
+ } else if (Ret == 0) {
+ break;
+ }
+ Offset += (size_t)Ret;
+ Buffer->DataLength += (size_t)Ret;
+ }
+
+ return TRUE;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsDecrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_updates_bytes_opt_(*OutputBufferLength)
+ uint8_t* OutputBuffer,
+ _Inout_ uint32_t* OutputBufferLength
+ )
+{
+ CXPLAT_TLS_RESULT_FLAGS Result = 0;
+ int Ret;
+ CXPLAT_DBG_ASSERT(InputBuffer != NULL || *InputBufferLength == 0);
+ CXPLAT_DBG_ASSERT(OutputBuffer != NULL && *OutputBufferLength > 0);
+ CXPLAT_DBG_ASSERT(TlsContext->IsQMux);
+
+ if (InputBuffer != NULL && *InputBufferLength > 0) {
+ Ret = BIO_write(TlsContext->rbio, InputBuffer, (int)*InputBufferLength);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+ QuicTraceLogConnError(
+ OpenSslBIOWriteError,
+ TlsContext->Connection,
+ "BIO_write failed, error: %d",
+ Err);
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ return Result;
+ }
+ *InputBufferLength = Ret;
+ }
+
+ size_t OutputBufferOffset = 0;
+ do {
+ Ret =
+ SSL_read(TlsContext->Ssl,
+ OutputBuffer + OutputBufferOffset,
+ (int)*OutputBufferLength - (int)OutputBufferOffset);
+ if (Ret < 0) {
+ int Err = SSL_get_error(TlsContext->Ssl, Ret);
+
+ if (Err == SSL_ERROR_WANT_READ || Err == SSL_ERROR_WANT_WRITE) {
+ break;
+ } else if (Err == SSL_ERROR_ZERO_RETURN) {
+ break;
+ } else {
+ QuicTraceLogConnError(
+ OpenSslSSLReadError,
+ TlsContext->Connection,
+ "SSL_read failed, error: %d",
+ Err);
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ return Result;
+ }
+ }
+ OutputBufferOffset += (size_t)Ret;
+ } while (Ret > 0 && OutputBufferOffset < *OutputBufferLength);
+ *OutputBufferLength = (uint32_t)OutputBufferOffset;
+
+ return Result;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+CxPlatTlsGetRecordOverhead(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Out_ CXPLAT_TLS_RECORD_OVERHEAD* Overhead
+ )
+{
+ UNREFERENCED_PARAMETER(TlsContext);
+ Overhead->MaxHeader = 5; // TLS record header is always 5 bytes
+ Overhead->MaxTrailer = 512; // Maximum possible trailer length for TLS 1.3 with AEAD ciphers
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
CxPlatSecConfigParamSet(
diff --git a/src/platform/tls_schannel.c b/src/platform/tls_schannel.c
index dbabc9226f..131bad1a2e 100644
--- a/src/platform/tls_schannel.c
+++ b/src/platform/tls_schannel.c
@@ -545,6 +545,7 @@ typedef struct _SEC_BUFFER_WORKSPACE {
typedef struct CXPLAT_TLS {
+ BOOLEAN IsQMux : 1;
BOOLEAN IsServer : 1;
BOOLEAN GeneratedFirstPayload : 1;
BOOLEAN PeerTransportParamsReceived : 1;
@@ -612,6 +613,16 @@ typedef struct CXPLAT_TLS {
//
QUIC_TLS_SECRETS* TlsSecrets;
+ _Field_size_bytes_(InputBufferAllocLength)
+ uint8_t* InputBuffer;
+ uint32_t InputBufferLength;
+ uint32_t InputBufferAllocLength;
+
+ _Field_size_bytes_(OutputBufferAllocLength)
+ uint8_t* OutputBuffer;
+ uint32_t OutputBufferLength;
+ uint32_t OutputBufferAllocLength;
+
} CXPLAT_TLS;
typedef enum QUIC_CERT_BLOB_TYPE {
@@ -1541,7 +1552,11 @@ CxPlatTlsInitialize(
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
CXPLAT_TLS* TlsContext = NULL;
- CXPLAT_DBG_ASSERT(Config->HkdfLabels);
+ if (!Config->IsQMux) {
+ CXPLAT_DBG_ASSERT(Config->HkdfLabels);
+ } else {
+ CxPlatTlsTPHeaderSize = 0;
+ }
if (Config->IsServer != !(Config->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_CLIENT)) {
QuicTraceEvent(
@@ -1568,6 +1583,7 @@ CxPlatTlsInitialize(
RtlZeroMemory(TlsContext, sizeof(*TlsContext));
SecInvalidateHandle(&TlsContext->SchannelContext);
+ TlsContext->IsQMux = Config->IsQMux;
TlsContext->IsServer = Config->IsServer;
TlsContext->Connection = Config->Connection;
TlsContext->HkdfLabels = Config->HkdfLabels;
@@ -1591,13 +1607,15 @@ CxPlatTlsInitialize(
AlpnList->ProtocolListSize = Config->AlpnBufferLength;
memcpy(&AlpnList->ProtocolList, Config->AlpnBuffer, Config->AlpnBufferLength);
- TlsContext->TransportParams = (SEND_GENERIC_TLS_EXTENSION*)Config->LocalTPBuffer;
- TlsContext->TransportParams->ExtensionType = Config->TPType;
- TlsContext->TransportParams->HandshakeType =
- Config->IsServer ? TlsHandshake_EncryptedExtensions : TlsHandshake_ClientHello;
- TlsContext->TransportParams->Flags = 0;
- TlsContext->TransportParams->BufferSize =
- (uint16_t)(Config->LocalTPLength - FIELD_OFFSET(SEND_GENERIC_TLS_EXTENSION, Buffer));
+ if (!Config->IsQMux) {
+ TlsContext->TransportParams = (SEND_GENERIC_TLS_EXTENSION*)Config->LocalTPBuffer;
+ TlsContext->TransportParams->ExtensionType = Config->TPType;
+ TlsContext->TransportParams->HandshakeType =
+ Config->IsServer ? TlsHandshake_EncryptedExtensions : TlsHandshake_ClientHello;
+ TlsContext->TransportParams->Flags = 0;
+ TlsContext->TransportParams->BufferSize =
+ (uint16_t)(Config->LocalTPLength - FIELD_OFFSET(SEND_GENERIC_TLS_EXTENSION, Buffer));
+ }
State->EarlyDataState = CXPLAT_TLS_EARLY_DATA_UNSUPPORTED; // 0-RTT not currently supported.
if (Config->ResumptionTicketBuffer != NULL) {
@@ -1665,6 +1683,12 @@ CxPlatTlsUninitialize(
TlsContext->PeerTransportParams = NULL;
TlsContext->PeerTransportParamsLength = 0;
}
+ if (TlsContext->InputBuffer != NULL) {
+ CXPLAT_FREE(TlsContext->InputBuffer, QUIC_POOL_TLS_BUFFER);
+ TlsContext->InputBuffer = NULL;
+ TlsContext->InputBufferLength = 0;
+ TlsContext->InputBufferAllocLength = 0;
+ }
CXPLAT_FREE(TlsContext, QUIC_POOL_TLS_CTX);
}
}
@@ -2445,8 +2469,8 @@ CxPlatTlsWriteDataToSchannel(
//
// Not all the input buffer was consumed. There is some 'extra' left over.
//
- CXPLAT_DBG_ASSERT(InSecBuffers[1].cbBuffer <= *InBufferLength);
- *InBufferLength -= InSecBuffers[1].cbBuffer;
+ CXPLAT_DBG_ASSERT(ExtraBuffer->cbBuffer <= *InBufferLength);
+ *InBufferLength -= ExtraBuffer->cbBuffer;
}
QuicTraceLogConnInfo(
@@ -2895,6 +2919,1235 @@ CxPlatTlsProcessData(
return Result;
}
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsHandshake(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_ CXPLAT_TLS_DATA_TYPE DataType,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t * InputBufferLength,
+ _Inout_ QUIC_BUFFER* OutputBuffers,
+ _Inout_ uint32_t OutputBuffersCount,
+ _Inout_ CXPLAT_TLS_PROCESS_STATE* State
+ )
+{
+#ifdef _KERNEL_MODE
+ UNREFERENCED_PARAMETER(DataType);
+ UNREFERENCED_PARAMETER(InputBuffer);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(OutputBuffers);
+ UNREFERENCED_PARAMETER(OutputBuffersCount);
+ UNREFERENCED_PARAMETER(State);
+
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "Handshake not supported in kernel mode");
+ return CXPLAT_TLS_RESULT_ERROR;
+#else
+ if (DataType == CXPLAT_TLS_TICKET_DATA) {
+ QuicTraceLogConnVerbose(
+ SchannelIgnoringTicket,
+ TlsContext->Connection,
+ "Ignoring %u ticket bytes",
+ *InputBufferLength);
+ return CXPLAT_TLS_RESULT_ERROR;
+ }
+
+ SEC_WCHAR* TargetServerName = NULL;
+
+ SecBuffer* InSecBuffers = TlsContext->Workspace.InSecBuffers;
+ SecBuffer* OutSecBuffers = TlsContext->Workspace.OutSecBuffers;
+
+ SecBufferDesc InSecBufferDesc;
+ InSecBufferDesc.ulVersion = SECBUFFER_VERSION;
+ InSecBufferDesc.pBuffers = InSecBuffers;
+ InSecBufferDesc.cBuffers = 0;
+
+ SecBufferDesc OutSecBufferDesc;
+ OutSecBufferDesc.ulVersion = SECBUFFER_VERSION;
+ OutSecBufferDesc.pBuffers = OutSecBuffers;
+ OutSecBufferDesc.cBuffers = 0;
+
+ uint8_t AlertBufferRaw[2];
+
+ if (*InputBufferLength == 0) {
+
+ //
+ // If the input length is zero, then we are initializing the client
+ // side, and have a few special differences in this code path.
+ //
+ CXPLAT_DBG_ASSERT(TlsContext->IsServer == FALSE);
+
+ if (TlsContext->SNI != NULL) {
+ QUIC_STATUS Status = CxPlatUtf8ToWideChar(TlsContext->SNI, QUIC_POOL_TLS_SNI, &TargetServerName);
+ if (QUIC_FAILED(Status)) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ Status,
+ "Convert SNI to unicode");
+ return CXPLAT_TLS_RESULT_ERROR;
+ }
+ }
+
+ //
+ // The first (input) secbuffer holds the ALPN for client initials.
+ //
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_APPLICATION_PROTOCOLS;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = TlsContext->AppProtocolsSize;
+ InSecBuffers[InSecBufferDesc.cBuffers].pvBuffer = TlsContext->ApplicationProtocols;
+ InSecBufferDesc.cBuffers++;
+
+ } else {
+
+ //
+ // The first (input) secbuffer holds the received TLS data.
+ //
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_TOKEN;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = *InputBufferLength;
+ InSecBuffers[InSecBufferDesc.cBuffers].pvBuffer = (void*)InputBuffer;
+ InSecBufferDesc.cBuffers++;
+ }
+
+ //
+ // More (input) secbuffers to allow Schannel to signal to if any data is
+ // extra or missing.
+ // N.B. These must always immediately follow the SECBUFFER_TOKEN. Nothing
+ // is allowed to be before them.
+ //
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_EMPTY;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = 0;
+ InSecBuffers[InSecBufferDesc.cBuffers].pvBuffer = NULL;
+ InSecBufferDesc.cBuffers++;
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_EMPTY;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = 0;
+ InSecBuffers[InSecBufferDesc.cBuffers].pvBuffer = NULL;
+ InSecBufferDesc.cBuffers++;
+
+ //
+ // If this is the first server call to ASC, populate the ALPN extension.
+ //
+ if (TlsContext->IsServer && !TlsContext->GeneratedFirstPayload) {
+ //
+ // The last (input) secbuffer contains the ALPN on server.
+ //
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_APPLICATION_PROTOCOLS;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = TlsContext->AppProtocolsSize;
+ InSecBuffers[InSecBufferDesc.cBuffers].pvBuffer = TlsContext->ApplicationProtocols;
+ InSecBufferDesc.cBuffers++;
+ }
+
+ //
+ // The first (output) secbuffer is the buffer for Schannel to write any
+ // TLS payload to send back out.
+ //
+ for (uint32_t i = 0; i < OutputBuffersCount; ++i) {
+ if (OutputBuffers[i].Length == 0 || OutputBuffers[i].Buffer == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "Invalid output buffer");
+ return CXPLAT_TLS_RESULT_ERROR;
+ }
+ OutSecBuffers[OutSecBufferDesc.cBuffers].BufferType = SECBUFFER_TOKEN;
+ OutSecBuffers[OutSecBufferDesc.cBuffers].cbBuffer = OutputBuffers[i].Length;
+ OutSecBuffers[OutSecBufferDesc.cBuffers].pvBuffer = OutputBuffers[i].Buffer;
+ OutSecBufferDesc.cBuffers++;
+ }
+
+ //
+ // Another (output) secbuffer is for any TLS alerts.
+ //
+ OutSecBuffers[OutSecBufferDesc.cBuffers].BufferType = SECBUFFER_ALERT;
+ OutSecBuffers[OutSecBufferDesc.cBuffers].cbBuffer = sizeof(AlertBufferRaw);
+ OutSecBuffers[OutSecBufferDesc.cBuffers].pvBuffer = AlertBufferRaw;
+ OutSecBufferDesc.cBuffers++;
+
+ //
+ // Four more output secbuffers for any traffic secrets generated.
+ //
+ for (uint8_t i = 0; i < SEC_TRAFFIC_SECRETS_COUNT; ++i) {
+ OutSecBuffers[OutSecBufferDesc.cBuffers].BufferType = SECBUFFER_TRAFFIC_SECRETS;
+ OutSecBuffers[OutSecBufferDesc.cBuffers].cbBuffer = MAX_SEC_TRAFFIC_SECRETS_SIZE;
+ OutSecBuffers[OutSecBufferDesc.cBuffers].pvBuffer =
+ TlsContext->Workspace.OutTrafSecBuf + i * MAX_SEC_TRAFFIC_SECRETS_SIZE;
+ OutSecBufferDesc.cBuffers++;
+ }
+
+ CXPLAT_STATIC_ASSERT(ISC_REQ_SEQUENCE_DETECT == ASC_REQ_SEQUENCE_DETECT, "These are assumed to match");
+ CXPLAT_STATIC_ASSERT(ISC_REQ_CONFIDENTIALITY == ASC_REQ_CONFIDENTIALITY, "These are assumed to match");
+ ULONG ContextReq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY;
+ if (TlsContext->IsServer) {
+ ContextReq |= ASC_REQ_EXTENDED_ERROR | ASC_REQ_STREAM |
+ ASC_REQ_SESSION_TICKET; // Always use session tickets for resumption
+ if (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION) {
+ ContextReq |= ASC_REQ_MUTUAL_AUTH;
+ }
+ } else {
+ ContextReq |= ISC_REQ_EXTENDED_ERROR | ISC_REQ_STREAM;
+ if (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_USE_SUPPLIED_CREDENTIALS) {
+ ContextReq |= ISC_REQ_USE_SUPPLIED_CREDS;
+ }
+ }
+ ULONG ContextAttr;
+ SECURITY_STATUS SecStatus;
+
+ if (TlsContext->IsServer) {
+ CXPLAT_DBG_ASSERT(!(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_CLIENT));
+
+ SecStatus =
+ AcceptSecurityContext(
+ &TlsContext->SecConfig->CredentialHandle,
+ SecIsValidHandle(&TlsContext->SchannelContext) ? &TlsContext->SchannelContext : NULL,
+ &InSecBufferDesc,
+ ContextReq,
+ 0,
+ &(TlsContext->SchannelContext),
+ &OutSecBufferDesc,
+ &ContextAttr,
+ NULL); // FYI, used for client authentication certificate.
+
+ } else {
+ CXPLAT_DBG_ASSERT(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_CLIENT);
+
+ SecStatus =
+ InitializeSecurityContextW(
+ &TlsContext->SecConfig->CredentialHandle,
+ SecIsValidHandle(&TlsContext->SchannelContext) ? &TlsContext->SchannelContext : NULL,
+ TargetServerName, // Only set to non-null on client initial.
+ ContextReq,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &InSecBufferDesc,
+ 0,
+ &TlsContext->SchannelContext,
+ &OutSecBufferDesc,
+ &ContextAttr,
+ NULL);
+ }
+
+ CXPLAT_TLS_RESULT_FLAGS Result = 0;
+
+ SecBuffer* ExtraBuffer = NULL;
+ SecBuffer* MissingBuffer = NULL;
+ for (uint32_t i = 0; i < InSecBufferDesc.cBuffers; ++i) {
+ if (ExtraBuffer == NULL &&
+ InSecBufferDesc.pBuffers[i].BufferType == SECBUFFER_EXTRA) {
+ ExtraBuffer = &InSecBufferDesc.pBuffers[i];
+ } else if (MissingBuffer == NULL &&
+ InSecBufferDesc.pBuffers[i].BufferType == SECBUFFER_MISSING) {
+ MissingBuffer = &InSecBufferDesc.pBuffers[i];
+ }
+ }
+
+ SecBuffer* OutputTokenBuffer = NULL;
+ SecBuffer* AlertBuffer = NULL;
+ SEC_TRAFFIC_SECRETS* NewPeerTrafficSecrets[2] = {0};
+ SEC_TRAFFIC_SECRETS* NewOwnTrafficSecrets[2] = {0};
+ uint8_t NewPeerTrafficSecretsCount = 0;
+ uint8_t NewOwnTrafficSecretsCount = 0;
+
+ for (uint32_t i = 0; i < OutSecBufferDesc.cBuffers; ++i) {
+ if (OutputTokenBuffer == NULL &&
+ OutSecBufferDesc.pBuffers[i].BufferType == SECBUFFER_TOKEN) {
+ OutputTokenBuffer = &OutSecBufferDesc.pBuffers[i];
+ } else if (AlertBuffer == NULL &&
+ OutSecBufferDesc.pBuffers[i].BufferType == SECBUFFER_ALERT &&
+ OutSecBufferDesc.pBuffers[i].cbBuffer > 0) {
+ AlertBuffer = &OutSecBufferDesc.pBuffers[i];
+ } else if (OutSecBufferDesc.pBuffers[i].BufferType == SECBUFFER_TRAFFIC_SECRETS) {
+ SEC_TRAFFIC_SECRETS* TrafficSecret =
+ (SEC_TRAFFIC_SECRETS*)OutSecBufferDesc.pBuffers[i].pvBuffer;
+ if (TrafficSecret->TrafficSecretType == SecTrafficSecret_None) {
+ continue;
+ }
+ QuicTraceLogConnVerbose(
+ SchannelKeyReady,
+ TlsContext->Connection,
+ "Key Ready Type, %u [%hu to %hu]",
+ (uint32_t)TrafficSecret->TrafficSecretType,
+ TrafficSecret->MsgSequenceStart,
+ TrafficSecret->MsgSequenceEnd);
+ if (TlsContext->IsServer) {
+ if (TrafficSecret->TrafficSecretType == SecTrafficSecret_Server) {
+ NewOwnTrafficSecrets[NewOwnTrafficSecretsCount++] = TrafficSecret;
+ } else {
+ NewPeerTrafficSecrets[NewPeerTrafficSecretsCount++] = TrafficSecret;
+ }
+ } else {
+ if (TrafficSecret->TrafficSecretType == SecTrafficSecret_Server) {
+ NewPeerTrafficSecrets[NewPeerTrafficSecretsCount++] = TrafficSecret;
+ } else {
+ NewOwnTrafficSecrets[NewOwnTrafficSecretsCount++] = TrafficSecret;
+ }
+ }
+ }
+ }
+
+ switch (SecStatus) {
+ case SEC_E_BUFFER_TOO_SMALL: {
+ //
+ // The output buffer for the TLS response is too small. We need to grow
+ // the buffer and try again.
+ //
+ QuicTraceLogConnInfo(
+ SchannelOutBufferTooSmall,
+ TlsContext->Connection,
+ "Notified Schannel output buffer too small");
+ Result |= CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ case SEC_E_OK:
+
+ //
+ // The handshake has completed. This may or may not result in more data
+ // that needs to be sent back in response (depending on client/server).
+ //
+
+ if (!State->HandshakeComplete) {
+ SecPkgContext_ApplicationProtocol NegotiatedAlpn;
+ SecStatus =
+ QueryContextAttributesW(
+ &TlsContext->SchannelContext,
+ SECPKG_ATTR_APPLICATION_PROTOCOL,
+ &NegotiatedAlpn);
+ if (SecStatus != SEC_E_OK) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "query negotiated ALPN");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ if (NegotiatedAlpn.ProtoNegoStatus != SecApplicationProtocolNegotiationStatus_Success) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ NegotiatedAlpn.ProtoNegoStatus,
+ "ALPN negotiation status");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ const SEC_APPLICATION_PROTOCOL_LIST* AlpnList =
+ &TlsContext->ApplicationProtocols->ProtocolLists[0];
+ State->NegotiatedAlpn =
+ CxPlatTlsAlpnFindInList(
+ AlpnList->ProtocolListSize,
+ AlpnList->ProtocolList,
+ NegotiatedAlpn.ProtocolIdSize,
+ NegotiatedAlpn.ProtocolId);
+ if (State->NegotiatedAlpn == NULL) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "ALPN Mismatch");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ SecPkgContext_CertificateValidationResult CertValidationResult = {0,0};
+
+ SecPkgContext_SessionInfo SessionInfo;
+ SecStatus =
+ QueryContextAttributesW(
+ &TlsContext->SchannelContext,
+ SECPKG_ATTR_SESSION_INFO,
+ &SessionInfo);
+ if (SecStatus != SEC_E_OK) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "query session info");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ if (SessionInfo.dwFlags & SSL_SESSION_RECONNECT) {
+ State->SessionResumed = TRUE;
+ }
+
+ const BOOLEAN RequirePeerCert =
+ !TlsContext->IsServer ||
+ TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION;
+
+ QUIC_CERT_BLOB PeerCertBlob;
+ CxPlatZeroMemory(&PeerCertBlob, sizeof(PeerCertBlob));
+ if (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE) {
+ PeerCertBlob.Type = QUIC_CERT_BLOB_SERIALIZED;
+ SecStatus =
+ QueryContextAttributesW(
+ &TlsContext->SchannelContext,
+ SECPKG_ATTR_SERIALIZED_REMOTE_CERT_CONTEXT_INPROC,
+ (PVOID)&(PeerCertBlob.Serialized));
+ } else {
+ SecStatus =
+ QueryContextAttributesW(
+ &TlsContext->SchannelContext,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ (PVOID)&PeerCertBlob.Context);
+ PeerCertBlob.Type =
+ PeerCertBlob.Context ?
+ QUIC_CERT_BLOB_CONTEXT : QUIC_CERT_BLOB_NONE;
+ }
+ if ((SecStatus == SEC_E_NO_CREDENTIALS || SecStatus == SEC_E_INTERNAL_ERROR) &&
+ (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION)) {
+ //
+ // Certificate validation is being deferred to the application:
+ // indicate no certificate is present but let the application decide if this is a
+ // failure.
+ // SEC_E_INTERNAL_ERROR can be returned on SECPKG_ATTR_REMOTE_CERT_CONTEXT if no
+ // certificate is found, normalize to SEC_E_NO_CREDENTIALS.
+ //
+ PeerCertBlob.Type = QUIC_CERT_BLOB_NONE;
+ CertValidationResult.hrVerifyChainStatus = SEC_E_NO_CREDENTIALS;
+ } else if (SecStatus == SEC_E_OK &&
+ !(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION) &&
+ (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION ||
+ TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION)) {
+ //
+ // Collect the client cert validation result
+ //
+ SecStatus =
+ QueryContextAttributesW(
+ &TlsContext->SchannelContext,
+ SECPKG_ATTR_CERT_CHECK_RESULT_INPROC,
+ &CertValidationResult);
+ if (SecStatus == SEC_E_NO_CREDENTIALS) {
+ CertValidationResult.hrVerifyChainStatus = SecStatus;
+ } else if (SecStatus != SEC_E_OK) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "query cert validation result");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ } else if (SecStatus != SEC_E_OK && RequirePeerCert &&
+ !(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION)) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "Query peer cert");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ State->AlertCode = CXPLAT_TLS_ALERT_CODE_INTERNAL_ERROR;
+ break;
+ }
+
+ if (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED) {
+ Result |=
+ CxPlatTlsIndicateCertificateReceived(
+ TlsContext,
+ State,
+ &CertValidationResult,
+ &PeerCertBlob);
+ }
+
+ if (TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE) {
+ if (PeerCertBlob.Serialized.pbData != NULL) {
+ FreeContextBuffer(PeerCertBlob.Serialized.pbData);
+ }
+ } else {
+ if (PeerCertBlob.Context != NULL) {
+ CertFreeCertificateContext(PeerCertBlob.Context);
+ }
+ }
+
+ if ((Result & CXPLAT_TLS_RESULT_ERROR) != 0) {
+ break;
+ }
+
+ if (!(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION) &&
+ CertValidationResult.hrVerifyChainStatus != QUIC_STATUS_SUCCESS) {
+ //
+ // If the server has configured client authentication but not deferred validation,
+ // fail the handshake if the client cert doesn't pass validation
+ //
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ CertValidationResult.hrVerifyChainStatus,
+ "Certificate validation failed");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ State->AlertCode = CXPLAT_TLS_ALERT_CODE_BAD_CERTIFICATE;
+ break;
+ }
+
+ QuicTraceLogConnInfo(
+ SchannelHandshakeComplete,
+ TlsContext->Connection,
+ "Handshake complete (resume=%hu)",
+ State->SessionResumed);
+ State->HandshakeComplete = TRUE;
+ Result |= CXPLAT_TLS_RESULT_HANDSHAKE_COMPLETE;
+ }
+
+ __fallthrough;
+
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_CONTINUE_NEEDED_MESSAGE_OK:
+
+ if (AlertBuffer != NULL) {
+ if (AlertBuffer->cbBuffer < 2) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "TLS alert message received (invalid)");
+ } else {
+ State->AlertCode = ((uint8_t*)AlertBuffer->pvBuffer)[1];
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ State->AlertCode,
+ "TLS alert message received");
+ }
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+
+ //
+ // Some or all of the input data was processed. There may or may not be
+ // corresponding output data to send in response.
+ //
+
+ if (ExtraBuffer != NULL && ExtraBuffer->cbBuffer > 0) {
+ //
+ // Not all the input buffer was consumed. There is some 'extra' left over.
+ //
+ CXPLAT_DBG_ASSERT(ExtraBuffer->cbBuffer <= *InputBufferLength);
+ *InputBufferLength -= ExtraBuffer->cbBuffer;
+ }
+
+ QuicTraceLogConnInfo(
+ SchannelConsumedBytes,
+ TlsContext->Connection,
+ "Consumed %u bytes",
+ *InputBufferLength);
+
+ //
+ // Update our "read" key state based on any new peer keys being available.
+ //
+ for (uint8_t i = 0; i < NewPeerTrafficSecretsCount; ++i) {
+ Result |= CXPLAT_TLS_RESULT_READ_KEY_UPDATED;
+ if (NewPeerTrafficSecrets[i]->TrafficSecretType == SecTrafficSecret_ClientEarlyData) {
+ CXPLAT_FRE_ASSERT(FALSE); // TODO - Finish the 0-RTT logic.
+ CXPLAT_FRE_ASSERT(TlsContext->IsServer);
+ if (TlsContext->TlsSecrets != NULL) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)NewPeerTrafficSecrets[i]->TrafficSecretSize;
+ memcpy(
+ TlsContext->TlsSecrets->ClientEarlyTrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ClientEarlyTrafficSecret = TRUE;
+ }
+ } else {
+ if (State->ReadKey == QUIC_PACKET_KEY_INITIAL) {
+ if (!QuicPacketKeyCreate(
+ TlsContext,
+ QUIC_PACKET_KEY_HANDSHAKE,
+ "peer handshake traffic secret",
+ NewPeerTrafficSecrets[i],
+ &State->ReadKeys[QUIC_PACKET_KEY_HANDSHAKE])) {
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ State->ReadKey = QUIC_PACKET_KEY_HANDSHAKE;
+ QuicTraceLogConnInfo(
+ SchannelReadHandshakeStart,
+ TlsContext->Connection,
+ "Reading Handshake data starts now");
+ if (TlsContext->TlsSecrets != NULL) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)NewPeerTrafficSecrets[i]->TrafficSecretSize;
+ if (TlsContext->IsServer) {
+ memcpy(
+ TlsContext->TlsSecrets->ClientHandshakeTrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ClientHandshakeTrafficSecret = TRUE;
+ } else {
+ memcpy(
+ TlsContext->TlsSecrets->ServerHandshakeTrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ServerHandshakeTrafficSecret = TRUE;
+ }
+ }
+ } else if (State->ReadKey == QUIC_PACKET_KEY_HANDSHAKE) {
+ if (!QuicPacketKeyCreate(
+ TlsContext,
+ QUIC_PACKET_KEY_1_RTT,
+ "peer application traffic secret",
+ NewPeerTrafficSecrets[i],
+ &State->ReadKeys[QUIC_PACKET_KEY_1_RTT])) {
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ State->ReadKey = QUIC_PACKET_KEY_1_RTT;
+ QuicTraceLogConnInfo(
+ SchannelRead1RttStart,
+ TlsContext->Connection,
+ "Reading 1-RTT data starts now");
+ if (TlsContext->TlsSecrets != NULL) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)NewPeerTrafficSecrets[i]->TrafficSecretSize;
+ if (TlsContext->IsServer) {
+ memcpy(
+ TlsContext->TlsSecrets->ClientTrafficSecret0,
+ NewPeerTrafficSecrets[i]->TrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ClientTrafficSecret0 = TRUE;
+ } else {
+ memcpy(
+ TlsContext->TlsSecrets->ServerTrafficSecret0,
+ NewPeerTrafficSecrets[i]->TrafficSecret,
+ NewPeerTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ServerTrafficSecret0 = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Update our "write" state based on any of our own keys being available
+ //
+ for (uint8_t i = 0; i < NewOwnTrafficSecretsCount; ++i) {
+ Result |= CXPLAT_TLS_RESULT_WRITE_KEY_UPDATED;
+ if (NewOwnTrafficSecrets[i]->TrafficSecretType == SecTrafficSecret_ClientEarlyData) {
+ CXPLAT_FRE_ASSERT(FALSE); // TODO - Finish the 0-RTT logic.
+ CXPLAT_FRE_ASSERT(!TlsContext->IsServer);
+ if (TlsContext->TlsSecrets != NULL) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)NewOwnTrafficSecrets[i]->TrafficSecretSize;
+ memcpy(
+ TlsContext->TlsSecrets->ClientEarlyTrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ClientEarlyTrafficSecret = TRUE;
+ }
+ } else {
+ if (State->WriteKey == QUIC_PACKET_KEY_INITIAL) {
+ if (!QuicPacketKeyCreate(
+ TlsContext,
+ QUIC_PACKET_KEY_HANDSHAKE,
+ "own handshake traffic secret",
+ NewOwnTrafficSecrets[i],
+ &State->WriteKeys[QUIC_PACKET_KEY_HANDSHAKE])) {
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ State->BufferOffsetHandshake =
+ State->BufferTotalLength + NewOwnTrafficSecrets[i]->MsgSequenceStart;
+ State->BufferOffset1Rtt = // HACK - Currently Schannel has weird output for 1-RTT start
+ State->BufferTotalLength + NewOwnTrafficSecrets[i]->MsgSequenceEnd;
+ State->WriteKey = QUIC_PACKET_KEY_HANDSHAKE;
+ QuicTraceLogConnInfo(
+ SchannelWriteHandshakeStart,
+ TlsContext->Connection,
+ "Writing Handshake data starts at %u",
+ State->BufferOffsetHandshake);
+ if (TlsContext->TlsSecrets != NULL) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)NewOwnTrafficSecrets[i]->TrafficSecretSize;
+ if (TlsContext->IsServer) {
+ memcpy(
+ TlsContext->TlsSecrets->ServerHandshakeTrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ServerHandshakeTrafficSecret = TRUE;
+ } else {
+ memcpy(
+ TlsContext->TlsSecrets->ClientHandshakeTrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ClientHandshakeTrafficSecret = TRUE;
+ }
+ }
+ } else if (State->WriteKey == QUIC_PACKET_KEY_HANDSHAKE) {
+ if (!TlsContext->IsServer && State->BufferOffsetHandshake == State->BufferOffset1Rtt) {
+ State->BufferOffset1Rtt = // HACK - Currently Schannel has weird output for 1-RTT start
+ State->BufferTotalLength + NewOwnTrafficSecrets[i]->MsgSequenceEnd;
+ } else {
+ if (!QuicPacketKeyCreate(
+ TlsContext,
+ QUIC_PACKET_KEY_1_RTT,
+ "own application traffic secret",
+ NewOwnTrafficSecrets[i],
+ &State->WriteKeys[QUIC_PACKET_KEY_1_RTT])) {
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+ //State->BufferOffset1Rtt = // Currently have to get the offset from the Handshake "end"
+ // State->BufferTotalLength + NewOwnTrafficSecrets[i]->MsgSequenceStart;
+ State->WriteKey = QUIC_PACKET_KEY_1_RTT;
+ QuicTraceLogConnInfo(
+ SchannelWrite1RttStart,
+ TlsContext->Connection,
+ "Writing 1-RTT data starts at %u",
+ State->BufferOffset1Rtt);
+ if (TlsContext->TlsSecrets != NULL) {
+ TlsContext->TlsSecrets->SecretLength = (uint8_t)NewOwnTrafficSecrets[i]->TrafficSecretSize;
+ if (TlsContext->IsServer) {
+ memcpy(
+ TlsContext->TlsSecrets->ServerTrafficSecret0,
+ NewOwnTrafficSecrets[i]->TrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ServerTrafficSecret0 = TRUE;
+ } else {
+ memcpy(
+ TlsContext->TlsSecrets->ClientTrafficSecret0,
+ NewOwnTrafficSecrets[i]->TrafficSecret,
+ NewOwnTrafficSecrets[i]->TrafficSecretSize);
+ TlsContext->TlsSecrets->IsSet.ClientTrafficSecret0 = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (SecStatus == SEC_E_OK) {
+ //
+ // We're done with the TlsSecrets.
+ //
+ TlsContext->TlsSecrets = NULL;
+ }
+
+ if (OutputTokenBuffer != NULL && OutputTokenBuffer->cbBuffer > 0) {
+ //
+ // There is output data to send back.
+ //
+ Result |= CXPLAT_TLS_RESULT_DATA;
+ uint32_t OutputTokenBuffersCount = 0;
+ for (uint32_t i = 0; i < OutSecBufferDesc.cBuffers; ++i) {
+ if (OutSecBufferDesc.pBuffers[i].BufferType == SECBUFFER_TOKEN) {
+ CXPLAT_DBG_ASSERT(OutputTokenBuffersCount < OutputBuffersCount);
+ CXPLAT_DBG_ASSERT(OutputBuffers[OutputTokenBuffersCount].Buffer == (uint8_t*)OutSecBufferDesc.pBuffers[i].pvBuffer);
+ OutputBuffers[OutputTokenBuffersCount++].Length = OutSecBufferDesc.pBuffers[i].cbBuffer;
+ QuicTraceLogConnInfo(
+ SchannelProducedData,
+ TlsContext->Connection,
+ "Produced %u bytes",
+ OutSecBufferDesc.pBuffers[i].cbBuffer);
+
+ }
+ }
+
+ TlsContext->GeneratedFirstPayload = TRUE;
+
+ }
+
+ break;
+
+ case SEC_E_INCOMPLETE_MESSAGE:
+
+ //
+ // None of the input buffer was consumed. There wasn't a complete TLS
+ // record for Schannel to process. Check to see if Schannel indicated
+ // how much more data they expect.
+ //
+ *InputBufferLength = 0;
+
+ if (MissingBuffer != NULL && MissingBuffer->cbBuffer != 0) {
+ QuicTraceLogConnInfo(
+ SchannelMissingData,
+ TlsContext->Connection,
+ "TLS message missing %u bytes of data",
+ MissingBuffer->cbBuffer);
+ }
+
+ break;
+
+ //
+ // Fall through here in other cases when we don't expect this.
+ //
+ __fallthrough;
+
+ default:
+ //
+ // Some other error occurred and we should indicate no data could be
+ // processed successfully.
+ //
+ if (AlertBuffer != NULL) {
+ if (AlertBuffer->cbBuffer < 2) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "TLS alert message received (invalid)");
+ } else {
+ State->AlertCode = ((uint8_t*)AlertBuffer->pvBuffer)[1];
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ State->AlertCode,
+ "TLS alert message received");
+ }
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ }
+ if (SecStatus == SEC_I_INCOMPLETE_CREDENTIALS &&
+ State->AlertCode == TLS1_ALERT_CLOSE_NOTIFY) {
+ //
+ // Work-around for Schannel sending the wrong TLS alert.
+ //
+ State->AlertCode = TLS1_ALERT_CERTIFICATE_REQUIRED;
+ }
+ *InputBufferLength = 0;
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "Accept/InitializeSecurityContext");
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ break;
+ }
+
+ if (TargetServerName != NULL) {
+ CXPLAT_FREE(TargetServerName, QUIC_POOL_TLS_SNI);
+ }
+
+ return Result;
+#endif
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsWriteEarlyData(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ size_t * InputBufferLength
+ )
+{
+ UNREFERENCED_PARAMETER(TlsContext);
+ UNREFERENCED_PARAMETER(InputBuffer);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ return FALSE;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+CxPlatTlsEncrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Inout_ CXPLAT_TLS_ENCRYPT_BUFFER* Buffer
+ )
+{
+#ifdef _KERNEL_MODE
+ UNREFERENCED_PARAMETER(TlsContext);
+ UNREFERENCED_PARAMETER(Buffer);
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "Encrypt not supported in kernel mode");
+ return FALSE;
+#else
+ SecBuffer* InSecBuffers = TlsContext->Workspace.InSecBuffers;
+ CXPLAT_TLS_RECORD_OVERHEAD Overhead;
+ CxPlatTlsGetRecordOverhead(TlsContext, &Overhead);
+
+ if (Buffer->Capacity < Overhead.MaxHeader + Buffer->DataLength + Overhead.MaxTrailer) {
+ return FALSE;
+ }
+ if (Buffer->DataOffset != Overhead.MaxHeader) {
+ return FALSE;
+ }
+
+ SecBufferDesc InSecBufferDesc;
+ InSecBufferDesc.ulVersion = SECBUFFER_VERSION;
+ InSecBufferDesc.pBuffers = InSecBuffers;
+ InSecBufferDesc.cBuffers = 0;
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_STREAM_HEADER;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = (uint32_t)Overhead.MaxHeader;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = Buffer->Base;
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_DATA;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = (uint32_t)Buffer->DataLength;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = Buffer->Base + Buffer->DataOffset;
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_STREAM_TRAILER;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = (uint32_t)Overhead.MaxTrailer;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer =
+ Buffer->Base + Buffer->DataOffset + Buffer->DataLength;
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_EMPTY;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = 0;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = NULL;
+
+ SECURITY_STATUS SecStatus =
+ EncryptMessage(
+ &TlsContext->SchannelContext,
+ 0,
+ &InSecBufferDesc,
+ 0);
+
+ if (SecStatus != SEC_E_OK) {
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "EncryptMessage");
+ return FALSE;
+ }
+
+ Buffer->DataLength =
+ InSecBuffers[0].cbBuffer +
+ InSecBuffers[1].cbBuffer +
+ InSecBuffers[2].cbBuffer;
+ return TRUE;
+#endif
+}
+
+#ifndef _KERNEL_MODE
+static
+BOOLEAN
+CopyInputToInternalBuffer(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(InputLength) const uint8_t* Input,
+ _In_ uint32_t InputLength
+ )
+{
+ if (TlsContext->InputBuffer == NULL || TlsContext->InputBufferAllocLength == 0) {
+ TlsContext->InputBufferAllocLength = 65536;
+ TlsContext->InputBufferLength = 0;
+ TlsContext->InputBuffer = CXPLAT_ALLOC_NONPAGED(TlsContext->InputBufferAllocLength, QUIC_POOL_TLS_BUFFER);
+ if (TlsContext->InputBuffer == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS input Buffer",
+ TlsContext->InputBufferAllocLength);
+ TlsContext->InputBufferAllocLength = 0;
+ return FALSE;
+ }
+ }
+
+ CXPLAT_DBG_ASSERT(TlsContext->InputBuffer != NULL);
+ CXPLAT_DBG_ASSERT(TlsContext->InputBufferAllocLength >= TlsContext->InputBufferLength);
+
+ uint8_t* CurrentInputBuffer = TlsContext->InputBuffer;
+ uint32_t CurrentInputBufferLength = TlsContext->InputBufferLength;
+ uint32_t CurrentInputBufferAllocLength = TlsContext->InputBufferAllocLength;
+
+ if (InputLength > UINT32_MAX - CurrentInputBufferLength) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "TLS input buffer too large");
+ return FALSE;
+ }
+
+ uint32_t RequiredLength = CurrentInputBufferLength + InputLength;
+ if (RequiredLength > CurrentInputBufferAllocLength) {
+ uint32_t NewInputBufferAllocLength = CurrentInputBufferAllocLength;
+ while (RequiredLength > NewInputBufferAllocLength) {
+ if (NewInputBufferAllocLength > UINT32_MAX / 2) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "TLS input buffer too large");
+ return FALSE;
+ }
+ NewInputBufferAllocLength *= 2;
+ }
+
+ uint8_t* NewInputBuffer = CXPLAT_ALLOC_NONPAGED(NewInputBufferAllocLength, QUIC_POOL_TLS_BUFFER);
+ if (NewInputBuffer == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS input Buffer",
+ NewInputBufferAllocLength);
+ return FALSE;
+ }
+
+ if (CurrentInputBufferLength > 0) {
+ CXPLAT_ANALYSIS_ASSUME(CurrentInputBufferLength <= CurrentInputBufferAllocLength);
+ CXPLAT_ANALYSIS_ASSUME(CurrentInputBufferLength <= NewInputBufferAllocLength);
+#pragma warning(suppress:6385) // Bounds validated above; static analysis loses the relation here.
+ CxPlatCopyMemory(NewInputBuffer, CurrentInputBuffer, CurrentInputBufferLength);
+ }
+
+ CXPLAT_FREE(CurrentInputBuffer, QUIC_POOL_TLS_BUFFER);
+ TlsContext->InputBuffer = NewInputBuffer;
+ TlsContext->InputBufferAllocLength = NewInputBufferAllocLength;
+ }
+
+ CXPLAT_DBG_ASSERT(InputLength <= TlsContext->InputBufferAllocLength - TlsContext->InputBufferLength);
+ CxPlatCopyMemory(TlsContext->InputBuffer + TlsContext->InputBufferLength, Input, InputLength);
+ TlsContext->InputBufferLength += InputLength;
+ return TRUE;
+}
+
+static
+BOOLEAN
+CopyOutputToInternalBuffer(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(OutputLength) const uint8_t* Output,
+ _In_ uint32_t OutputLength
+ )
+{
+ if (TlsContext->OutputBuffer == NULL || TlsContext->OutputBufferAllocLength == 0) {
+ TlsContext->OutputBufferAllocLength = 65536;
+ TlsContext->OutputBufferLength = 0;
+ TlsContext->OutputBuffer = CXPLAT_ALLOC_NONPAGED(TlsContext->OutputBufferAllocLength, QUIC_POOL_TLS_BUFFER);
+ if (TlsContext->OutputBuffer == NULL) {
+ TlsContext->OutputBufferAllocLength = 0;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS output Buffer",
+ TlsContext->OutputBufferAllocLength);
+ return FALSE;
+ }
+ }
+
+ CXPLAT_DBG_ASSERT(TlsContext->OutputBuffer != NULL);
+ CXPLAT_DBG_ASSERT(TlsContext->OutputBufferAllocLength >= TlsContext->OutputBufferLength);
+
+ uint8_t* CurrentOutputBuffer = TlsContext->OutputBuffer;
+ uint32_t CurrentOutputBufferLength = TlsContext->OutputBufferLength;
+ uint32_t CurrentOutputBufferAllocLength = TlsContext->OutputBufferAllocLength;
+
+ if (OutputLength > UINT32_MAX - CurrentOutputBufferLength) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "TLS output buffer too large");
+ return FALSE;
+ }
+
+ uint32_t RequiredLength = CurrentOutputBufferLength + OutputLength;
+ if (RequiredLength > CurrentOutputBufferAllocLength) {
+ uint32_t NewOutputBufferAllocLength = CurrentOutputBufferAllocLength;
+ while (RequiredLength > NewOutputBufferAllocLength) {
+ if (NewOutputBufferAllocLength > UINT32_MAX / 2) {
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "TLS output buffer too large");
+ return FALSE;
+ }
+ NewOutputBufferAllocLength *= 2;
+ }
+
+ uint8_t* NewOutputBuffer = CXPLAT_ALLOC_NONPAGED(NewOutputBufferAllocLength, QUIC_POOL_TLS_BUFFER);
+ if (NewOutputBuffer == NULL) {
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "TLS output Buffer",
+ NewOutputBufferAllocLength);
+ return FALSE;
+ }
+
+ if (CurrentOutputBufferLength > 0) {
+ CXPLAT_ANALYSIS_ASSUME(CurrentOutputBufferLength <= CurrentOutputBufferAllocLength);
+ CXPLAT_ANALYSIS_ASSUME(CurrentOutputBufferLength <= NewOutputBufferAllocLength);
+#pragma warning(suppress:6385) // Bounds validated above; static analysis loses the relation here.
+ CxPlatCopyMemory(NewOutputBuffer, CurrentOutputBuffer, CurrentOutputBufferLength);
+ }
+
+ CXPLAT_FREE(CurrentOutputBuffer, QUIC_POOL_TLS_BUFFER);
+ TlsContext->OutputBuffer = NewOutputBuffer;
+ TlsContext->OutputBufferAllocLength = NewOutputBufferAllocLength;
+ }
+
+ CXPLAT_DBG_ASSERT(OutputLength <= TlsContext->OutputBufferAllocLength - TlsContext->OutputBufferLength);
+ CxPlatCopyMemory(TlsContext->OutputBuffer + TlsContext->OutputBufferLength, Output, OutputLength);
+ TlsContext->OutputBufferLength += OutputLength;
+ return TRUE;
+}
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+CXPLAT_TLS_RESULT_FLAGS
+CxPlatTlsDecrypt(
+ _In_ CXPLAT_TLS* TlsContext,
+ _In_reads_bytes_(*InputBufferLength)
+ const uint8_t * InputBuffer,
+ _Inout_ uint32_t* InputBufferLength,
+ _Inout_updates_bytes_opt_(*OutputBufferLength)
+ uint8_t* OutputBuffer,
+ _Inout_ uint32_t* OutputBufferLength
+ )
+{
+#ifdef _KERNEL_MODE
+ UNREFERENCED_PARAMETER(TlsContext);
+ UNREFERENCED_PARAMETER(InputBuffer);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(OutputBuffer);
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ QuicTraceEvent(
+ TlsError,
+ "[ tls][%p] ERROR, %s.",
+ TlsContext->Connection,
+ "Decrypt not supported in kernel mode");
+ return CXPLAT_TLS_RESULT_ERROR;
+#else
+ CXPLAT_TLS_RESULT_FLAGS Result = 0;
+ uint32_t OutputBufferCapacity = *OutputBufferLength;
+ SecBuffer* InSecBuffers = TlsContext->Workspace.InSecBuffers;
+ SecBufferDesc InSecBufferDesc;
+ BOOLEAN Copied = FALSE;
+ InSecBufferDesc.ulVersion = SECBUFFER_VERSION;
+ InSecBufferDesc.pBuffers = InSecBuffers;
+ InSecBufferDesc.cBuffers = 0;
+
+ if (TlsContext->OutputBufferLength > 0) {
+ if (OutputBuffer == NULL || OutputBufferCapacity < TlsContext->OutputBufferLength) {
+ *OutputBufferLength = TlsContext->OutputBufferLength;
+ Result |= CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL;
+ return Result;
+ }
+ CxPlatCopyMemory(OutputBuffer, TlsContext->OutputBuffer, TlsContext->OutputBufferLength);
+ *OutputBufferLength = TlsContext->OutputBufferLength;
+ TlsContext->OutputBufferLength = 0;
+ return Result;
+ }
+
+ if (*InputBufferLength == 0 && TlsContext->InputBufferLength == 0) {
+ *OutputBufferLength = 0;
+ return Result;
+ }
+ if (TlsContext->InputBufferLength > 0) {
+ if (!CopyInputToInternalBuffer(TlsContext, InputBuffer, *InputBufferLength)) {
+ *OutputBufferLength = 0;
+ return Result;
+ }
+ Copied = TRUE;
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_DATA;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = TlsContext->InputBufferLength;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = (void *)TlsContext->InputBuffer;
+ } else {
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_DATA;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = *InputBufferLength;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = (void *)InputBuffer;
+ }
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_EMPTY;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = 0;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = NULL;
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_EMPTY;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = 0;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = NULL;
+
+ InSecBuffers[InSecBufferDesc.cBuffers].BufferType = SECBUFFER_EMPTY;
+ InSecBuffers[InSecBufferDesc.cBuffers].cbBuffer = 0;
+ InSecBuffers[InSecBufferDesc.cBuffers++].pvBuffer = NULL;
+
+ SECURITY_STATUS SecStatus =
+ DecryptMessage(
+ &TlsContext->SchannelContext,
+ &InSecBufferDesc,
+ 0,
+ NULL);
+
+ SecBuffer* OutputDataBuffer = NULL;
+ SecBuffer* ExtraBuffer = NULL;
+ switch (SecStatus) {
+ case SEC_E_OK:
+ for (uint32_t i = 0; i < InSecBufferDesc.cBuffers; ++i) {
+ if (InSecBuffers[i].BufferType == SECBUFFER_DATA) {
+ OutputDataBuffer = &InSecBuffers[i];
+ } else if (InSecBuffers[i].BufferType == SECBUFFER_EXTRA) {
+ ExtraBuffer = &InSecBuffers[i];
+ }
+ }
+ break;
+ case SEC_I_RENEGOTIATE:
+ Result |= CXPLAT_TLS_RESULT_RENEGOTIATE;
+ *InputBufferLength = 0;
+ *OutputBufferLength = 0;
+ break;
+ case SEC_E_INCOMPLETE_MESSAGE:
+ if (!Copied) {
+ if (!CopyInputToInternalBuffer(TlsContext, InputBuffer, *InputBufferLength)) {
+ *OutputBufferLength = 0;
+ return Result;
+ }
+ }
+ *OutputBufferLength = 0;
+ break;
+ default:
+ QuicTraceEvent(
+ TlsErrorStatus,
+ "[ tls][%p] ERROR, %u, %s.",
+ TlsContext->Connection,
+ SecStatus,
+ "DecryptMessage");
+ *InputBufferLength = 0;
+ *OutputBufferLength = 0;
+ break;
+ }
+
+ if (SecStatus == SEC_E_OK && OutputDataBuffer != NULL) {
+ if (OutputBuffer == NULL || OutputBufferCapacity < OutputDataBuffer->cbBuffer) {
+ if (!CopyOutputToInternalBuffer(TlsContext,
+ (const uint8_t*)OutputDataBuffer->pvBuffer,
+ OutputDataBuffer->cbBuffer)) {
+ Result |= CXPLAT_TLS_RESULT_ERROR;
+ *OutputBufferLength = 0;
+ return Result;
+ }
+ *OutputBufferLength = OutputDataBuffer->cbBuffer;
+ Result |= CXPLAT_TLS_RESULT_BUFFER_TOO_SMALL;
+ return Result;
+ }
+ CxPlatCopyMemory(OutputBuffer, OutputDataBuffer->pvBuffer, OutputDataBuffer->cbBuffer);
+ *OutputBufferLength = OutputDataBuffer->cbBuffer;
+ TlsContext->InputBufferLength = 0; // Clear the recv buffer since we've consumed it all
+ }
+
+ if (SecStatus == SEC_E_OK && ExtraBuffer != NULL && ExtraBuffer->cbBuffer > 0) {
+ CxPlatMoveMemory(
+ (uint8_t*)InputBuffer + (*InputBufferLength - ExtraBuffer->cbBuffer),
+ ExtraBuffer->pvBuffer,
+ ExtraBuffer->cbBuffer);
+ *InputBufferLength -= ExtraBuffer->cbBuffer;
+ }
+ if (!(SecStatus == SEC_E_OK && OutputDataBuffer != NULL)) {
+ *OutputBufferLength = 0;
+ }
+ return Result;
+#endif
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+CxPlatTlsGetRecordOverhead(
+ _In_ CXPLAT_TLS* TlsContext,
+ _Out_ CXPLAT_TLS_RECORD_OVERHEAD* Overhead
+ )
+{
+ SecPkgContext_StreamSizes Sizes;
+ QueryContextAttributes(&TlsContext->SchannelContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
+ Overhead->MaxHeader = Sizes.cbHeader;
+ Overhead->MaxTrailer = Sizes.cbTrailer;
+}
+
_IRQL_requires_max_(PASSIVE_LEVEL)
QUIC_STATUS
CxPlatSecConfigParamSet(
diff --git a/src/rs/ffi/linux_bindings.rs b/src/rs/ffi/linux_bindings.rs
index 93157fdee3..95be9f0c76 100644
--- a/src/rs/ffi/linux_bindings.rs
+++ b/src/rs/ffi/linux_bindings.rs
@@ -6655,10 +6655,13 @@ pub struct QUIC_API_TABLE {
pub ExecutionDelete: QUIC_EXECUTION_DELETE_FN,
pub ExecutionPoll: QUIC_EXECUTION_POLL_FN,
pub RegistrationClose2: QUIC_REGISTRATION_CLOSE2_FN,
+ pub ConnectionQmuxOpen: QUIC_CONNECTION_OPEN_FN,
+ pub ConnectionQmuxOpenInPartition: QUIC_CONNECTION_OPEN_IN_PARTITION_FN,
+ pub ListenerQmuxOpen: QUIC_LISTENER_OPEN_FN,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
- ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 304usize];
+ ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 328usize];
["Alignment of QUIC_API_TABLE"][::std::mem::align_of::() - 8usize];
["Offset of field: QUIC_API_TABLE::SetContext"]
[::std::mem::offset_of!(QUIC_API_TABLE, SetContext) - 0usize];
@@ -6740,6 +6743,12 @@ const _: () = {
[::std::mem::offset_of!(QUIC_API_TABLE, ExecutionPoll) - 288usize];
["Offset of field: QUIC_API_TABLE::RegistrationClose2"]
[::std::mem::offset_of!(QUIC_API_TABLE, RegistrationClose2) - 296usize];
+ ["Offset of field: QUIC_API_TABLE::ConnectionQmuxOpen"]
+ [::std::mem::offset_of!(QUIC_API_TABLE, ConnectionQmuxOpen) - 304usize];
+ ["Offset of field: QUIC_API_TABLE::ConnectionQmuxOpenInPartition"]
+ [::std::mem::offset_of!(QUIC_API_TABLE, ConnectionQmuxOpenInPartition) - 312usize];
+ ["Offset of field: QUIC_API_TABLE::ListenerQmuxOpen"]
+ [::std::mem::offset_of!(QUIC_API_TABLE, ListenerQmuxOpen) - 320usize];
};
pub const QUIC_STATUS_SUCCESS: QUIC_STATUS = 0;
pub const QUIC_STATUS_PENDING: QUIC_STATUS = 4294967294;
diff --git a/src/rs/ffi/win_bindings.rs b/src/rs/ffi/win_bindings.rs
index 3097ac9c9e..cbffadf452 100644
--- a/src/rs/ffi/win_bindings.rs
+++ b/src/rs/ffi/win_bindings.rs
@@ -6675,10 +6675,13 @@ pub struct QUIC_API_TABLE {
pub ExecutionDelete: QUIC_EXECUTION_DELETE_FN,
pub ExecutionPoll: QUIC_EXECUTION_POLL_FN,
pub RegistrationClose2: QUIC_REGISTRATION_CLOSE2_FN,
+ pub ConnectionQmuxOpen: QUIC_CONNECTION_OPEN_FN,
+ pub ConnectionQmuxOpenInPartition: QUIC_CONNECTION_OPEN_IN_PARTITION_FN,
+ pub ListenerQmuxOpen: QUIC_LISTENER_OPEN_FN,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
- ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 304usize];
+ ["Size of QUIC_API_TABLE"][::std::mem::size_of::() - 328usize];
["Alignment of QUIC_API_TABLE"][::std::mem::align_of::() - 8usize];
["Offset of field: QUIC_API_TABLE::SetContext"]
[::std::mem::offset_of!(QUIC_API_TABLE, SetContext) - 0usize];
@@ -6760,6 +6763,12 @@ const _: () = {
[::std::mem::offset_of!(QUIC_API_TABLE, ExecutionPoll) - 288usize];
["Offset of field: QUIC_API_TABLE::RegistrationClose2"]
[::std::mem::offset_of!(QUIC_API_TABLE, RegistrationClose2) - 296usize];
+ ["Offset of field: QUIC_API_TABLE::ConnectionQmuxOpen"]
+ [::std::mem::offset_of!(QUIC_API_TABLE, ConnectionQmuxOpen) - 304usize];
+ ["Offset of field: QUIC_API_TABLE::ConnectionQmuxOpenInPartition"]
+ [::std::mem::offset_of!(QUIC_API_TABLE, ConnectionQmuxOpenInPartition) - 312usize];
+ ["Offset of field: QUIC_API_TABLE::ListenerQmuxOpen"]
+ [::std::mem::offset_of!(QUIC_API_TABLE, ListenerQmuxOpen) - 320usize];
};
pub const QUIC_STATUS_SUCCESS: QUIC_STATUS = 0;
pub const QUIC_STATUS_PENDING: QUIC_STATUS = 459749;
diff --git a/src/rs/lib.rs b/src/rs/lib.rs
index c52ceed3a4..6202d86230 100644
--- a/src/rs/lib.rs
+++ b/src/rs/lib.rs
@@ -24,9 +24,9 @@ pub mod ffi;
pub use error::{Status, StatusCode};
mod types;
pub use types::{
- BufferRef, ConnectionEvent, ConnectionShutdownFlags, DatagramSendState, ListenerEvent,
- NewConnectionInfo, ReceiveFlags, SendFlags, StreamEvent, StreamOpenFlags, StreamShutdownFlags,
- StreamStartFlags, TlsProvider,
+ BufferRef, ConnectionEvent, ConnectionSendResumptionFlags, ConnectionShutdownFlags,
+ DatagramSendState, ListenerEvent, NewConnectionInfo, ReceiveFlags, SendFlags, StreamEvent,
+ StreamOpenFlags, StreamShutdownFlags, StreamStartFlags, TlsProvider,
};
mod settings;
pub use settings::{ServerResumptionLevel, Settings};
@@ -899,6 +899,24 @@ extern "C" fn raw_conn_callback(
impl Connection {
pub fn open(registration: &Registration, handler: F) -> Result
+ where
+ F: FnMut(ConnectionRef, ConnectionEvent) -> Result<(), Status> + 'static,
+ {
+ Self::open_common(registration, handler, false)
+ }
+
+ pub fn open_qmux(registration: &Registration, handler: F) -> Result
+ where
+ F: FnMut(ConnectionRef, ConnectionEvent) -> Result<(), Status> + 'static,
+ {
+ Self::open_common(registration, handler, true)
+ }
+
+ fn open_common(
+ registration: &Registration,
+ handler: F,
+ is_qmux: bool,
+ ) -> Result
where
F: FnMut(ConnectionRef, ConnectionEvent) -> Result<(), Status> + 'static,
{
@@ -907,12 +925,21 @@ impl Connection {
let b: Box> = Box::new(Box::new(handler));
let ctx = Box::into_raw(b);
let status = unsafe {
- Api::ffi_ref().ConnectionOpen.unwrap()(
- registration.handle,
- Some(raw_conn_callback),
- ctx as *mut c_void,
- std::ptr::addr_of_mut!(handle),
- )
+ if !is_qmux {
+ Api::ffi_ref().ConnectionOpen.unwrap()(
+ registration.handle,
+ Some(raw_conn_callback),
+ ctx as *mut c_void,
+ std::ptr::addr_of_mut!(handle),
+ )
+ } else {
+ Api::ffi_ref().ConnectionQmuxOpen.unwrap()(
+ registration.handle,
+ Some(raw_conn_callback),
+ ctx as *mut c_void,
+ std::ptr::addr_of_mut!(handle),
+ )
+ }
};
Status::ok_from_raw(status).inspect_err(|_| {
// attach memory back on failure
@@ -1017,6 +1044,26 @@ impl Connection {
Status::ok_from_raw(status)
}
+ pub fn send_resumption_ticket(
+ &self,
+ flags: ConnectionSendResumptionFlags,
+ resumption_app_data: Option<&[u8]>,
+ ) -> Result<(), Status> {
+ let status = unsafe {
+ Api::ffi_ref().ConnectionSendResumptionTicket.unwrap()(
+ self.handle,
+ flags.bits(),
+ resumption_app_data
+ .map(|data| data.len() as u16)
+ .unwrap_or(0),
+ resumption_app_data
+ .map(|data| data.as_ptr() as *const u8)
+ .unwrap_or(std::ptr::null()),
+ )
+ };
+ Status::ok_from_raw(status)
+ }
+
pub fn resumption_ticket_validation_complete(&self, result: BOOLEAN) -> Result<(), Status> {
let status = unsafe {
Api::ffi_ref()
@@ -1075,6 +1122,24 @@ extern "C" fn raw_listener_callback(
impl Listener {
pub fn open(registration: &Registration, handler: F) -> Result
+ where
+ F: Fn(ListenerRef, ListenerEvent) -> Result<(), Status> + 'static,
+ {
+ Self::open_common(registration, handler, false)
+ }
+
+ pub fn open_qmux(registration: &Registration, handler: F) -> Result
+ where
+ F: Fn(ListenerRef, ListenerEvent) -> Result<(), Status> + 'static,
+ {
+ Self::open_common(registration, handler, true)
+ }
+
+ fn open_common(
+ registration: &Registration,
+ handler: F,
+ is_qmux: bool,
+ ) -> Result
where
F: Fn(ListenerRef, ListenerEvent) -> Result<(), Status> + 'static,
{
@@ -1083,12 +1148,21 @@ impl Listener {
let b: Box> = Box::new(Box::new(handler));
let ctx = Box::into_raw(b);
let status = unsafe {
- Api::ffi_ref().ListenerOpen.unwrap()(
- registration.handle,
- Some(raw_listener_callback),
- ctx as *mut c_void,
- std::ptr::addr_of_mut!(handle),
- )
+ if !is_qmux {
+ Api::ffi_ref().ListenerOpen.unwrap()(
+ registration.handle,
+ Some(raw_listener_callback),
+ ctx as *mut c_void,
+ std::ptr::addr_of_mut!(handle),
+ )
+ } else {
+ Api::ffi_ref().ListenerQmuxOpen.unwrap()(
+ registration.handle,
+ Some(raw_listener_callback),
+ ctx as *mut c_void,
+ std::ptr::addr_of_mut!(handle),
+ )
+ }
};
Status::ok_from_raw(status).inspect_err(|_| {
let _ = unsafe { Box::from_raw(ctx) };
diff --git a/src/rs/types.rs b/src/rs/types.rs
index 9142ef9fdd..f9ac8cbfcb 100644
--- a/src/rs/types.rs
+++ b/src/rs/types.rs
@@ -479,6 +479,16 @@ bitflags::bitflags! {
}
}
+bitflags::bitflags! {
+ /// Controls send resumption ticket behavior.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub struct ConnectionSendResumptionFlags: crate::ffi::QUIC_SEND_RESUMPTION_FLAGS {
+ const NONE = crate::ffi::QUIC_SEND_RESUMPTION_FLAGS_QUIC_SEND_RESUMPTION_FLAG_NONE;
+ /// This is the final resumption ticket.
+ const FINAL = crate::ffi::QUIC_SEND_RESUMPTION_FLAGS_QUIC_SEND_RESUMPTION_FLAG_FINAL;
+ }
+}
+
bitflags::bitflags! {
/// Controls stream open behavior.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/src/tools/sample/sample.c b/src/tools/sample/sample.c
index d13993fe7e..e679248dbe 100644
--- a/src/tools/sample/sample.c
+++ b/src/tools/sample/sample.c
@@ -50,6 +50,7 @@
#include
#endif
#include "msquic.h"
+#include
#include
#include
@@ -82,7 +83,7 @@ const uint64_t IdleTimeoutMs = 1000;
//
// The length of buffer sent over the streams in the protocol.
//
-const uint32_t SendBufferLength = 100;
+const uint32_t SendBufferLength = 100000;
//
// The QUIC API/function table returned from MsQuicOpen2. It contains all the
@@ -126,11 +127,14 @@ void PrintUsage()
"Usage:\n"
"\n"
" quicsample.exe -client -unsecure -target:{IPAddress|Hostname} [-ticket:]\n"
+ " quicsample.exe -client -qmux -unsecure -target:{IPAddress|Hostname} [-ticket:]\n"
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
" quicsample.exe -multiclient -count: -unsecure -target:{IPAddress|Hostname}\n"
#endif
" quicsample.exe -server -cert_hash:<...>\n"
" quicsample.exe -server -cert_file:<...> -key_file:<...> [-password:<...>]\n"
+ " quicsample.exe -server -qmux -cert_hash:<...>\n"
+ " quicsample.exe -server -qmux -cert_file:<...> -key_file:<...> [-password:<...>]\n"
);
}
@@ -384,7 +388,7 @@ ServerStreamCallback(
//
// Data was received from the peer on the stream.
//
- printf("[strm][%p] Data received\n", Stream);
+ printf("[strm][%p] Data received %" PRIu64 " bytes, flags=0x%x\n", Stream, Event->RECEIVE.TotalBufferLength, Event->RECEIVE.Flags);
break;
case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN:
//
@@ -433,8 +437,13 @@ ServerConnectionCallback(
//
// The handshake has completed for the connection.
//
- printf("[conn][%p] Connected\n", Connection);
- MsQuic->ConnectionSendResumptionTicket(Connection, QUIC_SEND_RESUMPTION_FLAG_NONE, 0, NULL);
+ if (Event->CONNECTED.NegotiatedAlpnLength > 0) {
+ printf("[conn][%p] Connected, ALPN=%.*s\n", Connection, (int)Event->CONNECTED.NegotiatedAlpnLength, Event->CONNECTED.NegotiatedAlpn);
+ } else {
+ printf("[conn][%p] Connected\n", Connection);
+ }
+ QUIC_STATUS Status = MsQuic->ConnectionSendResumptionTicket(Connection, QUIC_SEND_RESUMPTION_FLAG_NONE, 0, NULL);
+ printf("[conn][%p] ConnectionSendResumptionTicket returned 0x%x\n", Connection, Status);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT:
//
@@ -652,9 +661,16 @@ RunServer(
//
// Create/allocate a new listener object.
//
- if (QUIC_FAILED(Status = MsQuic->ListenerOpen(Registration, ServerListenerCallback, NULL, &Listener))) {
- printf("ListenerOpen failed, 0x%x!\n", Status);
- goto Error;
+ if (!GetFlag(argc, argv, "qmux")) {
+ if (QUIC_FAILED(Status = MsQuic->ListenerOpen(Registration, ServerListenerCallback, NULL, &Listener))) {
+ printf("ListenerOpen failed, 0x%x!\n", Status);
+ goto Error;
+ }
+ } else {
+ if (QUIC_FAILED(Status = MsQuic->ListenerQmuxOpen(Registration, ServerListenerCallback, NULL, &Listener))) {
+ printf("ListenerQmuxOpen failed, 0x%x!\n", Status);
+ goto Error;
+ }
}
//
@@ -705,7 +721,7 @@ ClientStreamCallback(
//
// Data was received from the peer on the stream.
//
- printf("[strm][%p] Data received\n", Stream);
+ printf("[strm][%p] Data received %" PRIu64 " bytes, flags=0x%x\n", Stream, Event->RECEIVE.TotalBufferLength, Event->RECEIVE.Flags);
break;
case QUIC_STREAM_EVENT_PEER_SEND_ABORTED:
//
@@ -799,6 +815,7 @@ ClientSend(
}
}
+BOOLEAN TicketRecvd = FALSE;
//
// The clients's callback for connection events from MsQuic.
//
@@ -826,7 +843,11 @@ ClientConnectionCallback(
//
// The handshake has completed for the connection.
//
- printf("[conn][%p] Connected\n", Connection);
+ if (Event->CONNECTED.NegotiatedAlpnLength > 0) {
+ printf("[conn][%p] Connected, ALPN=%.*s\n", Connection, (int)Event->CONNECTED.NegotiatedAlpnLength, Event->CONNECTED.NegotiatedAlpn);
+ } else {
+ printf("[conn][%p] Connected\n", Connection);
+ }
ClientSend(Connection);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT:
@@ -863,10 +884,13 @@ ClientConnectionCallback(
// received from the server.
//
printf("[conn][%p] Resumption ticket received (%u bytes):\n", Connection, Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength);
- for (uint32_t i = 0; i < Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength; i++) {
- printf("%.2X", (uint8_t)Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket[i]);
+ if (!TicketRecvd) {
+ for (uint32_t i = 0; i < Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength; i++) {
+ fprintf(stderr, "%.2X", (uint8_t)Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket[i]);
+ }
+ fprintf(stderr, "\n");
}
- printf("\n");
+ TicketRecvd = TRUE;
break;
case QUIC_CONNECTION_EVENT_IDEAL_PROCESSOR_CHANGED:
printf(
@@ -895,6 +919,12 @@ ClientLoadConfiguration(
//
Settings.IdleTimeoutMs = IdleTimeoutMs;
Settings.IsSet.IdleTimeoutMs = TRUE;
+ // Settings.KeepAliveIntervalMs = 1000;
+ // Settings.IsSet.KeepAliveIntervalMs = TRUE;
+ Settings.PeerBidiStreamCount = 100;
+ Settings.IsSet.PeerBidiStreamCount = TRUE;
+ Settings.PeerUnidiStreamCount = 100;
+ Settings.IsSet.PeerUnidiStreamCount = TRUE;
//
// Configures a default client configuration, optionally disabling
@@ -954,9 +984,16 @@ RunClient(
//
// Allocate a new connection object.
//
- if (QUIC_FAILED(Status = MsQuic->ConnectionOpen(Registration, ClientConnectionCallback, NULL, &Connection))) {
- printf("ConnectionOpen failed, 0x%x!\n", Status);
- goto Error;
+ if (!GetFlag(argc, argv, "qmux")) {
+ if (QUIC_FAILED(Status = MsQuic->ConnectionOpen(Registration, ClientConnectionCallback, NULL, &Connection))) {
+ printf("ConnectionOpen failed, 0x%x!\n", Status);
+ goto Error;
+ }
+ } else {
+ if (QUIC_FAILED(Status = MsQuic->ConnectionQmuxOpen(Registration, ClientConnectionCallback, NULL, &Connection))) {
+ printf("ConnectionQmuxOpen failed, 0x%x!\n", Status);
+ goto Error;
+ }
}
if ((ResumptionTicketString = GetValue(argc, argv, "ticket")) != NULL) {
@@ -979,6 +1016,40 @@ RunClient(
}
}
+ if (ResumptionTicketString != NULL) {
+ HQUIC Stream = NULL;
+ uint8_t* SendBufferRaw;
+ QUIC_BUFFER* SendBuffer;
+
+ if (QUIC_FAILED(Status = MsQuic->StreamOpen(Connection, QUIC_STREAM_OPEN_FLAG_0_RTT, ClientStreamCallback, NULL, &Stream))) {
+ printf("StreamOpen failed, 0x%x!\n", Status);
+ goto Error;
+ }
+
+ printf("[strm][%p] Starting...\n", Stream);
+
+
+ //
+ // Allocates and builds the buffer to send over the stream.
+ //
+ SendBufferRaw = (uint8_t*)malloc(sizeof(QUIC_BUFFER) + SendBufferLength);
+ if (SendBufferRaw == NULL) {
+ printf("SendBuffer allocation failed!\n");
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ goto Error;
+ }
+ SendBuffer = (QUIC_BUFFER*)SendBufferRaw;
+ SendBuffer->Buffer = SendBufferRaw + sizeof(QUIC_BUFFER);
+ SendBuffer->Length = SendBufferLength;
+
+ printf("[strm][%p] Sending data...\n", Stream);
+
+ if (QUIC_FAILED(Status = MsQuic->StreamSend(Stream, SendBuffer, 1, QUIC_SEND_FLAG_ALLOW_0_RTT | QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, SendBuffer))) {
+ printf("StreamSend failed, 0x%x!\n", Status);
+ free(SendBufferRaw);
+ goto Error;
+ }
+ }
//
// Get the target / server name or IP from the command line.
//