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. //