diff --git a/src/core/binding.c b/src/core/binding.c index 348e1228d0..c873096d7a 100644 --- a/src/core/binding.c +++ b/src/core/binding.c @@ -560,46 +560,91 @@ _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN QuicBindingAddSourceConnectionID( _In_ QUIC_BINDING* Binding, - _In_ QUIC_CID_HASH_ENTRY* SourceCid + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_CID_SLIST_ENTRY* SourceCid ) { - return QuicLookupAddLocalCid(&Binding->Lookup, SourceCid, NULL); + return QuicLookupAddLocalCid(&Binding->Lookup, Connection, SourceCid, NULL); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOLEAN +QuicBindingAddAllSourceConnectionIDs( + _In_ QUIC_BINDING* Binding, + _In_ QUIC_CONNECTION* Connection + ) +{ + for (CXPLAT_SLIST_ENTRY* Link = Connection->SourceCids.Next; + Link != NULL; + Link = Link->Next) { + + QUIC_CID_SLIST_ENTRY* Entry = + CXPLAT_CONTAINING_RECORD( + Link, + QUIC_CID_SLIST_ENTRY, + Link); + if (!QuicBindingAddSourceConnectionID(Binding, Connection, Entry)) { + return FALSE; + } + } + + return TRUE; } _IRQL_requires_max_(DISPATCH_LEVEL) void QuicBindingRemoveSourceConnectionID( _In_ QUIC_BINDING* Binding, - _In_ QUIC_CID_HASH_ENTRY* SourceCid, - _In_ CXPLAT_SLIST_ENTRY** Entry + _In_ QUIC_CID_HASH_ENTRY* SourceCid ) { - QuicLookupRemoveLocalCid(&Binding->Lookup, SourceCid, Entry); + QuicLookupRemoveLocalCid(&Binding->Lookup, SourceCid); } _IRQL_requires_max_(DISPATCH_LEVEL) void -QuicBindingRemoveConnection( +QuicBindingRemoveAllSourceConnectionIDs( _In_ QUIC_BINDING* Binding, _In_ QUIC_CONNECTION* Connection ) { - if (Connection->RemoteHashEntry != NULL) { - QuicLookupRemoveRemoteHash(&Binding->Lookup, Connection->RemoteHashEntry); + for (CXPLAT_SLIST_ENTRY* Link = Connection->SourceCids.Next; + Link != NULL; + Link = Link->Next) { + + QUIC_CID_SLIST_ENTRY* Entry = + CXPLAT_CONTAINING_RECORD( + Link, + QUIC_CID_SLIST_ENTRY, + Link); + + CXPLAT_SLIST_ENTRY** HashLink = &Entry->HashEntries.Next; + while (*HashLink != NULL) { + QUIC_CID_HASH_ENTRY* HashEntry = + CXPLAT_CONTAINING_RECORD( + *HashLink, + QUIC_CID_HASH_ENTRY, + Link); + if (HashEntry->Binding == Binding) { + QuicBindingRemoveSourceConnectionID(Binding, HashEntry); + *HashLink = (*HashLink)->Next; + CXPLAT_FREE(HashEntry, QUIC_POOL_CIDHASH); + HashEntry = NULL; + } else { + HashLink = &(*HashLink)->Next; + } + } } - QuicLookupRemoveLocalCids(&Binding->Lookup, Connection); } _IRQL_requires_max_(DISPATCH_LEVEL) void -QuicBindingMoveSourceConnectionIDs( - _In_ QUIC_BINDING* BindingSrc, - _In_ QUIC_BINDING* BindingDest, - _In_ QUIC_CONNECTION* Connection +QuicBindingRemoveRemoteHash( + _In_ QUIC_BINDING* Binding, + _In_ QUIC_REMOTE_HASH_ENTRY* RemoteHashEntry ) { - QuicLookupMoveLocalConnectionIDs( - &BindingSrc->Lookup, &BindingDest->Lookup, Connection); + QuicLookupRemoveRemoteHash(&Binding->Lookup, RemoteHashEntry); } _IRQL_requires_max_(DISPATCH_LEVEL) @@ -1282,10 +1327,10 @@ QuicBindingCreateConnection( BOOLEAN BindingRefAdded = FALSE; CXPLAT_DBG_ASSERT(NewConnection->SourceCids.Next != NULL); - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( NewConnection->SourceCids.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); QuicConnAddRef(NewConnection, QUIC_CONN_REF_LOOKUP_RESULT); @@ -1320,6 +1365,8 @@ QuicBindingCreateConnection( } goto Exit; } + CXPLAT_DBG_ASSERT(NewConnection->RemoteHashEntry != NULL); + NewConnection->RemoteHashEntry->Binding = Binding; QuicWorkerQueueConnection(NewConnection->Worker, NewConnection); @@ -1353,7 +1400,7 @@ QuicBindingCreateConnection( } else { NewConnection->SourceCids.Next = NULL; - CXPLAT_FREE(SourceCid, QUIC_POOL_CIDHASH); + CXPLAT_FREE(SourceCid, QUIC_POOL_CIDSLIST); QuicConnRelease(NewConnection, QUIC_CONN_REF_LOOKUP_RESULT); #pragma prefast(suppress:6001, "SAL doesn't understand ref counts") QuicConnRelease(NewConnection, QUIC_CONN_REF_HANDLE_OWNER); diff --git a/src/core/binding.h b/src/core/binding.h index bdf61c3cb6..2e5f8847d4 100644 --- a/src/core/binding.h +++ b/src/core/binding.h @@ -373,7 +373,19 @@ _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN QuicBindingAddSourceConnectionID( _In_ QUIC_BINDING* Binding, - _In_ QUIC_CID_HASH_ENTRY* SourceCid + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_CID_SLIST_ENTRY* SourceCid + ); + +// +// Attempts to insert all the connection's new source CIDs into the binding's +// lookup table. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOLEAN +QuicBindingAddAllSourceConnectionIDs( + _In_ QUIC_BINDING* Binding, + _In_ QUIC_CONNECTION* Connection ); // @@ -383,30 +395,24 @@ _IRQL_requires_max_(DISPATCH_LEVEL) void QuicBindingRemoveSourceConnectionID( _In_ QUIC_BINDING* Binding, - _In_ QUIC_CID_HASH_ENTRY* SourceCid, - _In_ CXPLAT_SLIST_ENTRY** Entry + _In_ QUIC_CID_HASH_ENTRY* SourceCid ); // -// Removes all the connection's source CIDs from the binding's lookup table. +// Removes all the source CIDs from the binding's lookup table. // _IRQL_requires_max_(DISPATCH_LEVEL) void -QuicBindingRemoveConnection( +QuicBindingRemoveAllSourceConnectionIDs( _In_ QUIC_BINDING* Binding, _In_ QUIC_CONNECTION* Connection ); -// -// Moves all the connections source CIDs from the one binding's lookup table to -// another. -// _IRQL_requires_max_(DISPATCH_LEVEL) void -QuicBindingMoveSourceConnectionIDs( - _In_ QUIC_BINDING* BindingSrc, - _In_ QUIC_BINDING* BindingDest, - _In_ QUIC_CONNECTION* Connection +QuicBindingRemoveRemoteHash( + _In_ QUIC_BINDING* Binding, + _In_ QUIC_REMOTE_HASH_ENTRY* RemoteHashEntry ); // @@ -516,3 +522,16 @@ QuicRetryTokenDecrypt( CxPlatDispatchLockRelease(&MsQuicLib.StatelessRetryKeysLock); return QUIC_SUCCEEDED(Status); } + +// +// Helper to get the owning QUIC_BINDING for the lookup module. +// +inline +_Ret_notnull_ +QUIC_BINDING* +QuicLookupGetBinding( + _In_ QUIC_LOOKUP* Lookup + ) +{ + return CXPLAT_CONTAINING_RECORD(Lookup, QUIC_BINDING, Lookup); +} diff --git a/src/core/cid.h b/src/core/cid.h index 845c7a960b..294e801468 100644 --- a/src/core/cid.h +++ b/src/core/cid.h @@ -163,12 +163,21 @@ typedef struct QUIC_CID_LIST_ENTRY { #define QUIC_CID_VALIDATE_NULL(Conn, Cid) UNREFERENCED_PARAMETER(Cid) #endif +typedef struct QUIC_CID_SLIST_ENTRY { + + CXPLAT_SLIST_ENTRY Link; + CXPLAT_SLIST_ENTRY HashEntries; + QUIC_CID CID; + +} QUIC_CID_SLIST_ENTRY; + typedef struct QUIC_CID_HASH_ENTRY { CXPLAT_HASHTABLE_ENTRY Entry; CXPLAT_SLIST_ENTRY Link; QUIC_CONNECTION* Connection; - QUIC_CID CID; + QUIC_BINDING* Binding; + QUIC_CID_SLIST_ENTRY* Parent; } QUIC_CID_HASH_ENTRY; @@ -178,18 +187,16 @@ typedef struct QUIC_CID_HASH_ENTRY { // inline _Success_(return != NULL) -QUIC_CID_HASH_ENTRY* -QuicCidNewNullSource( - _In_ QUIC_CONNECTION* Connection - ) +QUIC_CID_SLIST_ENTRY* +QuicCidNewNullSource() { - QUIC_CID_HASH_ENTRY* Entry = - (QUIC_CID_HASH_ENTRY*)CXPLAT_ALLOC_NONPAGED( - sizeof(QUIC_CID_HASH_ENTRY), - QUIC_POOL_CIDHASH); + QUIC_CID_SLIST_ENTRY* Entry = + (QUIC_CID_SLIST_ENTRY*)CXPLAT_ALLOC_NONPAGED( + sizeof(QUIC_CID_SLIST_ENTRY), + QUIC_POOL_CIDSLIST); if (Entry != NULL) { - Entry->Connection = Connection; + Entry->HashEntries.Next = NULL; CxPlatZeroMemory(&Entry->CID, sizeof(Entry->CID)); } @@ -201,23 +208,22 @@ QuicCidNewNullSource( // inline _Success_(return != NULL) -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicCidNewSource( - _In_ QUIC_CONNECTION* Connection, _In_ uint8_t Length, _In_reads_(Length) const uint8_t* const Data ) { - QUIC_CID_HASH_ENTRY* Entry = - (QUIC_CID_HASH_ENTRY*) + QUIC_CID_SLIST_ENTRY* Entry = + (QUIC_CID_SLIST_ENTRY*) CXPLAT_ALLOC_NONPAGED( - sizeof(QUIC_CID_HASH_ENTRY) + + sizeof(QUIC_CID_SLIST_ENTRY) + Length, - QUIC_POOL_CIDHASH); + QUIC_POOL_CIDSLIST); if (Entry != NULL) { - Entry->Connection = Connection; + Entry->HashEntries.Next = NULL; CxPlatZeroMemory(&Entry->CID, sizeof(Entry->CID)); Entry->CID.Length = Length; if (Length != 0) { diff --git a/src/core/configuration.c b/src/core/configuration.c index ebffd3c803..3af6fb9bf6 100644 --- a/src/core/configuration.c +++ b/src/core/configuration.c @@ -450,6 +450,24 @@ QuicConfigurationParamGet( return QUIC_STATUS_SUCCESS; } +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (Param == QUIC_PARAM_CONFIGURATION_CONN_ID_GENERATION_DISABLED) { + + if (*BufferLength < sizeof(BOOLEAN)) { + *BufferLength = sizeof(BOOLEAN); + return QUIC_STATUS_BUFFER_TOO_SMALL; + } + + if (Buffer == NULL) { + return QUIC_STATUS_INVALID_PARAMETER; + } + + *BufferLength = sizeof(BOOLEAN); + *(BOOLEAN*)Buffer = Configuration->Settings.ConnIDGenDisabled; + + return QUIC_STATUS_SUCCESS; + } +#endif return QUIC_STATUS_INVALID_PARAMETER; } @@ -559,6 +577,20 @@ QuicConfigurationParamSet( return QUIC_STATUS_SUCCESS; +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + case QUIC_PARAM_CONFIGURATION_CONN_ID_GENERATION_DISABLED: + + if (Buffer == NULL || + BufferLength < sizeof(BOOLEAN)) { + return QUIC_STATUS_INVALID_PARAMETER; + } + + Configuration->Settings.IsSet.ConnIDGenDisabled = TRUE; + Configuration->Settings.ConnIDGenDisabled = *(BOOLEAN*)Buffer; + + return QUIC_STATUS_SUCCESS; +#endif + #ifdef WIN32 case QUIC_PARAM_CONFIGURATION_SCHANNEL_CREDENTIAL_ATTRIBUTE_W: diff --git a/src/core/connection.c b/src/core/connection.c index b59d19ba45..d4958a71e1 100644 --- a/src/core/connection.c +++ b/src/core/connection.c @@ -216,8 +216,8 @@ QuicConnAlloc( Path->DestCid->CID.SequenceNumber, CASTED_CLOG_BYTEARRAY(Path->DestCid->CID.Length, Path->DestCid->CID.Data)); - QUIC_CID_HASH_ENTRY* SourceCid = - QuicCidNewSource(Connection, Packet->DestCidLen, Packet->DestCid); + QUIC_CID_SLIST_ENTRY* SourceCid = + QuicCidNewSource(Packet->DestCidLen, Packet->DestCid); if (SourceCid == NULL) { Status = QUIC_STATUS_OUT_OF_MEMORY; goto Error; @@ -290,9 +290,9 @@ QuicConnAlloc( CXPLAT_FREE( CXPLAT_CONTAINING_RECORD( Connection->SourceCids.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link), - QUIC_POOL_CIDHASH); + QUIC_POOL_CIDSLIST); Connection->SourceCids.Next = NULL; } while (!CxPlatListIsEmpty(&Connection->DestCids)) { @@ -362,10 +362,11 @@ QuicConnFree( CxPlatRecvDataReturn((CXPLAT_RECV_DATA*)Connection->ReceiveQueue); Connection->ReceiveQueue = NULL; } - QUIC_PATH* Path = &Connection->Paths[0]; - if (Path->Binding != NULL) { - QuicLibraryReleaseBinding(Path->Binding); - Path->Binding = NULL; + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + if (Connection->Paths[i].Binding != NULL) { + QuicLibraryReleaseBinding(Connection->Paths[i].Binding); + Connection->Paths[i].Binding = NULL; + } } CxPlatDispatchLockUninitialize(&Connection->ReceiveQueueLock); QuicOperationQueueUninitialize(&Connection->OperQ); @@ -610,10 +611,10 @@ QuicConnTraceRundownOper( for (CXPLAT_SLIST_ENTRY* Entry = Connection->SourceCids.Next; Entry != NULL; Entry = Entry->Next) { - const QUIC_CID_HASH_ENTRY* SourceCid = + const QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( Entry, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); UNREFERENCED_PARAMETER(SourceCid); QuicTraceEvent( @@ -827,14 +828,14 @@ QuicConnUpdateRtt( } _IRQL_requires_max_(PASSIVE_LEVEL) -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicConnGenerateNewSourceCid( _In_ QUIC_CONNECTION* Connection, _In_ BOOLEAN IsInitial ) { uint8_t TryCount = 0; - QUIC_CID_HASH_ENTRY* SourceCid; + QUIC_CID_SLIST_ENTRY* SourceCid; if (!Connection->State.ShareBinding) { // @@ -844,6 +845,27 @@ QuicConnGenerateNewSourceCid( return NULL; } + // + // Find all the bindings that are currently in use by this connection. + // + QUIC_BINDING* Bindings[QUIC_MAX_PATH_COUNT] = {NULL}; + uint8_t BindingsCount = 0; + + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + if (Connection->Paths[i].Binding != NULL) { + BOOLEAN NewBinding = TRUE; + for (uint8_t j = 0; j < BindingsCount; ++j) { + if (Connection->Paths[i].Binding == Bindings[j]) { + NewBinding = FALSE; + break; + } + } + if (NewBinding) { + Bindings[BindingsCount++] = Connection->Paths[i].Binding; + } + } + } + // // Keep randomly generating new source CIDs until we find one that doesn't // collide with an existing one. @@ -852,7 +874,6 @@ QuicConnGenerateNewSourceCid( do { SourceCid = QuicCidNewRandomSource( - Connection, Connection->ServerID, Connection->PartitionID, Connection->CibirId[0], @@ -862,12 +883,45 @@ QuicConnGenerateNewSourceCid( AllocFailure, "Allocation of '%s' failed. (%llu bytes)", "new Src CID", - sizeof(QUIC_CID_HASH_ENTRY) + MsQuicLib.CidTotalLength); + sizeof(QUIC_CID_SLIST_ENTRY) + MsQuicLib.CidTotalLength); QuicConnFatalError(Connection, QUIC_STATUS_INTERNAL_ERROR, NULL); return NULL; } - if (!QuicBindingAddSourceConnectionID(Connection->Paths[0].Binding, SourceCid)) { - CXPLAT_FREE(SourceCid, QUIC_POOL_CIDHASH); + + BOOLEAN Collision = FALSE; + int8_t Revert = -1; + for (uint8_t i = 0; i < BindingsCount; ++i) { + if (!QuicBindingAddSourceConnectionID(Bindings[i], Connection, SourceCid)) { + Collision = TRUE; + if (i > 0) { + Revert = i - 1; + } + break; + } + } + + if (Collision) { + if (Revert >= 0) { + for (int8_t i = Revert; i >= 0; --i) { + if (Bindings[i] != NULL) { + while (SourceCid->HashEntries.Next != NULL) { + QUIC_CID_HASH_ENTRY* CID = + CXPLAT_CONTAINING_RECORD( + CxPlatListPopEntry(&SourceCid->HashEntries), + QUIC_CID_HASH_ENTRY, + Link); + if (CID->Binding == Bindings[i]) { + QuicBindingRemoveSourceConnectionID( + Bindings[i], + CID); + CXPLAT_FREE(CID, QUIC_POOL_CIDHASH); + break; + } + } + } + } + } + CXPLAT_FREE(SourceCid, QUIC_POOL_CIDSLIST); SourceCid = NULL; if (++TryCount > QUIC_CID_MAX_COLLISION_RETRY) { QuicTraceEvent( @@ -885,6 +939,8 @@ QuicConnGenerateNewSourceCid( } } while (SourceCid == NULL); + SourceCid->CID.SequenceNumber = Connection->NextSourceCidSequenceNumber++; + QuicTraceEvent( ConnSourceCidAdded, "[conn][%p] (SeqNum=%llu) New Source CID: %!CID!", @@ -892,7 +948,6 @@ QuicConnGenerateNewSourceCid( SourceCid->CID.SequenceNumber, CASTED_CLOG_BYTEARRAY(SourceCid->CID.Length, SourceCid->CID.Data)); - SourceCid->CID.SequenceNumber = Connection->NextSourceCidSequenceNumber++; if (SourceCid->CID.SequenceNumber > 0) { SourceCid->CID.NeedsToSend = TRUE; QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_NEW_CONNECTION_ID); @@ -953,13 +1008,14 @@ QuicConnGenerateNewSourceCids( // uint8_t NewCidCount; if (ReplaceExistingCids) { - NewCidCount = Connection->SourceCidLimit; + NewCidCount = 0; CXPLAT_SLIST_ENTRY* Entry = Connection->SourceCids.Next; while (Entry != NULL) { - QUIC_CID_HASH_ENTRY* SourceCid = - CXPLAT_CONTAINING_RECORD(Entry, QUIC_CID_HASH_ENTRY, Link); + QUIC_CID_SLIST_ENTRY* SourceCid = + CXPLAT_CONTAINING_RECORD(Entry, QUIC_CID_SLIST_ENTRY, Link); SourceCid->CID.Retired = TRUE; Entry = Entry->Next; + NewCidCount++; } } else { uint8_t CurrentCidCount = QuicConnSourceCidsCount(Connection); @@ -1110,7 +1166,6 @@ QuicConnReplaceRetiredCids( continue; } - QUIC_CID_VALIDATE_NULL(Connection, Path->DestCid); // Previously cleared on retire. QUIC_CID_LIST_ENTRY* NewDestCid = QuicConnGetUnusedDestCid(Connection); if (NewDestCid == NULL) { if (Path->IsActive) { @@ -1127,13 +1182,18 @@ QuicConnReplaceRetiredCids( Connection, "Non-active path has no replacement for retired CID."); CXPLAT_DBG_ASSERT(i != 0); + CXPLAT_DBG_ASSERT(Connection->Paths[i].Binding != NULL); + QuicLibraryReleaseBinding(Connection->Paths[i].Binding); + Connection->Paths[i].Binding = NULL; QuicPathRemove(Connection, i--); continue; } CXPLAT_DBG_ASSERT(NewDestCid != Path->DestCid); + QUIC_CID_LIST_ENTRY* OldDestCid = Path->DestCid; Path->DestCid = NewDestCid; QUIC_CID_SET_PATH(Connection, NewDestCid, Path); + QUIC_CID_VALIDATE_NULL(Connection, OldDestCid); Path->DestCid->CID.UsedLocally = TRUE; Path->InitiatedCidUpdate = TRUE; QuicPathValidate(Path); @@ -1155,6 +1215,42 @@ QuicConnReplaceRetiredCids( return TRUE; } +_IRQL_requires_max_(PASSIVE_LEVEL) +BOOLEAN +QuicConnAssignCids( + _In_ QUIC_CONNECTION* Connection + ) +{ + BOOLEAN Assigned = FALSE; + + CXPLAT_DBG_ASSERT(Connection->PathsCount <= QUIC_MAX_PATH_COUNT); + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + QUIC_PATH* Path = &Connection->Paths[i]; + if (Path->DestCid != NULL || !Path->InUse) { + continue; + } + + QUIC_CID_LIST_ENTRY* NewDestCid = QuicConnGetUnusedDestCid(Connection); + if (NewDestCid == NULL) { + return Assigned; + } + + Path->DestCid = NewDestCid; + QUIC_CID_SET_PATH(Connection, NewDestCid, Path); + Path->DestCid->CID.UsedLocally = TRUE; + QuicPathValidate(Path); + + Path->SendChallenge = TRUE; + Path->PathValidationStartTime = CxPlatTimeUs64(); + + CxPlatRandom(sizeof(Path->Challenge), Path->Challenge); + + Assigned = TRUE; + } + + return Assigned; +} + _IRQL_requires_max_(DISPATCH_LEVEL) uint64_t QuicGetEarliestExpirationTime( @@ -1362,12 +1458,36 @@ QuicConnOnShutdownComplete( if (Path->EncryptionOffloading) { QuicPathUpdateQeo(Connection, Path, CXPLAT_QEO_OPERATION_REMOVE); } + } + // + // Remove all entries in the binding's lookup tables so we don't get any + // more packets queued. + // + if (Connection->RemoteHashEntry != NULL) { + CXPLAT_DBG_ASSERT(Connection->RemoteHashEntry->Binding != NULL); + QuicBindingRemoveRemoteHash( + Connection->RemoteHashEntry->Binding, + Connection->RemoteHashEntry); + } - // - // Remove all entries in the binding's lookup tables so we don't get any - // more packets queued. - // - QuicBindingRemoveConnection(Connection->Paths[0].Binding, Connection); + while (Connection->SourceCids.Next != NULL) { + QUIC_CID_SLIST_ENTRY* CID = + CXPLAT_CONTAINING_RECORD( + CxPlatListPopEntry(&Connection->SourceCids), + QUIC_CID_SLIST_ENTRY, + Link); + while (CID->HashEntries.Next != NULL) { + QUIC_CID_HASH_ENTRY* CID1 = + CXPLAT_CONTAINING_RECORD( + CxPlatListPopEntry(&CID->HashEntries), + QUIC_CID_HASH_ENTRY, + Link); + QuicBindingRemoveSourceConnectionID( + CID1->Binding, + CID1); + CXPLAT_FREE(CID1, QUIC_POOL_CIDHASH); + } + CXPLAT_FREE(CID, QUIC_POOL_CIDSLIST); } // @@ -1859,17 +1979,16 @@ QuicConnStart( // Clients only need to generate a non-zero length source CID if it // intends to share the UDP binding. // - QUIC_CID_HASH_ENTRY* SourceCid; + QUIC_CID_SLIST_ENTRY* SourceCid; if (Connection->State.ShareBinding) { SourceCid = QuicCidNewRandomSource( - Connection, NULL, Connection->PartitionID, Connection->CibirId[0], Connection->CibirId+2); } else { - SourceCid = QuicCidNewNullSource(Connection); + SourceCid = QuicCidNewNullSource(); } if (SourceCid == NULL) { Status = QUIC_STATUS_OUT_OF_MEMORY; @@ -1885,7 +2004,7 @@ QuicConnStart( CASTED_CLOG_BYTEARRAY(SourceCid->CID.Length, SourceCid->CID.Data)); CxPlatListPushEntry(&Connection->SourceCids, &SourceCid->Link); - if (!QuicBindingAddSourceConnectionID(Path->Binding, SourceCid)) { + if (!QuicBindingAddSourceConnectionID(Path->Binding, Connection, SourceCid)) { QuicLibraryReleaseBinding(Path->Binding); Path->Binding = NULL; Status = QUIC_STATUS_OUT_OF_MEMORY; @@ -2237,10 +2356,10 @@ QuicConnGenerateLocalTransportParameters( CXPLAT_TEL_ASSERT(Connection->Configuration != NULL); CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL); - const QUIC_CID_HASH_ENTRY* SourceCid = + const QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( Connection->SourceCids.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); LocalTP->InitialMaxData = Connection->Send.MaxData; @@ -2372,10 +2491,10 @@ QuicConnGenerateLocalTransportParameters( if (Connection->State.HandshakeUsedRetryPacket) { CXPLAT_DBG_ASSERT(SourceCid->Link.Next != NULL); - const QUIC_CID_HASH_ENTRY* PrevSourceCid = + const QUIC_CID_SLIST_ENTRY* PrevSourceCid = CXPLAT_CONTAINING_RECORD( SourceCid->Link.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); LocalTP->Flags |= QUIC_TP_FLAG_RETRY_SOURCE_CONNECTION_ID; @@ -4977,6 +5096,10 @@ QuicConnRecvFrames( return FALSE; } + if (QuicConnAssignCids(Connection)) { + QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PATH_CHALLENGE); + } + AckEliciting = TRUE; break; } @@ -4998,7 +5121,7 @@ QuicConnRecvFrames( } BOOLEAN IsLastCid; - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = QuicConnGetSourceCidFromSeq( Connection, Frame.Sequence, @@ -5006,7 +5129,7 @@ QuicConnRecvFrames( &IsLastCid); if (SourceCid != NULL) { BOOLEAN CidAlreadyRetired = SourceCid->CID.Retired; - CXPLAT_FREE(SourceCid, QUIC_POOL_CIDHASH); + CXPLAT_FREE(SourceCid, QUIC_POOL_CIDSLIST); if (IsLastCid) { QuicTraceEvent( ConnError, @@ -5351,7 +5474,7 @@ QuicConnRecvPostProcessing( { BOOLEAN PeerUpdatedCid = FALSE; if (Packet->DestCidLen != 0) { - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = QuicConnGetSourceCidFromBuf( Connection, Packet->DestCidLen, @@ -5372,7 +5495,8 @@ QuicConnRecvPostProcessing( if (!(*Path)->GotValidPacket) { (*Path)->GotValidPacket = TRUE; - if (!(*Path)->IsActive) { + if (!(*Path)->IsActive && + QuicConnIsServer(Connection)) { // // This is the first valid packet received on this non-active path. @@ -5444,7 +5568,8 @@ QuicConnRecvPostProcessing( } } - if (Packet->HasNonProbingFrame && + if (QuicConnIsServer(Connection) && + Packet->HasNonProbingFrame && Packet->NewLargestPacketNumber && !(*Path)->IsActive) { // @@ -5601,6 +5726,7 @@ QuicConnRecvDatagrams( QUIC_RX_PACKET* Batch[QUIC_MAX_CRYPTO_BATCH_COUNT]; uint8_t Cipher[CXPLAT_HP_SAMPLE_LENGTH * QUIC_MAX_CRYPTO_BATCH_COUNT]; QUIC_PATH* CurrentPath = NULL; + uint8_t CurrentPathID = 0; QUIC_RX_PACKET* Packet; while ((Packet = Packets) != NULL) { @@ -5623,6 +5749,13 @@ QuicConnRecvDatagrams( CxPlatUpdateRoute(&DatagramPath->Route, Packet->Route); + if (CurrentPath != NULL) { + // + // The current path may be moved. + // + uint8_t PathIndex; + CurrentPath = QuicConnGetPathByID(Connection, CurrentPathID, &PathIndex); + } if (DatagramPath != CurrentPath) { if (BatchCount != 0) { // @@ -5640,6 +5773,7 @@ QuicConnRecvDatagrams( BatchCount = 0; } CurrentPath = DatagramPath; + CurrentPathID = CurrentPath->ID; } if (!IsDeferred) { @@ -5815,20 +5949,25 @@ QuicConnRecvDatagrams( QuicConnSilentlyAbort(Connection); } - // - // Any new paths created here were created before packet validation. Now - // remove any non-active paths that didn't get any valid packets. - // NB: Traversing the array backwards is simpler and more efficient here due - // to the array shifting that happens in QuicPathRemove. - // - for (uint8_t i = Connection->PathsCount - 1; i > 0; --i) { - if (!Connection->Paths[i].GotValidPacket) { - QuicTraceLogConnInfo( - PathDiscarded, - Connection, - "Removing invalid path[%hhu]", - Connection->Paths[i].ID); - QuicPathRemove(Connection, i); + if (QuicConnIsServer(Connection)) { + // + // Any new paths created here were created before packet validation. Now + // remove any non-active paths that didn't get any valid packets. + // NB: Traversing the array backwards is simpler and more efficient here due + // to the array shifting that happens in QuicPathRemove. + // + for (uint8_t i = Connection->PathsCount - 1; i > 0; --i) { + if (!Connection->Paths[i].GotValidPacket) { + QuicTraceLogConnInfo( + PathDiscarded, + Connection, + "Removing invalid path[%hhu]", + Connection->Paths[i].ID); + CXPLAT_DBG_ASSERT(Connection->Paths[i].Binding != NULL); + QuicLibraryReleaseBinding(Connection->Paths[i].Binding); + Connection->Paths[i].Binding = NULL; + QuicPathRemove(Connection, i); + } } } @@ -6020,11 +6159,17 @@ QuicConnProcessRouteCompletion( "Route resolution failed on Path[%hhu]. Switching paths...", PathId); QuicPathSetActive(Connection, &Connection->Paths[1]); + CXPLAT_DBG_ASSERT(Connection->Paths[1].Binding != NULL); + QuicLibraryReleaseBinding(Connection->Paths[1].Binding); + Connection->Paths[1].Binding = NULL; QuicPathRemove(Connection, 1); if (!QuicSendFlush(&Connection->Send)) { QuicSendQueueFlush(&Connection->Send, REASON_ROUTE_COMPLETION); } } else { + CXPLAT_DBG_ASSERT(Connection->Paths[PathIndex].Binding != NULL); + QuicLibraryReleaseBinding(Connection->Paths[PathIndex].Binding); + Connection->Paths[PathIndex].Binding = NULL; QuicPathRemove(Connection, PathIndex); } } @@ -6155,6 +6300,320 @@ QuicConnUpdatePeerPacketTolerance( } } +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +QuicConnOpenNewPath( + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_PATH* Path + ) +{ + CXPLAT_DBG_ASSERT(Connection->State.RemoteAddressSet); + CXPLAT_DBG_ASSERT(Connection->Configuration != NULL); + + CXPLAT_UDP_CONFIG UdpConfig = {0}; + UdpConfig.LocalAddress = &Path->Route.LocalAddress; + UdpConfig.RemoteAddress = &Connection->Paths[0].Route.RemoteAddress; + UdpConfig.Flags = Connection->State.ShareBinding ? CXPLAT_SOCKET_FLAG_SHARE : 0; + UdpConfig.InterfaceIndex = 0; + // Open a new binding with the same partition as the connection. + UdpConfig.PartitionIndex = QuicPartitionIdGetIndex(Connection->PartitionID); +#ifdef QUIC_COMPARTMENT_ID + UdpConfig.CompartmentId = Connection->Configuration->CompartmentId; +#endif +#ifdef QUIC_OWNING_PROCESS + UdpConfig.OwningProcess = Connection->Configuration->OwningProcess; +#endif + + QUIC_BINDING* NewBinding = NULL; + QUIC_STATUS Status = QuicLibraryGetBinding(&UdpConfig, &NewBinding); + if (QUIC_FAILED(Status)) { + return Status; + } + + Path->Binding = NewBinding; + + QuicBindingGetLocalAddress( + Path->Binding, + &Path->Route.LocalAddress); + + if (Path != &Connection->Paths[0]) { + CxPlatCopyMemory(&Path->Route.RemoteAddress, + &Connection->Paths[0].Route.RemoteAddress, + sizeof(QUIC_ADDR)); + } + + if (!Connection->State.ShareBinding) { + QUIC_CID_SLIST_ENTRY* SourceCid = QuicCidNewNullSource(); + if (SourceCid == NULL) { + return QUIC_STATUS_OUT_OF_MEMORY; + } + + 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(NewBinding, Connection, SourceCid)) { + return QUIC_STATUS_OUT_OF_MEMORY; + } + } else { + if (!QuicBindingAddAllSourceConnectionIDs(NewBinding, Connection)) { + QuicConnGenerateNewSourceCids(Connection, TRUE); + } + } + + QuicTraceEvent( + ConnLocalAddrAdded, + "[conn][%p] New Local IP: %!ADDR!", + Connection, + CASTED_CLOG_BYTEARRAY(sizeof(Path->Route.LocalAddress), &Path->Route.LocalAddress)); + + QUIC_CID_LIST_ENTRY* NewDestCid = QuicConnGetUnusedDestCid(Connection); + // + // If we can't get a unused CID, we defer sending a path challange until we receieve a new CID. + // + if (NewDestCid != NULL) { + Path->DestCid = NewDestCid; + QUIC_CID_SET_PATH(Connection, Path->DestCid, Path); + Path->DestCid->CID.UsedLocally = TRUE; + + CXPLAT_DBG_ASSERT(Path->DestCid != NULL); + QuicPathValidate(Path); + Path->SendChallenge = TRUE; + Path->PathValidationStartTime = CxPlatTimeUs64(); + + CxPlatRandom(sizeof(Path->Challenge), Path->Challenge); + } + + return QUIC_STATUS_SUCCESS; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +BOOLEAN +QuicConnOpenNewPaths( + _In_ QUIC_CONNECTION* Connection + ) +{ + BOOLEAN Assigned = FALSE; + + CXPLAT_DBG_ASSERT(Connection->PathsCount > 0); + for (uint8_t i = 1; i < Connection->PathsCount; ++i) { + if (Connection->Paths[i].Binding == NULL) { + QUIC_STATUS Status = QuicConnOpenNewPath(Connection, &Connection->Paths[i]); + if (QUIC_FAILED(Status)) { + CXPLAT_DBG_ASSERT(i != 0); + if (Connection->Paths[i].Binding != NULL) { + QuicLibraryReleaseBinding(Connection->Paths[i].Binding); + Connection->Paths[i].Binding = NULL; + } + QuicPathRemove(Connection, i--); + } else { + if (Connection->Paths[i].DestCid != NULL) { + Assigned = TRUE; + } + } + } + } + return Assigned; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +static +QUIC_STATUS +QuicConnAddLocalAddress( + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_ADDR* LocalAddress + ) +{ + if (QuicConnIsServer(Connection)) { + return QUIC_STATUS_NOT_SUPPORTED; + } + + if (Connection->State.ClosedLocally) { + return QUIC_STATUS_INVALID_STATE; + } + + BOOLEAN AddrInUse = FALSE; + if (Connection->State.LocalAddressSet) { + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + if (QuicAddrCompare( + &Connection->Paths[i].Route.LocalAddress, + LocalAddress)) { + AddrInUse = TRUE; + break; + } + } + } + + if (AddrInUse) { + return QUIC_STATUS_ADDRESS_IN_USE; + } + + if (Connection->PathsCount == QUIC_MAX_PATH_COUNT) { + // + // Already tracking the maximum number of paths, and can't free + // any more. + // + return QUIC_STATUS_OUT_OF_MEMORY; + } + + QUIC_PATH* Path = NULL; + if (!Connection->State.LocalAddressSet) { + Path = &Connection->Paths[0]; + Connection->State.LocalAddressSet = TRUE; + } else { + if (Connection->PathsCount > 1) { + // + // Make room for the new path (at index 1). + // + CxPlatMoveMemory( + &Connection->Paths[2], + &Connection->Paths[1], + (Connection->PathsCount - 1) * sizeof(QUIC_PATH)); + } + Path = &Connection->Paths[1]; + QuicPathInitialize(Connection, Path); + Path->Allowance = UINT32_MAX; + Connection->PathsCount++; + } + + CxPlatCopyMemory(&Path->Route.LocalAddress, LocalAddress, sizeof(QUIC_ADDR)); + + if (!(Connection->State.Started && Connection->State.HandshakeConfirmed)) { + return QUIC_STATUS_SUCCESS; + } + + CXPLAT_DBG_ASSERT(Path != &Connection->Paths[0]); + + QUIC_STATUS Status = QuicConnOpenNewPath(Connection, Path); + if (QUIC_FAILED(Status)) { + if (Path->Binding != NULL) { + QuicLibraryReleaseBinding(Path->Binding); + Path->Binding = NULL; + } + QuicPathRemove(Connection, 1); + } else { + if (Path->DestCid != NULL) { + QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PATH_CHALLENGE); + } + } + + return Status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +static +QUIC_STATUS +QuicConnRemoveLocalAddress( + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_ADDR* LocalAddress + ) +{ + if (QuicConnIsServer(Connection)) { + return QUIC_STATUS_INVALID_STATE; + } + + if (!Connection->State.LocalAddressSet) { + return QUIC_STATUS_NOT_FOUND; + } + + uint8_t PathIndex = Connection->PathsCount; + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + if (QuicAddrCompare( + &Connection->Paths[i].Route.LocalAddress, + LocalAddress)) { + PathIndex = i; + break; + } + } + + if (PathIndex == Connection->PathsCount) { + return QUIC_STATUS_NOT_FOUND; + } + + QUIC_PATH* Path = &Connection->Paths[PathIndex]; + + if (Path->DestCid != NULL && + Connection->State.Started && Connection->State.HandshakeConfirmed) { + QuicConnRetireCid(Connection, Path->DestCid); + } + + if (Path->Binding != NULL) { + QuicBindingRemoveAllSourceConnectionIDs(Path->Binding, Connection); + QuicLibraryReleaseBinding(Path->Binding); + Path->Binding = NULL; + } + + if (Connection->PathsCount == 1) { + if (!Connection->State.Started) { + Connection->State.LocalAddressSet = FALSE; + } else { + QuicTraceEvent( + ConnError, + "[conn][%p] ERROR, %s.", + Connection, + "Last Local Address Removed!"); + QuicConnSilentlyAbort(Connection); + return QUIC_STATUS_ABORTED; + } + } else { + if (Path->IsActive) { + CXPLAT_DBG_ASSERT(PathIndex == 0); + if (!Connection->State.Started) { + CXPLAT_DBG_ASSERT(Path->DestCid != NULL); + CXPLAT_DBG_ASSERT(!Path->DestCid->CID.Retired); +#if DEBUG + QUIC_CID_CLEAR_PATH(Path->DestCid); +#endif + // Move the dest CID to the new active path. + QUIC_CID_LIST_ENTRY* DestCid = Path->DestCid; + Path->DestCid = NULL; + QUIC_PATH* NewActivePath = &Connection->Paths[1]; + NewActivePath->DestCid = DestCid; + QUIC_CID_SET_PATH(Connection, NewActivePath->DestCid, NewActivePath); + + QuicPathSetActive(Connection, NewActivePath); + PathIndex = 1; // The removing path is now at index 1. + } else if (!Connection->State.HandshakeConfirmed) { + QuicTraceEvent( + ConnError, + "[conn][%p] ERROR, %s.", + Connection, + "Active Local Address Removed during Handshake!"); + QuicConnSilentlyAbort(Connection); + return QUIC_STATUS_ABORTED; + } else { + uint8_t NewActivePathIndex = Connection->PathsCount; + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + if (i != PathIndex && Connection->Paths[i].DestCid != NULL) { + NewActivePathIndex = i; + break; + } + } + if (NewActivePathIndex == Connection->PathsCount) { + QuicTraceEvent( + ConnError, + "[conn][%p] ERROR, %s.", + Connection, + "No Active Local Address Remaining!"); + QuicConnSilentlyAbort(Connection); + return QUIC_STATUS_ABORTED; + } + QUIC_PATH* NewActivePath = &Connection->Paths[NewActivePathIndex]; + QuicPathSetActive(Connection, NewActivePath); + PathIndex = NewActivePathIndex; // The removing path is now at the new active index. + } + } + QuicPathRemove(Connection, PathIndex); + } + + return QUIC_STATUS_SUCCESS; +} + #define QUIC_CONN_BAD_START_STATE(CONN) (CONN->State.Started || CONN->State.ClosedLocally) _IRQL_requires_max_(PASSIVE_LEVEL) @@ -6197,18 +6656,21 @@ QuicConnParamSet( break; } - Connection->State.LocalAddressSet = TRUE; - CxPlatCopyMemory(&Connection->Paths[0].Route.LocalAddress, Buffer, sizeof(QUIC_ADDR)); - QuicTraceEvent( - ConnLocalAddrAdded, - "[conn][%p] New Local IP: %!ADDR!", - Connection, - CASTED_CLOG_BYTEARRAY(sizeof(Connection->Paths[0].Route.LocalAddress), &Connection->Paths[0].Route.LocalAddress)); - - if (Connection->State.Started) { + if (!Connection->State.Started) { + Connection->State.LocalAddressSet = TRUE; + CxPlatCopyMemory(&Connection->Paths[0].Route.LocalAddress, Buffer, sizeof(QUIC_ADDR)); + } else { + CXPLAT_DBG_ASSERT(Connection->State.RemoteAddressSet); + QUIC_PATH* Path = QuicConnGetPathByAddress(Connection, LocalAddress, &Connection->Paths[0].Route.RemoteAddress); + if (Path != NULL) { + if (!Path->IsActive) { + QuicPathSetActive(Connection, Path); + } + Status = QUIC_STATUS_SUCCESS; + break; + } CXPLAT_DBG_ASSERT(Connection->Paths[0].Binding); - CXPLAT_DBG_ASSERT(Connection->State.RemoteAddressSet); CXPLAT_DBG_ASSERT(Connection->Configuration != NULL); QUIC_BINDING* OldBinding = Connection->Paths[0].Binding; @@ -6232,14 +6694,32 @@ QuicConnParamSet( Connection->Paths[0].Binding = OldBinding; break; } + + if (!QuicConnRetireCurrentDestCid(Connection, &Connection->Paths[0])) { + QuicLibraryReleaseBinding(Connection->Paths[0].Binding); + Connection->Paths[0].Binding = OldBinding; + Status = QUIC_STATUS_INVALID_STATE; + } + Connection->Paths[0].Route.Queue = NULL; // // TODO - Need to free any queued recv packets from old binding. // - QuicBindingMoveSourceConnectionIDs( - OldBinding, Connection->Paths[0].Binding, Connection); + if (!Connection->State.ShareBinding) { + if (!QuicBindingAddAllSourceConnectionIDs(Connection->Paths[0].Binding, Connection)) { + QuicLibraryReleaseBinding(Connection->Paths[0].Binding); + Connection->Paths[0].Binding = OldBinding; + Status = QUIC_STATUS_OUT_OF_MEMORY; + break; + } + } else { + if (!QuicBindingAddAllSourceConnectionIDs(Connection->Paths[0].Binding, Connection)) { + QuicConnGenerateNewSourceCids(Connection, TRUE); + } + } + QuicBindingRemoveAllSourceConnectionIDs(OldBinding, Connection); QuicLibraryReleaseBinding(OldBinding); QuicTraceEvent( @@ -6258,6 +6738,8 @@ QuicConnParamSet( Connection, CASTED_CLOG_BYTEARRAY(sizeof(Connection->Paths[0].Route.LocalAddress), &Connection->Paths[0].Route.LocalAddress)); + QuicCongestionControlReset(&Connection->CongestionControl, FALSE); + QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PING); } @@ -6623,6 +7105,30 @@ QuicConnParamSet( return QUIC_STATUS_SUCCESS; } + case QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS: { + + if (BufferLength != sizeof(QUIC_ADDR) || Buffer == NULL || + !QuicAddrIsValid((QUIC_ADDR*)Buffer)) { + Status = QUIC_STATUS_INVALID_PARAMETER; + break; + } + + Status = QuicConnAddLocalAddress(Connection, (QUIC_ADDR*)Buffer); + break; + } + + case QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS: { + + if (BufferLength != sizeof(QUIC_ADDR) || Buffer == NULL || + !QuicAddrIsValid((QUIC_ADDR*)Buffer)) { + Status = QUIC_STATUS_INVALID_PARAMETER; + break; + } + + Status = QuicConnRemoveLocalAddress(Connection, (QUIC_ADDR*)Buffer); + break; + } + // // Private // @@ -6729,6 +7235,31 @@ QuicConnParamSet( break; #endif +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + case QUIC_PARAM_CONN_DISABLE_CONN_ID_GENERATION: + + if (BufferLength != sizeof(BOOLEAN) || Buffer == NULL) { + Status = QUIC_STATUS_INVALID_PARAMETER; + break; + } + + Connection->State.DisableConnIDGen = *(BOOLEAN*)Buffer; + Status = QUIC_STATUS_SUCCESS; + break; + + case QUIC_PARAM_CONN_GENERATE_CONN_ID: + + if (!Connection->State.Connected || + !Connection->State.HandshakeConfirmed) { + Status = QUIC_STATUS_INVALID_STATE; + break; + } + + QuicConnGenerateNewSourceCids(Connection, FALSE); + Status = QUIC_STATUS_SUCCESS; + break; +#endif + default: Status = QUIC_STATUS_INVALID_PARAMETER; break; @@ -7216,10 +7747,12 @@ QuicConnParamGet( *BufferLength = Connection->OrigDestCID->Length; break; } + if (Buffer == NULL) { Status = QUIC_STATUS_INVALID_PARAMETER; break; } + CxPlatCopyMemory( Buffer, Connection->OrigDestCID->Data, @@ -7228,6 +7761,7 @@ QuicConnParamGet( // Tell app how much buffer we copied. // *BufferLength = Connection->OrigDestCID->Length; + Status = QUIC_STATUS_SUCCESS; break; @@ -7392,6 +7926,12 @@ QuicConnApplyNewSettings( } } +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (NewSettings->IsSet.ConnIDGenDisabled) { + Connection->State.DisableConnIDGen = NewSettings->ConnIDGenDisabled; + } +#endif + if (OverWrite) { QuicSettingsDumpNew(NewSettings); } else { diff --git a/src/core/connection.h b/src/core/connection.h index c754115f52..bf9907d6f3 100644 --- a/src/core/connection.h +++ b/src/core/connection.h @@ -208,6 +208,14 @@ typedef union QUIC_CONNECTION_STATE { // BOOLEAN DisableVneTp : 1; #endif + +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + // + // Whether to disable automatic generation of Connection ID. + // Only used for testing, and thus only enabled for debug builds. + // + BOOLEAN DisableConnIDGen : 1; +#endif }; } QUIC_CONNECTION_STATE; @@ -1187,7 +1195,7 @@ QuicConnQueueHighestPriorityOper( // Generates a new source connection ID. // _IRQL_requires_max_(PASSIVE_LEVEL) -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicConnGenerateNewSourceCid( _In_ QUIC_CONNECTION* Connection, _In_ BOOLEAN IsInitial @@ -1219,7 +1227,7 @@ QuicConnRetireCurrentDestCid( _IRQL_requires_max_(DISPATCH_LEVEL) _Success_(return != NULL) inline -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicConnGetSourceCidFromSeq( _In_ QUIC_CONNECTION* Connection, _In_ QUIC_VAR_INT SequenceNumber, @@ -1230,23 +1238,30 @@ QuicConnGetSourceCidFromSeq( for (CXPLAT_SLIST_ENTRY** Entry = &Connection->SourceCids.Next; *Entry != NULL; Entry = &(*Entry)->Next) { - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( *Entry, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); if (SourceCid->CID.SequenceNumber == SequenceNumber) { if (RemoveFromList) { - QuicBindingRemoveSourceConnectionID( - Connection->Paths[0].Binding, - SourceCid, - Entry); + while (SourceCid->HashEntries.Next != NULL) { + QUIC_CID_HASH_ENTRY* CID = + CXPLAT_CONTAINING_RECORD( + CxPlatListPopEntry(&SourceCid->HashEntries), + QUIC_CID_HASH_ENTRY, + Link); + QuicBindingRemoveSourceConnectionID( + CID->Binding, + CID); + } QuicTraceEvent( ConnSourceCidRemoved, "[conn][%p] (SeqNum=%llu) Removed Source CID: %!CID!", Connection, SourceCid->CID.SequenceNumber, CASTED_CLOG_BYTEARRAY(SourceCid->CID.Length, SourceCid->CID.Data)); + *Entry = (*Entry)->Next; } *IsLastCid = Connection->SourceCids.Next == NULL; return SourceCid; @@ -1260,7 +1275,7 @@ QuicConnGetSourceCidFromSeq( // _IRQL_requires_max_(DISPATCH_LEVEL) inline -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicConnGetSourceCidFromBuf( _In_ QUIC_CONNECTION* Connection, _In_ uint8_t CidLength, @@ -1271,10 +1286,10 @@ QuicConnGetSourceCidFromBuf( for (CXPLAT_SLIST_ENTRY* Entry = Connection->SourceCids.Next; Entry != NULL; Entry = Entry->Next) { - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( Entry, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); if (CidLength == SourceCid->CID.Length && memcmp(CidBuffer, SourceCid->CID.Data, CidLength) == 0) { @@ -1598,6 +1613,25 @@ QuicConnUpdatePeerPacketTolerance( _In_ uint8_t NewPacketTolerance ); +// +// Open a new path for the connection. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +QuicConnOpenNewPath( + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_PATH* Path + ); + +// +// Open new paths for the connection. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +BOOLEAN +QuicConnOpenNewPaths( + _In_ QUIC_CONNECTION* Connection + ); + // // Sets a connection parameter. // diff --git a/src/core/crypto.c b/src/core/crypto.c index 7633f9b32f..9e0922cc10 100644 --- a/src/core/crypto.c +++ b/src/core/crypto.c @@ -161,10 +161,10 @@ QuicCryptoInitialize( if (QuicConnIsServer(Connection)) { CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL); - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( Connection->SourceCids.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); HandshakeCid = SourceCid->CID.Data; @@ -416,10 +416,10 @@ QuicCryptoOnVersionChange( if (QuicConnIsServer(Connection)) { CXPLAT_DBG_ASSERT(Connection->SourceCids.Next != NULL); - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( Connection->SourceCids.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); HandshakeCid = SourceCid->CID.Data; @@ -498,6 +498,10 @@ QuicCryptoHandshakeConfirmed( QuicBindingOnConnectionHandshakeConfirmed(Path->Binding, Connection); } + if (QuicConnOpenNewPaths(Connection)) { + QuicSendSetSendFlag(&Connection->Send, QUIC_CONN_SEND_FLAG_PATH_CHALLENGE); + } + QuicCryptoDiscardKeys(Crypto, QUIC_PACKET_KEY_HANDSHAKE); } @@ -1597,10 +1601,10 @@ QuicCryptoProcessTlsCompletion( CXPLAT_DBG_ASSERT(Connection->SourceCids.Next->Next != NULL); CXPLAT_DBG_ASSERT(Connection->SourceCids.Next->Next != NULL); CXPLAT_DBG_ASSERT(Connection->SourceCids.Next->Next->Next == NULL); - QUIC_CID_HASH_ENTRY* InitialSourceCid = + QUIC_CID_SLIST_ENTRY* InitialSourceCid = CXPLAT_CONTAINING_RECORD( Connection->SourceCids.Next->Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); CXPLAT_DBG_ASSERT(InitialSourceCid->CID.IsInitial); Connection->SourceCids.Next->Next = Connection->SourceCids.Next->Next->Next; @@ -1611,7 +1615,7 @@ QuicCryptoProcessTlsCompletion( Connection, InitialSourceCid->CID.SequenceNumber, CASTED_CLOG_BYTEARRAY(InitialSourceCid->CID.Length, InitialSourceCid->CID.Data)); - CXPLAT_FREE(InitialSourceCid, QUIC_POOL_CIDHASH); + CXPLAT_FREE(InitialSourceCid, QUIC_POOL_CIDSLIST); } // @@ -1622,7 +1626,13 @@ QuicCryptoProcessTlsCompletion( Connection->State.Connected = TRUE; QuicPerfCounterIncrement(QUIC_PERF_COUNTER_CONN_CONNECTED); +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (!Connection->State.DisableConnIDGen) { + QuicConnGenerateNewSourceCids(Connection, FALSE); + } +#else QuicConnGenerateNewSourceCids(Connection, FALSE); +#endif CXPLAT_DBG_ASSERT(Crypto->TlsState.NegotiatedAlpn != NULL); if (QuicConnIsClient(Connection)) { @@ -1656,7 +1666,7 @@ QuicCryptoProcessTlsCompletion( } Connection->Stats.ResumptionSucceeded = Crypto->TlsState.SessionResumed; - CXPLAT_DBG_ASSERT(Connection->PathsCount == 1); + CXPLAT_DBG_ASSERT(Connection->PathsCount >= 1); QUIC_PATH* Path = &Connection->Paths[0]; CXPLAT_DBG_ASSERT(Path->IsActive); diff --git a/src/core/inline.c b/src/core/inline.c index a128550ea7..c30df3a689 100644 --- a/src/core/inline.c +++ b/src/core/inline.c @@ -27,22 +27,18 @@ QuicCidNewDestination( const uint8_t* const Data ); -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicCidNewSource( - _In_ QUIC_CONNECTION* Connection, _In_ uint8_t Length, _In_reads_(Length) const uint8_t* const Data ); -QUIC_CID_HASH_ENTRY* -QuicCidNewNullSource( - _In_ QUIC_CONNECTION* Connection - ); +QUIC_CID_SLIST_ENTRY* +QuicCidNewNullSource(); -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicCidNewRandomSource( - _In_opt_ QUIC_CONNECTION* Connection, _In_reads_opt_(MsQuicLib.CidServerIdLength) const void* ServerID, _In_ uint16_t PartitionID, @@ -715,6 +711,11 @@ QuicRetryTokenDecrypt( _Out_ QUIC_TOKEN_CONTENTS* Token ); +QUIC_BINDING* +QuicLookupGetBinding( + _In_ QUIC_LOOKUP* Lookup + ); + void QuicConnLogStatistics( _In_ const QUIC_CONNECTION* const Connection @@ -744,7 +745,7 @@ QuicPacketBuilderHasAllowance( _In_ const QUIC_PACKET_BUILDER* Builder ); -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicConnGetSourceCidFromSeq( _In_ QUIC_CONNECTION* Connection, _In_ QUIC_VAR_INT SequenceNumber, @@ -752,7 +753,7 @@ QuicConnGetSourceCidFromSeq( _Out_ BOOLEAN* IsLastCid ); -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicConnGetSourceCidFromBuf( _In_ QUIC_CONNECTION* Connection, _In_ uint8_t CidLength, diff --git a/src/core/library.h b/src/core/library.h index 930ce8111e..3d42473491 100644 --- a/src/core/library.h +++ b/src/core/library.h @@ -451,9 +451,8 @@ QuicPerfCounterTrySnapShot( // inline _Success_(return != NULL) -QUIC_CID_HASH_ENTRY* +QUIC_CID_SLIST_ENTRY* QuicCidNewRandomSource( - _In_opt_ QUIC_CONNECTION* Connection, _In_reads_opt_(MsQuicLib.CidServerIdLength) const void* ServerID, _In_ uint16_t PartitionID, @@ -466,15 +465,15 @@ QuicCidNewRandomSource( CXPLAT_DBG_ASSERT(MsQuicLib.CidTotalLength == MsQuicLib.CidServerIdLength + QUIC_CID_PID_LENGTH + QUIC_CID_PAYLOAD_LENGTH); CXPLAT_DBG_ASSERT(QUIC_CID_PAYLOAD_LENGTH > PrefixLength); - QUIC_CID_HASH_ENTRY* Entry = - (QUIC_CID_HASH_ENTRY*) + QUIC_CID_SLIST_ENTRY* Entry = + (QUIC_CID_SLIST_ENTRY*) CXPLAT_ALLOC_NONPAGED( - sizeof(QUIC_CID_HASH_ENTRY) + + sizeof(QUIC_CID_SLIST_ENTRY) + MsQuicLib.CidTotalLength, - QUIC_POOL_CIDHASH); + QUIC_POOL_CIDSLIST); if (Entry != NULL) { - Entry->Connection = Connection; + Entry->HashEntries.Next = NULL; CxPlatZeroMemory(&Entry->CID, sizeof(Entry->CID)); Entry->CID.Length = MsQuicLib.CidTotalLength; diff --git a/src/core/lookup.c b/src/core/lookup.c index 4449a05afc..21de48ed53 100644 --- a/src/core/lookup.c +++ b/src/core/lookup.c @@ -175,21 +175,32 @@ QuicLookupRebalance( // if (PreviousLookup != NULL) { - CXPLAT_SLIST_ENTRY* Entry = + CXPLAT_SLIST_ENTRY* Link = ((QUIC_CONNECTION*)PreviousLookup)->SourceCids.Next; - while (Entry != NULL) { - QUIC_CID_HASH_ENTRY *CID = + while (Link != NULL) { + QUIC_CID_SLIST_ENTRY* Entry = CXPLAT_CONTAINING_RECORD( - Entry, - QUIC_CID_HASH_ENTRY, + Link, + QUIC_CID_SLIST_ENTRY, Link); - (void)QuicLookupInsertLocalCid( - Lookup, - CxPlatHashSimple(CID->CID.Length, CID->CID.Data), - CID, - FALSE); - Entry = Entry->Next; + CXPLAT_SLIST_ENTRY* HashLink = Entry->HashEntries.Next; + while (HashLink != NULL) { + QUIC_CID_HASH_ENTRY *HashEntry = + CXPLAT_CONTAINING_RECORD( + HashLink, + QUIC_CID_HASH_ENTRY, + Link); + if (HashEntry->Binding == QuicLookupGetBinding(Lookup)) { + (void)QuicLookupInsertLocalCid( + Lookup, + CxPlatHashSimple(Entry->CID.Length, Entry->CID.Data), + HashEntry, + FALSE); + } + HashLink = HashLink->Next; + } + Link = Link->Next; } } @@ -216,15 +227,15 @@ QuicLookupRebalance( } CxPlatHashtableRemove(&PreviousTable[i].Table, Entry, NULL); - QUIC_CID_HASH_ENTRY *CID = + QUIC_CID_HASH_ENTRY* HashEntry = CXPLAT_CONTAINING_RECORD( Entry, QUIC_CID_HASH_ENTRY, Entry); (void)QuicLookupInsertLocalCid( Lookup, - CxPlatHashSimple(CID->CID.Length, CID->CID.Data), - CID, + CxPlatHashSimple(HashEntry->Parent->CID.Length, HashEntry->Parent->CID.Data), + HashEntry, FALSE); } #pragma warning(push) @@ -276,6 +287,7 @@ QuicLookupMaximizePartitioning( _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN QuicCidMatchConnection( + _In_ QUIC_LOOKUP* Lookup, _In_ const QUIC_CONNECTION* const Connection, _In_reads_(Length) const uint8_t* const DestCid, @@ -286,12 +298,24 @@ QuicCidMatchConnection( Link != NULL; Link = Link->Next) { - const QUIC_CID_HASH_ENTRY* const Entry = - CXPLAT_CONTAINING_RECORD(Link, const QUIC_CID_HASH_ENTRY, Link); + const QUIC_CID_SLIST_ENTRY* const Entry = + CXPLAT_CONTAINING_RECORD(Link, const QUIC_CID_SLIST_ENTRY, Link); + + for (CXPLAT_SLIST_ENTRY* HashLink = Entry->HashEntries.Next; + HashLink != NULL; + HashLink = HashLink->Next) { + + const QUIC_CID_HASH_ENTRY* const HashEntry = + CXPLAT_CONTAINING_RECORD(HashLink, const QUIC_CID_HASH_ENTRY, Link); - if (Length == Entry->CID.Length && - (Length == 0 || memcmp(DestCid, Entry->CID.Data, Length) == 0)) { - return TRUE; + if (HashEntry->Binding != QuicLookupGetBinding(Lookup)) { + continue; + } + + if (Length == HashEntry->Parent->CID.Length && + (Length == 0 || memcmp(DestCid, HashEntry->Parent->CID.Data, Length) == 0)) { + return TRUE; + } } } @@ -317,12 +341,12 @@ QuicHashLookupConnection( CxPlatHashtableLookup(Table, Hash, &Context); while (TableEntry != NULL) { - QUIC_CID_HASH_ENTRY* CIDEntry = + QUIC_CID_HASH_ENTRY* HashEntry = CXPLAT_CONTAINING_RECORD(TableEntry, QUIC_CID_HASH_ENTRY, Entry); - if (CIDEntry->CID.Length == Length && - memcmp(DestCid, CIDEntry->CID.Data, Length) == 0) { - return CIDEntry->Connection; + if (HashEntry->Parent->CID.Length == Length && + memcmp(DestCid, HashEntry->Parent->CID.Data, Length) == 0) { + return HashEntry->Connection; } TableEntry = CxPlatHashtableLookupNext(Table, &Context); @@ -349,7 +373,7 @@ QuicLookupFindConnectionByLocalCidInternal( // destination connection ID matches that connection. // if (Lookup->SINGLE.Connection != NULL && - QuicCidMatchConnection(Lookup->SINGLE.Connection, CID, CIDLen)) { + QuicCidMatchConnection(Lookup, Lookup->SINGLE.Connection, CID, CIDLen)) { Connection = Lookup->SINGLE.Connection; } @@ -475,14 +499,14 @@ QuicLookupInsertLocalCid( } } else { - CXPLAT_DBG_ASSERT(SourceCid->CID.Length >= MsQuicLib.CidServerIdLength + QUIC_CID_PID_LENGTH); + CXPLAT_DBG_ASSERT(SourceCid->Parent->CID.Length >= MsQuicLib.CidServerIdLength + QUIC_CID_PID_LENGTH); // // Insert the source connection ID into the hash table. // CXPLAT_STATIC_ASSERT(QUIC_CID_PID_LENGTH == 2, "The code below assumes 2 bytes"); uint16_t PartitionIndex; - CxPlatCopyMemory(&PartitionIndex, SourceCid->CID.Data + MsQuicLib.CidServerIdLength, 2); + CxPlatCopyMemory(&PartitionIndex, SourceCid->Parent->CID.Data + MsQuicLib.CidServerIdLength, 2); PartitionIndex &= MsQuicLib.PartitionMask; PartitionIndex %= Lookup->PartitionCount; QUIC_PARTITIONED_HASHTABLE* Table = &Lookup->HASH.Tables[PartitionIndex]; @@ -510,8 +534,6 @@ QuicLookupInsertLocalCid( Hash); #endif - SourceCid->CID.IsInLookupTable = TRUE; - return TRUE; } @@ -584,7 +606,7 @@ QuicLookupRemoveLocalCidInt( _In_ QUIC_CID_HASH_ENTRY* SourceCid ) { - CXPLAT_DBG_ASSERT(SourceCid->CID.IsInLookupTable); + CXPLAT_DBG_ASSERT(SourceCid->Parent != NULL); CXPLAT_DBG_ASSERT(Lookup->CidCount != 0); Lookup->CidCount--; @@ -606,14 +628,14 @@ QuicLookupRemoveLocalCidInt( Lookup->SINGLE.Connection = NULL; } } else { - CXPLAT_DBG_ASSERT(SourceCid->CID.Length >= MsQuicLib.CidServerIdLength + QUIC_CID_PID_LENGTH); + CXPLAT_DBG_ASSERT(SourceCid->Parent->CID.Length >= MsQuicLib.CidServerIdLength + QUIC_CID_PID_LENGTH); // // Remove the source connection ID from the multi-hash table. // CXPLAT_STATIC_ASSERT(QUIC_CID_PID_LENGTH == 2, "The code below assumes 2 bytes"); uint16_t PartitionIndex; - CxPlatCopyMemory(&PartitionIndex, SourceCid->CID.Data + MsQuicLib.CidServerIdLength, 2); + CxPlatCopyMemory(&PartitionIndex, SourceCid->Parent->CID.Data + MsQuicLib.CidServerIdLength, 2); PartitionIndex &= MsQuicLib.PartitionMask; PartitionIndex %= Lookup->PartitionCount; QUIC_PARTITIONED_HASHTABLE* Table = &Lookup->HASH.Tables[PartitionIndex]; @@ -726,7 +748,8 @@ _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN QuicLookupAddLocalCid( _In_ QUIC_LOOKUP* Lookup, - _In_ QUIC_CID_HASH_ENTRY* SourceCid, + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_CID_SLIST_ENTRY* SourceCid, _Out_opt_ QUIC_CONNECTION** Collision ) { @@ -746,8 +769,24 @@ QuicLookupAddLocalCid( Hash); if (ExistingConnection == NULL) { - Result = - QuicLookupInsertLocalCid(Lookup, Hash, SourceCid, TRUE); + QUIC_CID_HASH_ENTRY* HashEntry = + (QUIC_CID_HASH_ENTRY*)CXPLAT_ALLOC_NONPAGED( + sizeof(QUIC_CID_HASH_ENTRY), + QUIC_POOL_CIDHASH); + if (HashEntry != NULL) { + HashEntry->Parent = SourceCid; + HashEntry->Binding = QuicLookupGetBinding(Lookup); + HashEntry->Connection = Connection; + Result = + QuicLookupInsertLocalCid(Lookup, Hash, HashEntry, TRUE); + if (Result) { + CxPlatListPushEntry(&SourceCid->HashEntries, &HashEntry->Link); + } else { + CXPLAT_FREE(HashEntry, QUIC_POOL_CIDHASH); + } + } else { + Result = FALSE; + } if (Collision != NULL) { *Collision = NULL; } @@ -822,14 +861,11 @@ _IRQL_requires_max_(DISPATCH_LEVEL) void QuicLookupRemoveLocalCid( _In_ QUIC_LOOKUP* Lookup, - _In_ QUIC_CID_HASH_ENTRY* SourceCid, - _In_ CXPLAT_SLIST_ENTRY** Entry + _In_ QUIC_CID_HASH_ENTRY* SourceCid ) { CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); QuicLookupRemoveLocalCidInt(Lookup, SourceCid); - SourceCid->CID.IsInLookupTable = FALSE; - *Entry = (*Entry)->Next; CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); QuicConnRelease(SourceCid->Connection, QUIC_CONN_REF_LOOKUP_TABLE); } @@ -859,82 +895,3 @@ QuicLookupRemoveRemoteHash( QuicConnRelease(Connection, QUIC_CONN_REF_LOOKUP_TABLE); } -_IRQL_requires_max_(DISPATCH_LEVEL) -void -QuicLookupRemoveLocalCids( - _In_ QUIC_LOOKUP* Lookup, - _In_ QUIC_CONNECTION* Connection - ) -{ - uint8_t ReleaseRefCount = 0; - - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); - while (Connection->SourceCids.Next != NULL) { - QUIC_CID_HASH_ENTRY *CID = - CXPLAT_CONTAINING_RECORD( - CxPlatListPopEntry(&Connection->SourceCids), - QUIC_CID_HASH_ENTRY, - Link); - if (CID->CID.IsInLookupTable) { - QuicLookupRemoveLocalCidInt(Lookup, CID); - CID->CID.IsInLookupTable = FALSE; - ReleaseRefCount++; - } - CXPLAT_FREE(CID, QUIC_POOL_CIDHASH); - } - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); - - for (uint8_t i = 0; i < ReleaseRefCount; i++) { -#pragma prefast(suppress:6001, "SAL doesn't understand ref counts") - QuicConnRelease(Connection, QUIC_CONN_REF_LOOKUP_TABLE); - } -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -void -QuicLookupMoveLocalConnectionIDs( - _In_ QUIC_LOOKUP* LookupSrc, - _In_ QUIC_LOOKUP* LookupDest, - _In_ QUIC_CONNECTION* Connection - ) -{ - CXPLAT_SLIST_ENTRY* Entry = Connection->SourceCids.Next; - - CxPlatDispatchRwLockAcquireExclusive(&LookupSrc->RwLock, PrevIrql1); - while (Entry != NULL) { - QUIC_CID_HASH_ENTRY *CID = - CXPLAT_CONTAINING_RECORD( - Entry, - QUIC_CID_HASH_ENTRY, - Link); - if (CID->CID.IsInLookupTable) { - QuicLookupRemoveLocalCidInt(LookupSrc, CID); - QuicConnRelease(Connection, QUIC_CONN_REF_LOOKUP_TABLE); - } - Entry = Entry->Next; - } - CxPlatDispatchRwLockReleaseExclusive(&LookupSrc->RwLock, PrevIrql1); - - CxPlatDispatchRwLockAcquireExclusive(&LookupDest->RwLock, PrevIrql2); -#pragma prefast(suppress:6001, "SAL doesn't understand ref counts") - Entry = Connection->SourceCids.Next; - while (Entry != NULL) { - QUIC_CID_HASH_ENTRY *CID = - CXPLAT_CONTAINING_RECORD( - Entry, - QUIC_CID_HASH_ENTRY, - Link); - if (CID->CID.IsInLookupTable) { - BOOLEAN Result = - QuicLookupInsertLocalCid( - LookupDest, - CxPlatHashSimple(CID->CID.Length, CID->CID.Data), - CID, - TRUE); - CXPLAT_DBG_ASSERT(Result); - UNREFERENCED_PARAMETER(Result); - } - Entry = Entry->Next; - } - CxPlatDispatchRwLockReleaseExclusive(&LookupDest->RwLock, PrevIrql2); -} diff --git a/src/core/lookup.h b/src/core/lookup.h index 6675c29ef8..598733180a 100644 --- a/src/core/lookup.h +++ b/src/core/lookup.h @@ -11,6 +11,7 @@ typedef struct QUIC_REMOTE_HASH_ENTRY { CXPLAT_HASHTABLE_ENTRY Entry; QUIC_CONNECTION* Connection; + QUIC_BINDING* Binding; QUIC_ADDR RemoteAddress; uint8_t RemoteCidLength; uint8_t RemoteCid[0]; @@ -140,7 +141,8 @@ _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN QuicLookupAddLocalCid( _In_ QUIC_LOOKUP* Lookup, - _In_ QUIC_CID_HASH_ENTRY* SourceCid, + _In_ QUIC_CONNECTION* Connection, + _In_ QUIC_CID_SLIST_ENTRY* SourceCid, _Out_opt_ QUIC_CONNECTION** Collision ); @@ -166,8 +168,7 @@ _IRQL_requires_max_(DISPATCH_LEVEL) void QuicLookupRemoveLocalCid( _In_ QUIC_LOOKUP* Lookup, - _In_ QUIC_CID_HASH_ENTRY* SourceCid, - _In_ CXPLAT_SLIST_ENTRY** Entry + _In_ QUIC_CID_HASH_ENTRY* SourceCid ); // @@ -179,24 +180,3 @@ QuicLookupRemoveRemoteHash( _In_ QUIC_LOOKUP* Lookup, _In_ QUIC_REMOTE_HASH_ENTRY* RemoteHashEntry ); - -// -// Removes all the connection's local CIDs from the lookup. -// -_IRQL_requires_max_(DISPATCH_LEVEL) -void -QuicLookupRemoveLocalCids( - _In_ QUIC_LOOKUP* Lookup, - _In_ QUIC_CONNECTION* Connection - ); - -// -// Moves all the connection's local CIDs from the one lookup to another. -// -_IRQL_requires_max_(DISPATCH_LEVEL) -void -QuicLookupMoveLocalConnectionIDs( - _In_ QUIC_LOOKUP* LookupSrc, - _In_ QUIC_LOOKUP* LookupDest, - _In_ QUIC_CONNECTION* Connection - ); diff --git a/src/core/loss_detection.c b/src/core/loss_detection.c index 9598c56676..f0e8982a5e 100644 --- a/src/core/loss_detection.c +++ b/src/core/loss_detection.c @@ -585,7 +585,7 @@ QuicLossDetectionOnPacketAcknowledged( case QUIC_FRAME_NEW_CONNECTION_ID: { BOOLEAN IsLastCid; - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = QuicConnGetSourceCidFromSeq( Connection, Packet->Frames[i].NEW_CONNECTION_ID.Sequence, @@ -790,7 +790,7 @@ QuicLossDetectionRetransmitFrames( case QUIC_FRAME_NEW_CONNECTION_ID: { BOOLEAN IsLastCid; - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = QuicConnGetSourceCidFromSeq( Connection, Packet->Frames[i].NEW_CONNECTION_ID.Sequence, @@ -841,6 +841,9 @@ QuicLossDetectionRetransmitFrames( "Path[%hhu] validation timed out", Path->ID); QuicPerfCounterIncrement(QUIC_PERF_COUNTER_PATH_FAILURE); + CXPLAT_DBG_ASSERT(Connection->Paths[PathIndex].Binding != NULL); + QuicLibraryReleaseBinding(Connection->Paths[PathIndex].Binding); + Connection->Paths[PathIndex].Binding = NULL; QuicPathRemove(Connection, PathIndex); } else { Path->SendChallenge = TRUE; diff --git a/src/core/packet_builder.c b/src/core/packet_builder.c index 9d380fa09a..e502b6ce4a 100644 --- a/src/core/packet_builder.c +++ b/src/core/packet_builder.c @@ -117,7 +117,7 @@ QuicPacketBuilderInitialize( Builder->SourceCid = CXPLAT_CONTAINING_RECORD( Connection->SourceCids.Next, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); uint64_t TimeNow = CxPlatTimeUs64(); @@ -963,6 +963,7 @@ QuicPacketBuilderFinalize( Builder->Metadata->PacketLength = Builder->HeaderLength + PayloadLength; Builder->Metadata->Flags.EcnEctSet = Builder->EcnEctSet; + Builder->Metadata->PathId = Builder->Path->ID; QuicTraceEvent( ConnPacketSent, "[conn][%p][TX][%llu] %hhu (%hu bytes)", diff --git a/src/core/packet_builder.h b/src/core/packet_builder.h index 912ce89769..2d3c7faed5 100644 --- a/src/core/packet_builder.h +++ b/src/core/packet_builder.h @@ -23,7 +23,7 @@ typedef struct QUIC_PACKET_BUILDER { // // The source connection ID. // - QUIC_CID_HASH_ENTRY* SourceCid; + QUIC_CID_SLIST_ENTRY* SourceCid; // // Represents a set of UDP datagrams. diff --git a/src/core/path.c b/src/core/path.c index 91a545c54e..58ff940b18 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -183,6 +183,28 @@ QuicConnGetPathByID( return NULL; } +_IRQL_requires_max_(PASSIVE_LEVEL) +_Ret_maybenull_ +QUIC_PATH* +QuicConnGetPathByAddress( + _In_ QUIC_CONNECTION* Connection, + _In_ const QUIC_ADDR* LocalAddress, + _In_ const QUIC_ADDR* RemoteAddress + ) +{ + for (uint8_t i = 0; i < Connection->PathsCount; ++i) { + if (QuicAddrCompare( + LocalAddress, + &Connection->Paths[i].Route.LocalAddress) && + QuicAddrCompare( + RemoteAddress, + &Connection->Paths[i].Route.RemoteAddress)) { + return &Connection->Paths[i]; + } + } + return NULL; +} + _IRQL_requires_max_(PASSIVE_LEVEL) _Ret_maybenull_ QUIC_PATH* @@ -209,6 +231,11 @@ QuicConnGetPathForPacket( return &Connection->Paths[i]; } + if (!QuicConnIsServer(Connection)) { + // Client doesn't create a new path. + return NULL; + } + if (Connection->PathsCount == QUIC_MAX_PATH_COUNT) { // // See if any old paths share the same remote address, and is just a rebind. @@ -221,6 +248,7 @@ QuicConnGetPathForPacket( && QuicAddrGetFamily(&Packet->Route->RemoteAddress) == QuicAddrGetFamily(&Connection->Paths[i].Route.RemoteAddress) && QuicAddrCompareIp(&Packet->Route->RemoteAddress, &Connection->Paths[i].Route.RemoteAddress) && QuicAddrCompare(&Packet->Route->LocalAddress, &Connection->Paths[i].Route.LocalAddress)) { + QuicLibraryReleaseBinding(Connection->Paths[i].Binding); QuicPathRemove(Connection, i); } } @@ -234,6 +262,10 @@ QuicConnGetPathForPacket( } } + if (!QuicLibraryTryAddRefBinding(Connection->Paths[0].Binding)) { + return NULL; + } + if (Connection->PathsCount > 1) { // // Make room for the new path (at index 1). @@ -273,6 +305,7 @@ QuicPathSetActive( } else { CXPLAT_DBG_ASSERT(Path->DestCid != NULL); UdpPortChangeOnly = + QuicConnIsServer(Connection) && QuicAddrGetFamily(&Path->Route.RemoteAddress) == QuicAddrGetFamily(&Connection->Paths[0].Route.RemoteAddress) && QuicAddrCompareIp(&Path->Route.RemoteAddress, &Connection->Paths[0].Route.RemoteAddress); @@ -301,8 +334,8 @@ QuicPathSetActive( if (!UdpPortChangeOnly) { QuicCongestionControlReset(&Connection->CongestionControl, FALSE); } - CXPLAT_DBG_ASSERT(Path->DestCid != NULL); - CXPLAT_DBG_ASSERT(!Path->DestCid->CID.Retired); + CXPLAT_DBG_ASSERT(Connection->Paths[0].DestCid != NULL); + CXPLAT_DBG_ASSERT(!Connection->Paths[0].DestCid->CID.Retired); } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -313,8 +346,8 @@ QuicPathUpdateQeo( _In_ CXPLAT_QEO_OPERATION Operation ) { - const QUIC_CID_HASH_ENTRY* SourceCid = - CXPLAT_CONTAINING_RECORD(Connection->SourceCids.Next, QUIC_CID_HASH_ENTRY, Link); + const QUIC_CID_SLIST_ENTRY* SourceCid = + CXPLAT_CONTAINING_RECORD(Connection->SourceCids.Next, QUIC_CID_SLIST_ENTRY, Link); CXPLAT_QEO_CONNECTION Offloads[2] = { { Operation, diff --git a/src/core/path.h b/src/core/path.h index 4fdc2d78d8..283f6ce079 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -296,6 +296,15 @@ QuicConnGetPathByID( _Out_ uint8_t* Index ); +_IRQL_requires_max_(PASSIVE_LEVEL) +_Ret_maybenull_ +QUIC_PATH* +QuicConnGetPathByAddress( + _In_ QUIC_CONNECTION* Connection, + _In_ const QUIC_ADDR* LocalAddress, + _In_ const QUIC_ADDR* RemoteAddress + ); + _IRQL_requires_max_(PASSIVE_LEVEL) _Ret_maybenull_ QUIC_PATH* diff --git a/src/core/quicdef.h b/src/core/quicdef.h index 10bec83f95..f52195cec9 100644 --- a/src/core/quicdef.h +++ b/src/core/quicdef.h @@ -538,6 +538,11 @@ CXPLAT_STATIC_ASSERT( // #define QUIC_DEFAULT_STREAM_MULTI_RECEIVE_ENABLED FALSE +// +// The default settings for disabling Connection ID generation. +// +#define QUIC_DEFAULT_CONN_ID_GENERATION_DISABLED FALSE + // // The number of rounds in Cubic Slow Start to sample RTT. // @@ -682,3 +687,5 @@ CXPLAT_STATIC_ASSERT( #define QUIC_SETTING_MTU_MISSING_PROBE_COUNT "MtuDiscoveryMissingProbeCount" #define QUIC_SETTING_CONGESTION_CONTROL_ALGORITHM "CongestionControlAlgorithm" + +#define QUIC_SETTING_CONN_ID_GENERATION_DISABLED "ConnIDGenerationDisabled" diff --git a/src/core/send.c b/src/core/send.c index a0f155b8f3..ec3ddd2017 100644 --- a/src/core/send.c +++ b/src/core/send.c @@ -781,10 +781,10 @@ QuicSendWriteFrames( for (CXPLAT_SLIST_ENTRY* Entry = Connection->SourceCids.Next; Entry != NULL; Entry = Entry->Next) { - QUIC_CID_HASH_ENTRY* SourceCid = + QUIC_CID_SLIST_ENTRY* SourceCid = CXPLAT_CONTAINING_RECORD( Entry, - QUIC_CID_HASH_ENTRY, + QUIC_CID_SLIST_ENTRY, Link); if (!SourceCid->CID.NeedsToSend) { continue; diff --git a/src/core/settings.c b/src/core/settings.c index 40dd41245f..cb906e3199 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -168,6 +168,11 @@ QuicSettingsSetDefault( if (!Settings->IsSet.StreamMultiReceiveEnabled) { Settings->StreamMultiReceiveEnabled = QUIC_DEFAULT_STREAM_MULTI_RECEIVE_ENABLED; } +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (!Settings->IsSet.ConnIDGenDisabled) { + Settings->ConnIDGenDisabled = QUIC_DEFAULT_CONN_ID_GENERATION_DISABLED; + } +#endif } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -336,6 +341,11 @@ QuicSettingsCopy( if (!Destination->IsSet.StreamMultiReceiveEnabled) { Destination->StreamMultiReceiveEnabled = Source->StreamMultiReceiveEnabled; } +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (!Destination->IsSet.ConnIDGenDisabled) { + Destination->ConnIDGenDisabled = Source->ConnIDGenDisabled; + } +#endif } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -714,6 +724,13 @@ QuicSettingApply( Destination->StreamMultiReceiveEnabled = Source->StreamMultiReceiveEnabled; Destination->IsSet.StreamMultiReceiveEnabled = TRUE; } + +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (Source->IsSet.ConnIDGenDisabled && (!Destination->IsSet.ConnIDGenDisabled || OverWrite)) { + Destination->ConnIDGenDisabled = Source->ConnIDGenDisabled; + Destination->IsSet.ConnIDGenDisabled = TRUE; + } +#endif return TRUE; } @@ -1382,6 +1399,18 @@ QuicSettingsLoad( &ValueLen); Settings->StreamMultiReceiveEnabled = !!Value; } +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (!Settings->IsSet.ConnIDGenDisabled) { + Value = QUIC_DEFAULT_CONN_ID_GENERATION_DISABLED; + ValueLen = sizeof(Value); + CxPlatStorageReadValue( + Storage, + QUIC_SETTING_CONN_ID_GENERATION_DISABLED, + (uint8_t*)&Value, + &ValueLen); + Settings->ConnIDGenDisabled = !!Value; + } +#endif } _IRQL_requires_max_(PASSIVE_LEVEL) @@ -1615,6 +1644,11 @@ QuicSettingsDumpNew( if (Settings->IsSet.StreamMultiReceiveEnabled) { QuicTraceLogVerbose(SettingStreamMultiReceiveEnabled, "[sett] StreamMultiReceiveEnabled = %hhu", Settings->StreamMultiReceiveEnabled); } +#if QUIC_TEST_MANUAL_CONN_ID_GENERATION + if (Settings->IsSet.ConnIDGenDisabled) { + QuicTraceLogVerbose(SettingConnIDGenDisabled, "[sett] ConnIDGenDisabled = %hhu", Settings->ConnIDGenDisabled); + } +#endif } #define SETTINGS_SIZE_THRU_FIELD(SettingsType, Field) \ diff --git a/src/core/settings.h b/src/core/settings.h index 9650e119bb..3f016b482d 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -62,7 +62,8 @@ typedef struct QUIC_SETTINGS_INTERNAL { uint64_t OneWayDelayEnabled : 1; uint64_t NetStatsEventEnabled : 1; uint64_t StreamMultiReceiveEnabled : 1; - uint64_t RESERVED : 16; + uint64_t ConnIDGenDisabled : 1; + uint64_t RESERVED : 15; } IsSet; }; @@ -113,6 +114,7 @@ typedef struct QUIC_SETTINGS_INTERNAL { uint8_t OneWayDelayEnabled : 1; uint8_t NetStatsEventEnabled : 1; uint8_t StreamMultiReceiveEnabled : 1; + uint8_t ConnIDGenDisabled : 1; uint8_t MtuDiscoveryMissingProbeCount; } QUIC_SETTINGS_INTERNAL; diff --git a/src/generated/linux/connection.c.clog.h b/src/generated/linux/connection.c.clog.h index 181deaaa58..a6cf39afc4 100644 --- a/src/generated/linux/connection.c.clog.h +++ b/src/generated/linux/connection.c.clog.h @@ -682,10 +682,10 @@ tracepoint(CLOG_CONNECTION_C, FirstCidUsage , arg1, arg3);\ // Decoder Ring for PathDiscarded // [conn][%p] Removing invalid path[%hhu] // QuicTraceLogConnInfo( - PathDiscarded, - Connection, - "Removing invalid path[%hhu]", - Connection->Paths[i].ID); + PathDiscarded, + Connection, + "Removing invalid path[%hhu]", + Connection->Paths[i].ID); // arg1 = arg1 = Connection = arg1 // arg3 = arg3 = Connection->Paths[i].ID = arg3 ----------------------------------------------------------*/ diff --git a/src/generated/linux/connection.c.clog.h.lttng.h b/src/generated/linux/connection.c.clog.h.lttng.h index 8cef04d1cc..37095d84b3 100644 --- a/src/generated/linux/connection.c.clog.h.lttng.h +++ b/src/generated/linux/connection.c.clog.h.lttng.h @@ -719,10 +719,10 @@ TRACEPOINT_EVENT(CLOG_CONNECTION_C, FirstCidUsage, // Decoder Ring for PathDiscarded // [conn][%p] Removing invalid path[%hhu] // QuicTraceLogConnInfo( - PathDiscarded, - Connection, - "Removing invalid path[%hhu]", - Connection->Paths[i].ID); + PathDiscarded, + Connection, + "Removing invalid path[%hhu]", + Connection->Paths[i].ID); // arg1 = arg1 = Connection = arg1 // arg3 = arg3 = Connection->Paths[i].ID = arg3 ----------------------------------------------------------*/ diff --git a/src/generated/linux/settings.c.clog.h b/src/generated/linux/settings.c.clog.h index ddf319a195..3501062ef2 100644 --- a/src/generated/linux/settings.c.clog.h +++ b/src/generated/linux/settings.c.clog.h @@ -842,6 +842,21 @@ tracepoint(CLOG_SETTINGS_C, SettingStreamMultiReceiveEnabled , arg2);\ +/*---------------------------------------------------------- +// Decoder Ring for SettingConnIDGenDisabled +// [sett] ConnIDGenDisabled = %hhu +// QuicTraceLogVerbose(SettingConnIDGenDisabled, "[sett] ConnIDGenDisabled = %hhu", Settings->ConnIDGenDisabled); +// arg2 = arg2 = Settings->ConnIDGenDisabled = arg2 +----------------------------------------------------------*/ +#ifndef _clog_3_ARGS_TRACE_SettingConnIDGenDisabled +#define _clog_3_ARGS_TRACE_SettingConnIDGenDisabled(uniqueId, encoded_arg_string, arg2)\ +tracepoint(CLOG_SETTINGS_C, SettingConnIDGenDisabled , arg2);\ + +#endif + + + + /*---------------------------------------------------------- // Decoder Ring for SettingsLoadInvalidAcceptableVersion // Invalid AcceptableVersion loaded from storage! 0x%x at position %d diff --git a/src/generated/linux/settings.c.clog.h.lttng.h b/src/generated/linux/settings.c.clog.h.lttng.h index c168d96477..40aaf98930 100644 --- a/src/generated/linux/settings.c.clog.h.lttng.h +++ b/src/generated/linux/settings.c.clog.h.lttng.h @@ -874,6 +874,22 @@ TRACEPOINT_EVENT(CLOG_SETTINGS_C, SettingStreamMultiReceiveEnabled, +/*---------------------------------------------------------- +// Decoder Ring for SettingConnIDGenDisabled +// [sett] ConnIDGenDisabled = %hhu +// QuicTraceLogVerbose(SettingConnIDGenDisabled, "[sett] ConnIDGenDisabled = %hhu", Settings->ConnIDGenDisabled); +// arg2 = arg2 = Settings->ConnIDGenDisabled = arg2 +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_SETTINGS_C, SettingConnIDGenDisabled, + TP_ARGS( + unsigned char, arg2), + TP_FIELDS( + ctf_integer(unsigned char, arg2, arg2) + ) +) + + + /*---------------------------------------------------------- // Decoder Ring for SettingsLoadInvalidAcceptableVersion // Invalid AcceptableVersion loaded from storage! 0x%x at position %d diff --git a/src/inc/msquic.h b/src/inc/msquic.h index b8dc5471db..d2de7ea361 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -923,6 +923,10 @@ typedef struct QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W { #define QUIC_PARAM_CONN_STATISTICS_V2 0x05000016 // QUIC_STATISTICS_V2 #define QUIC_PARAM_CONN_STATISTICS_V2_PLAT 0x05000017 // QUIC_STATISTICS_V2 #define QUIC_PARAM_CONN_ORIG_DEST_CID 0x05000018 // uint8_t[] +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS 0x05000019 // QUIC_ADDR +#define QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS 0x0500001A // QUIC_ADDR +#endif // // Parameters for TLS. diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index f051e84139..41fadab557 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -741,6 +741,24 @@ struct MsQuicConfiguration { MsQuicConfiguration(const MsQuicConfiguration& Other) = delete; MsQuicConfiguration& operator=(const MsQuicConfiguration& Other) = delete; QUIC_STATUS + SetParam( + _In_ uint32_t Param, + _In_ uint32_t BufferLength, + _In_reads_bytes_(BufferLength) + const void* Buffer + ) noexcept { + return MsQuic->SetParam(Handle, Param, BufferLength, Buffer); + } + QUIC_STATUS + GetParam( + _In_ uint32_t Param, + _Inout_ _Pre_defensive_ uint32_t* BufferLength, + _Out_writes_bytes_opt_(*BufferLength) + void* Buffer + ) noexcept { + return MsQuic->GetParam(Handle, Param, BufferLength, Buffer); + } + QUIC_STATUS LoadCredential(_In_ const QUIC_CREDENTIAL_CONFIG* CredConfig) noexcept { return MsQuic->ConfigurationLoadCredential(Handle, CredConfig); } diff --git a/src/inc/msquicp.h b/src/inc/msquicp.h index 138a880c74..70e73b1a6a 100644 --- a/src/inc/msquicp.h +++ b/src/inc/msquicp.h @@ -93,6 +93,11 @@ typedef struct QUIC_TEST_DATAPATH_HOOKS { // negotiation transport parameter. // #define QUIC_TEST_DISABLE_VNE_TP_GENERATION 1 + +// +// Enable support to disable automatic generation of connection ID. +// +#define QUIC_TEST_MANUAL_CONN_ID_GENERATION 1 #endif typedef struct QUIC_PRIVATE_TRANSPORT_PARAMETER { @@ -125,6 +130,7 @@ typedef struct QUIC_PRIVATE_TRANSPORT_PARAMETER { #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES #define QUIC_PARAM_CONFIGURATION_VERSION_NEG_ENABLED 0x83000001 // BOOLEAN #endif +#define QUIC_PARAM_CONFIGURATION_CONN_ID_GENERATION_DISABLED 0x83000002 // BOOLEAN // // The different private parameters for Connection. @@ -135,6 +141,8 @@ typedef struct QUIC_PRIVATE_TRANSPORT_PARAMETER { #define QUIC_PARAM_CONN_TEST_TRANSPORT_PARAMETER 0x85000002 // QUIC_PRIVATE_TRANSPORT_PARAMETER #define QUIC_PARAM_CONN_KEEP_ALIVE_PADDING 0x85000003 // uint16_t #define QUIC_PARAM_CONN_DISABLE_VNE_TP_GENERATION 0x85000004 // BOOLEAN +#define QUIC_PARAM_CONN_DISABLE_CONN_ID_GENERATION 0x85000005 // BOOLEAN +#define QUIC_PARAM_CONN_GENERATE_CONN_ID 0x85000006 // No payload #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES #define QUIC_PARAM_STREAM_RELIABLE_OFFSET_RECV 0x88000000 // uint64_t diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h index 4e56a87fd5..a4e70f55f7 100644 --- a/src/inc/quic_platform.h +++ b/src/inc/quic_platform.h @@ -148,6 +148,7 @@ typedef struct CXPLAT_SLIST_ENTRY { #define QUIC_POOL_ROUTE_RESOLUTION_WORKER 'A4cQ' // Qc4A - QUIC route resolution worker #define QUIC_POOL_ROUTE_RESOLUTION_OPER 'B4cQ' // Qc4B - QUIC route resolution operation #define QUIC_POOL_EXECUTION_CONFIG 'C4cQ' // Qc4C - QUIC execution config +#define QUIC_POOL_CIDSLIST 'D4cQ' // Qc0D - QUIC CID SLIST Entry typedef enum CXPLAT_THREAD_FLAGS { CXPLAT_THREAD_FLAG_NONE = 0x0000, diff --git a/src/manifest/clog.sidecar b/src/manifest/clog.sidecar index 907d055946..f23e9ce70e 100644 --- a/src/manifest/clog.sidecar +++ b/src/manifest/clog.sidecar @@ -10326,6 +10326,18 @@ ], "macroName": "QuicTraceLogVerbose" }, + "SettingConnIDGenDisabled": { + "ModuleProperites": {}, + "TraceString": "[sett] ConnIDGenDisabled = %hhu", + "UniqueId": "SettingConnIDGenDisabled", + "splitArgs": [ + { + "DefinationEncoding": "hhu", + "MacroVariableName": "arg2" + } + ], + "macroName": "QuicTraceLogVerbose" + }, "SettingDestCidUpdateIdleTimeoutMs": { "ModuleProperites": {}, "TraceString": "[sett] DestCidUpdateIdleTimeoutMs = %u", @@ -16489,6 +16501,11 @@ "TraceID": "SettingCongestionControlAlgorithm", "EncodingString": "[sett] CongestionControlAlgorithm = %hu" }, + { + "UniquenessHash": "b6c1fd3f-0e54-a7a9-78a2-d86eecf13d2a", + "TraceID": "SettingConnIDGenDisabled", + "EncodingString": "[sett] ConnIDGenDisabled = %hhu" + }, { "UniquenessHash": "7aef2287-3f2c-be15-9d32-4f26f16b46f1", "TraceID": "SettingDestCidUpdateIdleTimeoutMs", diff --git a/src/platform/datapath_winkernel.c b/src/platform/datapath_winkernel.c index a2f7391153..e76634a415 100644 --- a/src/platform/datapath_winkernel.c +++ b/src/platform/datapath_winkernel.c @@ -2381,7 +2381,6 @@ RecvDataReturn( DATAPATH_RX_IO_BLOCK* IoBlock = CXPLAT_CONTAINING_RECORD(Datagram, DATAPATH_RX_PACKET, Data)->IoBlock; - CXPLAT_DBG_ASSERT(Binding == NULL || Binding == IoBlock->Binding); Binding = IoBlock->Binding; Datagram->Allocated = FALSE; diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h index 77de8d913f..f2d2aec883 100644 --- a/src/test/MsQuicTests.h +++ b/src/test/MsQuicTests.h @@ -364,6 +364,35 @@ QuicTestVNTPOtherVersionZero( // Post Handshake Tests // +void +QuicTestProbePath( + _In_ int Family, + _In_ BOOLEAN ShareBinding, + _In_ BOOLEAN DeferConnIDGen, + _In_ uint32_t DropPacketCount + ); + +typedef enum QUIC_MIGRATION_TYPE { + MigrateWithProbe, + MigrateWithoutProbe, + DeleteAndMigrate, +} QUIC_MIGRATION_TYPE; + +void +QuicTestMigration( + _In_ int Family, + _In_ BOOLEAN ShareBinding, + _In_ QUIC_MIGRATION_TYPE Type + ); + +void +QuicTestMultipleLocalAddresses( + _In_ int Family, + _In_ BOOLEAN ShareBinding, + _In_ BOOLEAN DeferConnIDGen, + _In_ uint32_t DropPacketCount + ); + void QuicTestNatPortRebind( _In_ int Family, @@ -1340,4 +1369,25 @@ typedef struct { QUIC_CTL_CODE(126, METHOD_BUFFERED, FILE_WRITE_DATA) // int - Family -#define QUIC_MAX_IOCTL_FUNC_CODE 126 +typedef struct { + int Family; + BOOLEAN ShareBinding; + BOOLEAN DeferConnIDGen; + uint32_t DropPacketCount; +} QUIC_RUN_PROBE_PATH_PARAMS; + +#define IOCTL_QUIC_RUN_PROBE_PATH \ + QUIC_CTL_CODE(127, METHOD_BUFFERED, FILE_WRITE_DATA) + // QUIC_RUN_PROBE_PATH_PARAMS + +typedef struct { + int Family; + BOOLEAN ShareBinding; + QUIC_MIGRATION_TYPE Type; +} QUIC_RUN_MIGRATION_PARAMS; + +#define IOCTL_QUIC_RUN_MIGRATION \ + QUIC_CTL_CODE(128, METHOD_BUFFERED, FILE_WRITE_DATA) + // QUIC_RUN_MIGRATION + +#define QUIC_MAX_IOCTL_FUNC_CODE 128 diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index 5b01ffb796..1d27948ccd 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -1665,7 +1665,60 @@ TEST_P(WithFamilyArgs, PathValidationTimeout) { QuicTestPathValidationTimeout(GetParam().Family); } } -#endif + +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) +TEST_P(WithProbePathArgs, ProbePath) { + TestLoggerT Logger("QuicTestProbePath", GetParam()); + if (TestingKernelMode) { + QUIC_RUN_PROBE_PATH_PARAMS Params = { + GetParam().Family, + GetParam().ShareBinding, + GetParam().DeferConnIDGen, + GetParam().DropPacketCount + }; + ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_PROBE_PATH, Params)); + } else { + QuicTestProbePath( + GetParam().Family, + GetParam().ShareBinding, + GetParam().DeferConnIDGen, + GetParam().DropPacketCount); + } +} + +TEST_P(WithMigrationArgs, Migration) { + TestLoggerT Logger("QuicTestMigration", GetParam()); + if (TestingKernelMode) { + QUIC_RUN_MIGRATION_PARAMS Params = { + GetParam().Family, + GetParam().ShareBinding, + GetParam().Type + }; + ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_MIGRATION, Params)); + } else { + QuicTestMigration(GetParam().Family, GetParam().ShareBinding, GetParam().Type); + } +} + +TEST_P(WithProbePathArgs, MultipleLocalAddresses) { + TestLoggerT Logger("QuicTestMultipleLocalAddresses", GetParam()); + if (TestingKernelMode) { + QUIC_RUN_PROBE_PATH_PARAMS Params = { + GetParam().Family, + GetParam().ShareBinding, + GetParam().DeferConnIDGen, + GetParam().DropPacketCount + }; + ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_PROBE_PATH, Params)); + } else { + QuicTestMultipleLocalAddresses(GetParam().Family, + GetParam().ShareBinding, + GetParam().DeferConnIDGen, + GetParam().DropPacketCount); + } +} +#endif // QUIC_API_ENABLE_PREVIEW_FEATURES +#endif // QUIC_TEST_DATAPATH_HOOKS_ENABLED TEST_P(WithFamilyArgs, ChangeMaxStreamIDs) { TestLoggerT Logger("QuicTestChangeMaxStreamID", GetParam()); @@ -2439,6 +2492,15 @@ INSTANTIATE_TEST_SUITE_P( WithRebindPaddingArgs, ::testing::ValuesIn(RebindPaddingArgs::Generate())); +INSTANTIATE_TEST_SUITE_P( + Basic, + WithProbePathArgs, + ::testing::ValuesIn(ProbePathArgs::Generate())); + +INSTANTIATE_TEST_SUITE_P( + Basic, + WithMigrationArgs, + ::testing::ValuesIn(MigrationArgs::Generate())); #endif // QUIC_TEST_DATAPATH_HOOKS_ENABLED #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES diff --git a/src/test/bin/quic_gtest.h b/src/test/bin/quic_gtest.h index c280313c23..ced759f682 100644 --- a/src/test/bin/quic_gtest.h +++ b/src/test/bin/quic_gtest.h @@ -929,3 +929,56 @@ std::ostream& operator << (std::ostream& o, const TlsConfigArgs& args) { class WithValidateTlsConfigArgs : public testing::Test, public testing::WithParamInterface { }; + +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) +struct ProbePathArgs { + int Family; + BOOLEAN ShareBinding; + BOOLEAN DeferConnIDGen; + uint32_t DropPacketCount; + static ::std::vector Generate() { + ::std::vector list; + for (int Family : { 4, 6 }) + for (BOOLEAN ShareBinding : { TRUE, FALSE }) + for (BOOLEAN DeferConnIDGen : { TRUE, FALSE }) + for (uint32_t DropPacketCount : { 0, 1 }) + list.push_back({ Family, ShareBinding, DeferConnIDGen, DropPacketCount }); + return list; + } +}; + +std::ostream& operator << (std::ostream& o, const ProbePathArgs& args) { + return o << (args.Family == 4 ? "v4" : "v6") << "/" + << (args.ShareBinding ? "ShareBinding" : "not ShareBinding") << "/" + << (args.DeferConnIDGen ? "DeferConnIDGen" : "not DeferConnIDGen") << "/" + << "DropPacketCount: " << args.DropPacketCount; +} + +class WithProbePathArgs : public testing::Test, + public testing::WithParamInterface { +}; + +struct MigrationArgs { + int Family; + BOOLEAN ShareBinding; + QUIC_MIGRATION_TYPE Type; + static ::std::vector Generate() { + ::std::vector list; + for (int Family : { 4, 6 }) + for (BOOLEAN ShareBinding : { TRUE, FALSE }) + for (QUIC_MIGRATION_TYPE Type : { MigrateWithProbe, MigrateWithoutProbe, DeleteAndMigrate }) + list.push_back({ Family, ShareBinding, Type }); + return list; + } +}; + +std::ostream& operator << (std::ostream& o, const MigrationArgs& args) { + return o << (args.Family == 4 ? "v4" : "v6") << "/" + << (args.ShareBinding ? "ShareBinding" : "not ShareBinding") << "/" + << (args.Type ? (args.Type == MigrateWithoutProbe ? "Migrate without Probe" : "Delete and Migrate") : "Migrate with Probe"); +} + +class WithMigrationArgs : public testing::Test, + public testing::WithParamInterface { +}; +#endif diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index 1558f81396..60ceaa9c45 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -525,6 +525,8 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] = 0, sizeof(BOOLEAN), sizeof(INT32), + sizeof(QUIC_RUN_PROBE_PATH_PARAMS), + sizeof(QUIC_RUN_MIGRATION_PARAMS), }; CXPLAT_STATIC_ASSERT( @@ -556,6 +558,8 @@ typedef union { QUIC_RUN_KEY_UPDATE_RANDOM_LOSS_PARAMS KeyUpdateRandomLossParams; QUIC_RUN_MTU_DISCOVERY_PARAMS MtuDiscoveryParams; uint32_t Test; + QUIC_RUN_PROBE_PATH_PARAMS ProbePathParams; + QUIC_RUN_MIGRATION_PARAMS MigrationParams; QUIC_RUN_REBIND_PARAMS RebindParams; UINT8 RejectByClosing; QUIC_RUN_CIBIR_EXTENSION CibirParams; @@ -963,6 +967,25 @@ QuicTestCtlEvtIoDeviceControl( Params->Family)); break; + case IOCTL_QUIC_RUN_PROBE_PATH: + CXPLAT_FRE_ASSERT(Params != nullptr); + QuicTestCtlRun( + QuicTestProbePath( + Params->ProbePathParams.Family, + Params->ProbePathParams.ShareBinding, + Params->ProbePathParams.DeferConnIDGen, + Params->ProbePathParams.DropPacketCount)); + break; + + case IOCTL_QUIC_RUN_MIGRATION: + CXPLAT_FRE_ASSERT(Params != nullptr); + QuicTestCtlRun( + QuicTestMigration( + Params->MigrationParams.Family, + Params->MigrationParams.ShareBinding, + Params->MigrationParams.Type)); + break; + case IOCTL_QUIC_RUN_NAT_PORT_REBIND: CXPLAT_FRE_ASSERT(Params != nullptr); QuicTestCtlRun( diff --git a/src/test/lib/ApiTest.cpp b/src/test/lib/ApiTest.cpp index 71bda9c8ef..9a68c48f9a 100644 --- a/src/test/lib/ApiTest.cpp +++ b/src/test/lib/ApiTest.cpp @@ -4521,6 +4521,366 @@ void QuicTest_QUIC_PARAM_CONN_ORIG_DEST_CID(MsQuicRegistration& Registration, Ms } } +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) +void QuicTest_QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS(MsQuicRegistration& Registration, MsQuicConfiguration& ClientConfiguration) +{ + TestScopeLogger LogScope0("QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS"); + // + // SetParam + // + { + TestScopeLogger LogScope1("SetParam"); + // + // Connection ClosedLocally + // + { + TestScopeLogger LogScope2("Connection is closed locally"); + TEST_TRUE(ClientConfiguration.IsValid()); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + SimulateConnBadStartState(Connection, ClientConfiguration); + + QUIC_ADDR Dummy = {}; + TEST_QUIC_STATUS( + QUIC_STATUS_INVALID_STATE, + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Good before ConnectionStart + // + { + TestScopeLogger LogScope2("Good before ConnectionStart"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + QUIC_ADDR Dummy = {}; + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Good after ConnectionStart + // + { + TestScopeLogger LogScope2("Good after ConnectionStart"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + TEST_QUIC_SUCCEEDED( + MsQuic->ConnectionStart( + Connection.Handle, + ClientConfiguration, + QUIC_ADDRESS_FAMILY_INET, + "localhost", + 4433)); + + QUIC_ADDR Dummy = {}; + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Duplicate address + // + { + TestScopeLogger LogScope2("Duplicate address"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + QUIC_ADDR Dummy = {}; + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + + TEST_QUIC_STATUS( + QUIC_STATUS_ADDRESS_IN_USE, + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Multiple local addresses + // + { + TestScopeLogger LogScope2("Multiple local addresses"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + uint16_t Port = 4433; + + for (uint8_t i = 0; i < 4; i++) { + QUIC_ADDR ClientAddr; + QuicAddrFromString("127.0.0.1", Port++, &ClientAddr); + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(ClientAddr), + &ClientAddr)); + } + } + + // + // Too many local addresses + // + { + TestScopeLogger LogScope2("Too many local addresses"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + uint16_t Port = 4433; + + for (uint8_t i = 0; i < 5; i++) { + QUIC_ADDR ClientAddr; + QuicAddrFromString("127.0.0.1", Port++, &ClientAddr); + if (i < 4) { + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(ClientAddr), + &ClientAddr)); + } else { + TEST_QUIC_STATUS( + QUIC_STATUS_OUT_OF_MEMORY, + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(ClientAddr), + &ClientAddr)); + } + } + } + + } +} + +void QuicTest_QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS(MsQuicRegistration& Registration, MsQuicConfiguration& ClientConfiguration) +{ + TestScopeLogger LogScope0("QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS"); + // + // SetParam + // + { + TestScopeLogger LogScope1("SetParam"); + // + // No local address to remove + // + { + TestScopeLogger LogScope2("No local address to remove"); + TEST_TRUE(ClientConfiguration.IsValid()); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + QUIC_ADDR Dummy = {}; + TEST_QUIC_STATUS( + QUIC_STATUS_NOT_FOUND, + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Set and remove a local address + // + { + TestScopeLogger LogScope2("Set and remove a local address"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + QUIC_ADDR Dummy = {}; + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Add and remove a local address + // + { + TestScopeLogger LogScope2("Add and remove a local address"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + QUIC_ADDR Dummy = {}; + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(Dummy), + &Dummy)); + } + + // + // Add two local addresses and remove the first local address + // + { + TestScopeLogger LogScope2("Add two local addresses and remove the first local address"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + uint16_t Port = 4433; + QUIC_ADDR ClientAddr[2]; + for (uint8_t i = 0; i < 2; i++) { + QuicAddrFromString("127.0.0.1", Port++, &ClientAddr[i]); + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(ClientAddr[i]), + &ClientAddr[i])); + } + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(ClientAddr[0]), + &ClientAddr[0])); + } + + // + // Add two local addresses and remove the second local address + // + { + TestScopeLogger LogScope2("Add two local addresses and remove the second local address"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + uint16_t Port = 4433; + QUIC_ADDR ClientAddr[2]; + for (uint8_t i = 0; i < 2; i++) { + QuicAddrFromString("127.0.0.1", Port++, &ClientAddr[i]); + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(ClientAddr[i]), + &ClientAddr[i])); + } + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(ClientAddr[1]), + &ClientAddr[0])); + } + + // + // Remove a local address that belongs to the active path dusring handshake + // + { + TestScopeLogger LogScope2("Remove the last one local address after start"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + TEST_QUIC_SUCCEEDED( + MsQuic->ConnectionStart( + Connection.Handle, + ClientConfiguration, + QUIC_ADDRESS_FAMILY_INET, + "localhost", + 4433)); + + QuicAddr ClientLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(ClientLocalAddr)); + + TEST_QUIC_STATUS( + QUIC_STATUS_ABORTED, + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(ClientLocalAddr.SockAddr), + &ClientLocalAddr.SockAddr)); + } + + // + // Remove a local address that belongs to the active path dusring handshake + // + { + TestScopeLogger LogScope2("Remove a local address that belongs to the active path during handshake"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + TEST_QUIC_SUCCEEDED( + MsQuic->ConnectionStart( + Connection.Handle, + ClientConfiguration, + QUIC_ADDRESS_FAMILY_INET, + "localhost", + 4433)); + + QuicAddr SecondLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(SecondLocalAddr)); + SecondLocalAddr.IncrementPort(); + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr)); + + QuicAddr ClientLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(ClientLocalAddr)); + + TEST_QUIC_STATUS( + QUIC_STATUS_ABORTED, + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(ClientLocalAddr.SockAddr), + &ClientLocalAddr.SockAddr)); + } + + // + // Remove a local address that belongs to the non-active path dusring handshake + // + { + TestScopeLogger LogScope2("Remove a local address that belongs to the non-active path during handshake"); + MsQuicConnection Connection(Registration); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + TEST_QUIC_SUCCEEDED( + MsQuic->ConnectionStart( + Connection.Handle, + ClientConfiguration, + QUIC_ADDRESS_FAMILY_INET, + "localhost", + 4433)); + + QuicAddr SecondLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(SecondLocalAddr)); + SecondLocalAddr.IncrementPort(); + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr)); + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr)); + } + } +} +#endif + void QuicTestConnectionParam() { MsQuicAlpn Alpn("MsQuicTest"); @@ -4554,6 +4914,10 @@ void QuicTestConnectionParam() QuicTest_QUIC_PARAM_CONN_STATISTICS_V2(Registration); QuicTest_QUIC_PARAM_CONN_STATISTICS_V2_PLAT(Registration); QuicTest_QUIC_PARAM_CONN_ORIG_DEST_CID(Registration, ClientConfiguration); +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) + QuicTest_QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS(Registration, ClientConfiguration); + QuicTest_QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS(Registration, ClientConfiguration); +#endif } // diff --git a/src/test/lib/PathTest.cpp b/src/test/lib/PathTest.cpp index fc5b0c7979..37458f3b4c 100644 --- a/src/test/lib/PathTest.cpp +++ b/src/test/lib/PathTest.cpp @@ -119,3 +119,300 @@ QuicTestLocalPathChanges( PeerStreamsChanged.Reset(); } } + +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) +void +QuicTestProbePath( + _In_ int Family, + _In_ BOOLEAN ShareBinding, + _In_ BOOLEAN DeferConnIDGen, + _In_ uint32_t DropPacketCount + ) +{ + PathTestContext Context; + CxPlatEvent PeerStreamsChanged; + MsQuicRegistration Registration(true); + TEST_TRUE(Registration.IsValid()); + + MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", ServerSelfSignedCredConfig); + TEST_TRUE(ServerConfiguration.IsValid()); + + if (DeferConnIDGen) { + BOOLEAN DisableConnIdGeneration = TRUE; + TEST_QUIC_SUCCEEDED( + ServerConfiguration.SetParam( + QUIC_PARAM_CONFIGURATION_CONN_ID_GENERATION_DISABLED, + sizeof(DisableConnIdGeneration), + &DisableConnIdGeneration)); + } + + MsQuicCredentialConfig ClientCredConfig; + MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", ClientCredConfig); + TEST_TRUE(ClientConfiguration.IsValid()); + + MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, PathTestContext::ConnCallback, &Context); + TEST_QUIC_SUCCEEDED(Listener.GetInitStatus()); + QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6; + QuicAddr ServerLocalAddr(QuicAddrFamily); + TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest", &ServerLocalAddr.SockAddr)); + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + MsQuicConnection Connection(Registration, CleanUpManual, ClientCallback, &PeerStreamsChanged); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + if (ShareBinding) { + Connection.SetShareUdpBinding(); + } + + TEST_QUIC_SUCCEEDED(Connection.Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort())); + TEST_TRUE(Connection.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); + TEST_TRUE(Context.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); + TEST_NOT_EQUAL(nullptr, Context.Connection); + + QuicAddr SecondLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(SecondLocalAddr)); + SecondLocalAddr.IncrementPort(); + + PathProbeHelper *ProbeHelper = new(std::nothrow) PathProbeHelper(SecondLocalAddr.GetPort(), DropPacketCount, DropPacketCount); + + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + uint32_t Try = 0; + do { + Status = Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr); + + if (Status != QUIC_STATUS_SUCCESS) { + delete ProbeHelper; + SecondLocalAddr.IncrementPort(); + ProbeHelper = new(std::nothrow) PathProbeHelper(SecondLocalAddr.GetPort(), DropPacketCount, DropPacketCount); + } + } while (Status == QUIC_STATUS_ADDRESS_IN_USE && ++Try <= 3); + TEST_EQUAL(Status, QUIC_STATUS_SUCCESS); + + if (DeferConnIDGen) { + TEST_QUIC_SUCCEEDED( + Context.Connection->SetParam( + QUIC_PARAM_CONN_GENERATE_CONN_ID, + 0, + NULL)); + } + + TEST_TRUE(ProbeHelper->ServerReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + TEST_TRUE(ProbeHelper->ClientReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + QUIC_STATISTICS_V2 Stats; + uint32_t Size = sizeof(Stats); + TEST_QUIC_SUCCEEDED( + Connection.GetParam( + QUIC_PARAM_CONN_STATISTICS_V2_PLAT, + &Size, + &Stats)); + TEST_EQUAL(Stats.RecvDroppedPackets, 0); + delete ProbeHelper; +} + +void +QuicTestMigration( + _In_ int Family, + _In_ BOOLEAN ShareBinding, + _In_ QUIC_MIGRATION_TYPE Type + ) +{ + PathTestContext Context; + CxPlatEvent PeerStreamsChanged; + MsQuicRegistration Registration(true); + TEST_TRUE(Registration.IsValid()); + + MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", ServerSelfSignedCredConfig); + TEST_TRUE(ServerConfiguration.IsValid()); + + MsQuicCredentialConfig ClientCredConfig; + MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", ClientCredConfig); + TEST_TRUE(ClientConfiguration.IsValid()); + + MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, PathTestContext::ConnCallback, &Context); + TEST_QUIC_SUCCEEDED(Listener.GetInitStatus()); + QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6; + QuicAddr ServerLocalAddr(QuicAddrFamily); + TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest", &ServerLocalAddr.SockAddr)); + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + MsQuicConnection Connection(Registration, MsQuicCleanUpMode::CleanUpManual, ClientCallback, &PeerStreamsChanged); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + if (ShareBinding) { + Connection.SetShareUdpBinding(); + } + + Connection.SetSettings(MsQuicSettings{}.SetKeepAlive(25)); + + TEST_QUIC_SUCCEEDED(Connection.Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort())); + TEST_TRUE(Connection.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); + TEST_TRUE(Context.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); + TEST_NOT_EQUAL(nullptr, Context.Connection); + + QuicAddr SecondLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(SecondLocalAddr)); + SecondLocalAddr.IncrementPort(); + + PathProbeHelper* ProbeHelper = new(std::nothrow) PathProbeHelper(SecondLocalAddr.GetPort()); + + if (Type == MigrateWithProbe || Type == DeleteAndMigrate) { + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + int Try = 0; + do { + Status = Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr); + + if (Status != QUIC_STATUS_SUCCESS) { + delete ProbeHelper; + SecondLocalAddr.IncrementPort(); + ProbeHelper = new(std::nothrow) PathProbeHelper(SecondLocalAddr.GetPort()); + } + } while (Status == QUIC_STATUS_ADDRESS_IN_USE && ++Try <= 3); + TEST_QUIC_SUCCEEDED(Status); + + TEST_TRUE(ProbeHelper->ServerReceiveProbeEvent.WaitTimeout(TestWaitTimeout)); + TEST_TRUE(ProbeHelper->ClientReceiveProbeEvent.WaitTimeout(TestWaitTimeout)); + delete ProbeHelper; + + QUIC_STATISTICS_V2 Stats; + uint32_t Size = sizeof(Stats); + TEST_QUIC_SUCCEEDED( + Connection.GetParam( + QUIC_PARAM_CONN_STATISTICS_V2_PLAT, + &Size, + &Stats)); + TEST_EQUAL(Stats.RecvDroppedPackets, 0); + + if (Type == MigrateWithProbe) { + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr)); + } else { + QuicAddr ClientLocalAddr; + TEST_QUIC_SUCCEEDED(Connection.GetLocalAddr(ClientLocalAddr)); + + TEST_QUIC_SUCCEEDED( + Connection.SetParam( + QUIC_PARAM_CONN_REMOVE_LOCAL_ADDRESS, + sizeof(ClientLocalAddr.SockAddr), + &ClientLocalAddr.SockAddr)); + } + } else { + // + // Wait for handshake confirmation. + // + CxPlatSleep(100); + + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + int Try = 0; + do { + Status = Connection.SetParam( + QUIC_PARAM_CONN_LOCAL_ADDRESS, + sizeof(SecondLocalAddr.SockAddr), + &SecondLocalAddr.SockAddr); + if (Status != QUIC_STATUS_SUCCESS) { + SecondLocalAddr.IncrementPort(); + } + } while (Status == QUIC_STATUS_ADDRESS_IN_USE && ++Try <= 3); + TEST_QUIC_SUCCEEDED(Status); + } + + + TEST_TRUE(Context.PeerAddrChangedEvent.WaitTimeout(1500)); + QuicAddr ServerRemoteAddr; + TEST_QUIC_SUCCEEDED(Context.Connection->GetRemoteAddr(ServerRemoteAddr)); + TEST_TRUE(QuicAddrCompare(&SecondLocalAddr.SockAddr, &ServerRemoteAddr.SockAddr)); + Connection.SetSettings(MsQuicSettings{}.SetKeepAlive(0)); + TEST_TRUE(PeerStreamsChanged.WaitTimeout(1500)); +} + +void +QuicTestMultipleLocalAddresses( + _In_ int Family, + _In_ BOOLEAN ShareBinding, + _In_ BOOLEAN DeferConnIDGen, + _In_ uint32_t DropPacketCount + ) +{ + PathTestContext Context; + CxPlatEvent PeerStreamsChanged; + MsQuicRegistration Registration(true); + TEST_TRUE(Registration.IsValid()); + + MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", ServerSelfSignedCredConfig); + TEST_TRUE(ServerConfiguration.IsValid()); + + if (DeferConnIDGen) { + BOOLEAN DisableConnIdGeneration = TRUE; + TEST_QUIC_SUCCEEDED( + ServerConfiguration.SetParam( + QUIC_PARAM_CONFIGURATION_CONN_ID_GENERATION_DISABLED, + sizeof(DisableConnIdGeneration), + &DisableConnIdGeneration)); + } + + MsQuicCredentialConfig ClientCredConfig; + MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", ClientCredConfig); + TEST_TRUE(ClientConfiguration.IsValid()); + + MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, PathTestContext::ConnCallback, &Context); + TEST_QUIC_SUCCEEDED(Listener.GetInitStatus()); + QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6; + QuicAddr ServerLocalAddr(QuicAddrFamily); + TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest", &ServerLocalAddr.SockAddr)); + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + MsQuicConnection Connection(Registration, CleanUpManual, ClientCallback, &PeerStreamsChanged); + TEST_QUIC_SUCCEEDED(Connection.GetInitStatus()); + + if (ShareBinding) { + Connection.SetShareUdpBinding(); + } + + QuicAddr ClientLocalAddrs[4] = {QuicAddrFamily, QuicAddrFamily, QuicAddrFamily, QuicAddrFamily}; + for (uint8_t i = 0; i < 4; i++) { + ClientLocalAddrs[i].SetPort(rand() % 65536); + QUIC_STATUS Status; + uint32_t Try = 0; + do { + Status = Connection.SetParam( + QUIC_PARAM_CONN_ADD_LOCAL_ADDRESS, + sizeof(ClientLocalAddrs[i].SockAddr), + &ClientLocalAddrs[i].SockAddr); + if (Status != QUIC_STATUS_ADDRESS_IN_USE) { + TEST_QUIC_SUCCEEDED(Status); + break; + } + } while (++Try < 3); + TEST_QUIC_SUCCEEDED(Status); + } + + PathProbeHelper ProbeHelpers[3] = { + {ClientLocalAddrs[1].GetPort(), DropPacketCount, DropPacketCount}, + {ClientLocalAddrs[2].GetPort(), DropPacketCount, DropPacketCount}, + {ClientLocalAddrs[3].GetPort(), DropPacketCount, DropPacketCount}}; + + TEST_QUIC_SUCCEEDED(Connection.Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort())); + TEST_TRUE(Connection.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); + TEST_TRUE(Context.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); + TEST_NOT_EQUAL(nullptr, Context.Connection); + + if (DeferConnIDGen) { + TEST_QUIC_SUCCEEDED(Context.Connection->SetParam(QUIC_PARAM_CONN_GENERATE_CONN_ID, 0, NULL)); + } + + TEST_TRUE(ProbeHelpers[0].ServerReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + TEST_TRUE(ProbeHelpers[0].ClientReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + TEST_TRUE(ProbeHelpers[1].ServerReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + TEST_TRUE(ProbeHelpers[1].ClientReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + TEST_TRUE(ProbeHelpers[2].ServerReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); + TEST_TRUE(ProbeHelpers[2].ClientReceiveProbeEvent.WaitTimeout(TestWaitTimeout * 10)); +} +#endif diff --git a/src/test/lib/TestHelpers.h b/src/test/lib/TestHelpers.h index d4a04f9d58..cbfd6ce504 100644 --- a/src/test/lib/TestHelpers.h +++ b/src/test/lib/TestHelpers.h @@ -782,6 +782,49 @@ struct MtuDropHelper : public DatapathHook } }; +struct PathProbeHelper : public DatapathHook +{ + uint16_t ClientProbePort; + CxPlatEvent ServerReceiveProbeEvent; + CxPlatEvent ClientReceiveProbeEvent; + uint32_t ClientDropPacketCount; + uint32_t ServerDropPacketCount; + PathProbeHelper(uint16_t ClientPort, uint32_t ClientCount = 0, uint32_t ServerCount = 0) : + ClientProbePort(ClientPort), + ClientDropPacketCount(ClientCount), + ServerDropPacketCount(ServerCount) { + DatapathHooks::Instance->AddHook(this); + } + ~PathProbeHelper() { + DatapathHooks::Instance->RemoveHook(this); + } + void ClientDropPackets(uint32_t Count) { ClientDropPacketCount = Count; } + void ServerDropPackets(uint32_t Count) { ServerDropPacketCount = Count; } + _IRQL_requires_max_(DISPATCH_LEVEL) + BOOLEAN + Receive( + _Inout_ struct CXPLAT_RECV_DATA* Datagram + ) { + if (QuicAddrGetPort(&Datagram->Route->RemoteAddress) == ClientProbePort) { + if (ClientDropPacketCount == 0) { + ServerReceiveProbeEvent.Set(); + } else { + ClientDropPacketCount--; + return TRUE; + } + } + if (QuicAddrGetPort(&Datagram->Route->LocalAddress) == ClientProbePort) { + if (ServerDropPacketCount == 0) { + ClientReceiveProbeEvent.Set(); + } else { + ServerDropPacketCount--; + return TRUE; + } + } + return FALSE; + } +}; + struct ReplaceAddressHelper : public DatapathHook { QUIC_ADDR Original;