diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2c875e9423..0232166913 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCES configuration.c congestion_control.c connection.c + connection_pool.c crypto.c crypto_tls.c cubic.c diff --git a/src/core/api.h b/src/core/api.h index 0ee664a9f0..103ce029c9 100644 --- a/src/core/api.h +++ b/src/core/api.h @@ -287,3 +287,11 @@ MsQuicConnectionCertificateValidationComplete( _In_ BOOLEAN Result, _In_ QUIC_TLS_ALERT_CODES TlsAlert ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +QUIC_STATUS +QUIC_API +MsQuicConnectionPoolApiOpen( + _Out_ _Pre_defensive_ const void** ConnPoolApi + ); diff --git a/src/core/library.c b/src/core/library.c index 09802a1168..17ceef9536 100644 --- a/src/core/library.c +++ b/src/core/library.c @@ -370,6 +370,7 @@ MsQuicLibraryInitialize( CxPlatZeroMemory(MsQuicLib.PerfCounterSamples, sizeof(MsQuicLib.PerfCounterSamples)); CxPlatRandom(sizeof(MsQuicLib.ToeplitzHash.HashKey), MsQuicLib.ToeplitzHash.HashKey); + MsQuicLib.ToeplitzHash.InputSize = CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC; CxPlatToeplitzHashInitialize(&MsQuicLib.ToeplitzHash); CxPlatZeroMemory(&MsQuicLib.Settings, sizeof(MsQuicLib.Settings)); @@ -1802,6 +1803,8 @@ MsQuicOpenVersion( Api->DatagramSend = MsQuicDatagramSend; + Api->ConnectionPoolApiOpen = MsQuicConnectionPoolApiOpen; + *QuicApi = Api; Exit: @@ -2456,3 +2459,77 @@ QuicLibraryGenerateStatelessResetToken( } return Status; } + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +MsQuicConnectionPoolApiClose( + _In_ _Pre_defensive_ const void* ConnPoolApi + ) +{ + if (ConnPoolApi != NULL) { + QuicTraceLogVerbose( + ApiMsQuicConnectionPoolApiClose, + "[ api] MsQuicConnectionPoolApiClose"); + CXPLAT_FREE(ConnPoolApi, QUIC_POOL_CONN_POOL_API_TABLE); + MsQuicRelease(); + } +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +QUIC_STATUS +QUIC_API +MsQuicConnectionPoolApiOpen( + _Out_ _Pre_defensive_ const void** ConnPoolApi + ) +{ + QUIC_STATUS Status; + BOOLEAN ReleaseRefOnFailure = FALSE; + + if (ConnPoolApi == NULL) { + QuicTraceLogVerbose( + ApiMsQuicConnectionPoolApiOpenNull, + "[ api] MsQuicConnectionPoolApiOpen, NULL"); + Status = QUIC_STATUS_INVALID_PARAMETER; + goto Exit; + } + + QuicTraceLogVerbose( + ApiMsQuicConnectionPoolApiOpenEntry, + "[ api] MsQuicConnectionPoolApiOpen"); + + Status = MsQuicAddRef(); + if (QUIC_FAILED(Status)) { + goto Exit; + } + ReleaseRefOnFailure = TRUE; + + QUIC_CONNECTION_POOL_API_TABLE* Api = + CXPLAT_ALLOC_NONPAGED( + sizeof(QUIC_CONNECTION_POOL_API_TABLE), + QUIC_POOL_CONN_POOL_API_TABLE); + if (Api == NULL) { + Status = QUIC_STATUS_OUT_OF_MEMORY; + goto Exit; + } + + Api->ConnectionPoolApiClose = MsQuicConnectionPoolApiClose; + Api->SimpleConnectionPoolCreate = MsQuicSimpleConnectionPoolCreate; + + *ConnPoolApi = Api; + +Exit: + + QuicTraceLogVerbose( + ApiMsQuicConnectionPoolApiOpenExit, + "[ api] MsQuicConnectionPoolApiOpen, status=0x%x", + Status); + + if (QUIC_FAILED(Status)) { + if (ReleaseRefOnFailure) { + MsQuicRelease(); + } + } + + return Status; +} diff --git a/src/core/precomp.h b/src/core/precomp.h index 253cc48be9..7c63a555fe 100644 --- a/src/core/precomp.h +++ b/src/core/precomp.h @@ -73,6 +73,7 @@ #include "datagram.h" #include "version_neg.h" #include "connection.h" +#include "connection_pool.h" #include "packet_builder.h" #include "listener.h" #include "cubic.h" diff --git a/src/inc/msquic.h b/src/inc/msquic.h index 6f8b57fa6b..c878d48d8d 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -1605,6 +1605,62 @@ QUIC_STATUS _In_opt_ void* ClientSendContext ); +// +// Connection Pool API +// + +// +// Closes the Connection Pool API table and cleans up the API reference. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_CONN_POOL_API_CLOSE)( + _In_ void* ConnPoolApi + ); + +// +// Creates a simple connection pool with NumberOfConnections connections +// all with the same Context and Handler, and puts them in the +// caller-supplied array. +// +// One of ServerName or ServerAddress *MUST* be supplied, and for the pool to +// work correctly, the connections *MUST* be started with the same ServerName +// and ServerPort as supplied in this call. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +QUIC_STATUS +(QUIC_API * QUIC_SIMPLE_CONN_POOL_CREATE_FN)( + _In_ HQUIC* Registration, + _In_ QUIC_CONNECTION_CALLBACK_HANDLER Handler, + _In_opt_ void* Context, + _In_opt_ const char* ServerName, + _In_opt_ const QUIC_ADDR* ServerAddress, + _In_ uint16_t ServerPort, + _In_ uint32_t NumberOfConnections, + _Out_writes_bytes_(NumberOfConnections * sizeof(HQUIC)) + HQUIC** ConnectionPool + ); + +typedef struct QUIC_CONNECTION_POOL_API_TABLE { + QUIC_CONN_POOL_API_CLOSE ConnectionPoolApiClose; + QUIC_SIMPLE_CONN_POOL_CREATE_FN SimpleConnectionPoolCreate; + +} QUIC_CONNECTION_POOL_API_TABLE; + +// +// Gets the function table for the Connection Pool API, for a given version. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_POOL_API_OPEN_FN)( + _Out_ _Pre_defensive_ const void** ConnPoolApi + ); + // // Version 2 API Function Table. Returned from MsQuicOpenVersion when Version // is 2. Also returned from MsQuicOpen2. @@ -1657,6 +1713,8 @@ typedef struct QUIC_API_TABLE { QUIC_STREAM_PROVIDE_RECEIVE_BUFFERS_FN StreamProvideReceiveBuffers; // Available from v2.5 #endif + QUIC_CONNECTION_POOL_API_OPEN_FN ConnectionPoolApiOpen; // Available from v2.5 + } QUIC_API_TABLE; #define QUIC_API_VERSION_1 1 // Not supported any more diff --git a/src/inc/quic_datapath.h b/src/inc/quic_datapath.h index a3c88f4039..6c6f6c6cdc 100644 --- a/src/inc/quic_datapath.h +++ b/src/inc/quic_datapath.h @@ -858,6 +858,37 @@ CxPlatUpdateRoute( _In_ CXPLAT_ROUTE* SrcRoute ); +// +// Get the RSS Configuration of the interface. +// +#define CXPLAT_RSS_HASH_TYPE_IPV4 0x001 +#define CXPLAT_RSS_HASH_TYPE_TCP_IPV4 0x002 +#define CXPLAT_RSS_HASH_TYPE_UDP_IPV4 0x004 +#define CXPLAT_RSS_HASH_TYPE_IPV6 0x008 +#define CXPLAT_RSS_HASH_TYPE_TCP_IPV6 0x010 +#define CXPLAT_RSS_HASH_TYPE_UDP_IPV6 0x020 +#define CXPLAT_RSS_HASH_TYPE_IPV6_EX 0x040 +#define CXPLAT_RSS_HASH_TYPE_TCP_IPV6_EX 0x080 +#define CXPLAT_RSS_HASH_TYPE_UDP_IPV6_EX 0x100 + +typedef uint32_t CXPLAT_RSS_HASH_TYPE; + +typedef struct CXPLAT_RSS_CONFIG { + CXPLAT_RSS_HASH_TYPE HashTypes; + uint32_t RssSecretKeyLength; + uint32_t RssIndirectionTableLength; + uint8_t* RssSecretKey; + CXPLAT_PROCESSOR_INFO* RssIndirectionTable; +} CXPLAT_RSS_CONFIG; + +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +CxPlatDataPathGetRssConfig( + _In_ uint32_t InterfaceIndex, + _Outptr_ _At_(*RssConfig, __drv_allocatesMem(Mem)) + CXPLAT_RSS_CONFIG** RssConfig + ); + #if defined(__cplusplus) } #endif diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h index cf75a686f8..56dbf4989e 100644 --- a/src/inc/quic_platform.h +++ b/src/inc/quic_platform.h @@ -149,6 +149,8 @@ typedef struct CXPLAT_SLIST_ENTRY { #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_APP_BUFFER_CHUNK 'D4cQ' // Qc4D - QUIC receive chunk for app buffers +#define QUIC_POOL_CONN_POOL_API_TABLE 'E4cQ' // Qc4E - QUIC Connection Pool API table +#define QUIC_POOL_DATAPATH_RSS_CONFIG 'F4cQ' // Qc4F - QUIC Datapath RSS configuration typedef enum CXPLAT_THREAD_FLAGS { CXPLAT_THREAD_FLAG_NONE = 0x0000, diff --git a/src/inc/quic_toeplitz.h b/src/inc/quic_toeplitz.h index 1af96d2fed..f5a3ccc77c 100644 --- a/src/inc/quic_toeplitz.h +++ b/src/inc/quic_toeplitz.h @@ -16,11 +16,23 @@ extern "C" { // // The size (in bytes) of the input. +// +typedef uint32_t CXPLAT_TOEPLITZ_INPUT_SIZE; + +// +// This includes space for 16 bytes for source IPv6 address, 16 bytes for +// destination IPv6 address, 2 bytes for source port, and 2 bytes for +// destination port. +// +#define CXPLAT_TOEPLITZ_INPUT_SIZE_IP 36 +// // This includes space for a 20 bytes for CID, 16 bytes for IPv6 address // and 2 bytes for UDP port. // -#define CXPLAT_TOEPLITZ_INPUT_SIZE 38 +#define CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC 38 + +#define CXPLAT_TOEPLITZ_INPUT_SIZE_MAX 38 // // The size (in bytes) of the output hash. @@ -29,9 +41,9 @@ extern "C" { #define CXPLAT_TOEPLITZ_OUPUT_SIZE sizeof(uint32_t) // -// The size (in bytes) of the key is equal to the size of the input and output. +// The max size (in bytes) of the key is equal to the size of the input and output. // -#define CXPLAT_TOEPLITZ_KEY_SIZE (CXPLAT_TOEPLITZ_INPUT_SIZE + CXPLAT_TOEPLITZ_OUPUT_SIZE) +#define CXPLAT_TOEPLITZ_KEY_SIZE_MAX (CXPLAT_TOEPLITZ_INPUT_SIZE_MAX + CXPLAT_TOEPLITZ_OUPUT_SIZE) // // Fixed lookup table size. @@ -39,17 +51,18 @@ extern "C" { #define CXPLAT_TOEPLITZ_LOOKUP_TABLE_SIZE 16 // -// Fixed number of lookup tables. +// Fixed number of lookup tables. Use the maximum of two possible values. // -#define CXPLAT_TOEPLITZ_LOOKUP_TABLE_COUNT (CXPLAT_TOEPLITZ_INPUT_SIZE * NIBBLES_PER_BYTE) +#define CXPLAT_TOEPLITZ_LOOKUP_TABLE_COUNT_MAX (CXPLAT_TOEPLITZ_INPUT_SIZE_MAX * NIBBLES_PER_BYTE) typedef struct CXPLAT_TOEPLITZ_LOOKUP_TABLE { uint32_t Table[CXPLAT_TOEPLITZ_LOOKUP_TABLE_SIZE]; } CXPLAT_TOEPLITZ_LOOKUP_TABLE; typedef struct CXPLAT_TOEPLITZ_HASH { - CXPLAT_TOEPLITZ_LOOKUP_TABLE LookupTableArray[CXPLAT_TOEPLITZ_LOOKUP_TABLE_COUNT]; - uint8_t HashKey[CXPLAT_TOEPLITZ_KEY_SIZE]; + CXPLAT_TOEPLITZ_LOOKUP_TABLE LookupTableArray[CXPLAT_TOEPLITZ_LOOKUP_TABLE_COUNT_MAX]; + uint8_t HashKey[CXPLAT_TOEPLITZ_KEY_SIZE_MAX]; + CXPLAT_TOEPLITZ_INPUT_SIZE InputSize; } CXPLAT_TOEPLITZ_HASH; // @@ -113,6 +126,70 @@ CxPlatToeplitzHashComputeAddr( } } +// +// Computes the Toeplitz hash of two QUIC addresses as RSS would. +// +inline +void +CxPlatToeplitzHashComputeRss( + _In_ const CXPLAT_TOEPLITZ_HASH* Toeplitz, + _In_ const QUIC_ADDR* SrcAddr, + _In_ const QUIC_ADDR* DestAddr, + _Inout_ uint32_t* Key, + _Out_ uint32_t* Offset + ) +{ + CXPLAT_FRE_ASSERT(QuicAddrGetFamily(SrcAddr) == QuicAddrGetFamily(DestAddr)); + + if (QuicAddrGetFamily(SrcAddr) == QUIC_ADDRESS_FAMILY_INET) { + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)SrcAddr) + QUIC_ADDR_V4_IP_OFFSET, + 4, 0); + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)DestAddr) + QUIC_ADDR_V4_IP_OFFSET, + 4, 4); + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)SrcAddr) + QUIC_ADDR_V4_PORT_OFFSET, + 2, 8); + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)DestAddr) + QUIC_ADDR_V4_PORT_OFFSET, + 2, 10); + *Offset = 4 + 4 + 2 + 2; + } else { + CXPLAT_DBG_ASSERT(QuicAddrGetFamily(SrcAddr) == QUIC_ADDRESS_FAMILY_INET6); + + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)SrcAddr) + QUIC_ADDR_V6_IP_OFFSET, + 16, 0); + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)DestAddr) + QUIC_ADDR_V6_IP_OFFSET, + 16, 16); + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)SrcAddr) + QUIC_ADDR_V6_PORT_OFFSET, + 2, 32); + *Key ^= + CxPlatToeplitzHashCompute( + Toeplitz, + ((uint8_t*)DestAddr) + QUIC_ADDR_V6_PORT_OFFSET, + 2, 34); + *Offset = 16 + 16 + 2 + 2; + } +} + #if defined(__cplusplus) } #endif diff --git a/src/platform/datapath_raw_xdp_win.c b/src/platform/datapath_raw_xdp_win.c index e7a745ea58..dcac076e65 100644 --- a/src/platform/datapath_raw_xdp_win.c +++ b/src/platform/datapath_raw_xdp_win.c @@ -1882,3 +1882,115 @@ CxPlatIoXdpShutdownEventComplete( Partition); CxPlatDpRawRelease((XDP_DATAPATH*)Partition->Xdp); } + +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +CxPlatDataPathGetRssConfig( + _In_ uint32_t InterfaceIndex, + _Outptr_ _At_(*RssConfig, __drv_allocatesMem(Mem)) + CXPLAT_RSS_CONFIG** RssConfig + ) +{ + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + uint32_t RssConfigSize = 0; + XDP_RSS_CONFIGURATION *RawRssConfig = NULL; + + if (RssConfig == NULL) { + Status = QUIC_STATUS_INVALID_PARAMETER; + goto Error; + } + + *RssConfig = NULL; + + HANDLE InterfaceHandle = NULL; + HRESULT Result = XdpInterfaceOpen(InterfaceIndex, &InterfaceHandle); + if (FAILED(Result)) { + QuicTraceLogError( + XdpGetRssConfigInterfaceOpenFailed, + "[ xdp] Failed to open Xdp interface for index %u. Error %d", + InterfaceIndex, + Result); + Status = Result; + goto Error; + } + + Result = XdpRssGet(InterfaceHandle, RawRssConfig, &RssConfigSize); + if (SUCCEEDED(Result)) { + QuicTraceLogError( + XdpGetRssConfigSizeFailed, + "[ xdp][%p] Failed to get RSS configuration size on IfIndex %u, RssConfigSize %u, Result %d", + InterfaceHandle, + InterfaceIndex, + RssConfigSize, + Result); + Status = QUIC_STATUS_INTERNAL_ERROR; + goto Error; + } + + RawRssConfig = CXPLAT_ALLOC_PAGED(RssConfigSize, QUIC_POOL_TMP_ALLOC); + if (RawRssConfig == NULL) { + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "XDP RSS Config", + RssConfigSize); + Status = QUIC_STATUS_OUT_OF_MEMORY; + goto Error; + } + + Result = XdpRssGet(InterfaceHandle, RawRssConfig, &RssConfigSize); + if (FAILED(Result)) { + QuicTraceLogError( + XdpGetRssConfigFailed, + "[ xdp][%p] Failed to get RSS configuration on IfIndex %u, RssConfigSize %u, Result %d", + InterfaceHandle, + InterfaceIndex, + RssConfigSize, + Result); + Status = Result; + goto Error; + } + + RssConfigSize = sizeof(CXPLAT_RSS_CONFIG) + + RawRssConfig->HashSecretKeySize + + RawRssConfig->IndirectionTableSize; + CXPLAT_RSS_CONFIG* NewRssConfig = + (CXPLAT_RSS_CONFIG*)CXPLAT_ALLOC_NONPAGED( + RssConfigSize, + QUIC_POOL_DATAPATH_RSS_CONFIG); + if (NewRssConfig == NULL) { + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "CXPLAT RSS Config", + RssConfigSize); + Status = QUIC_STATUS_OUT_OF_MEMORY; + goto Error; + } + + NewRssConfig->HashTypes = RawRssConfig->HashType; + + NewRssConfig->RssSecretKey = (uint8_t*)(NewRssConfig + 1); + NewRssConfig->RssSecretKeyLength = RawRssConfig->HashSecretKeySize; + CxPlatCopyMemory( + NewRssConfig->RssSecretKey, + RTL_PTR_ADD(RawRssConfig, RawRssConfig->HashSecretKeyOffset), + NewRssConfig->RssSecretKeyLength); + + NewRssConfig->RssIndirectionTable = + (CXPLAT_PROCESSOR_INFO*)(NewRssConfig->RssSecretKey + NewRssConfig->RssSecretKeyLength); + NewRssConfig->RssIndirectionTableLength = RawRssConfig->IndirectionTableSize; + CxPlatCopyMemory( + NewRssConfig->RssIndirectionTable, + RTL_PTR_ADD(RawRssConfig, RawRssConfig->IndirectionTableOffset), + NewRssConfig->RssIndirectionTableLength); + + *RssConfig = NewRssConfig; + NewRssConfig = NULL; + +Error: + if (RawRssConfig != NULL) { + CXPLAT_FREE(RawRssConfig, QUIC_POOL_TMP_ALLOC); + } + return Status; +} diff --git a/src/platform/inline.c b/src/platform/inline.c index 5ab290945b..598683b6fe 100644 --- a/src/platform/inline.c +++ b/src/platform/inline.c @@ -401,6 +401,15 @@ CxPlatToeplitzHashComputeAddr( _Out_ uint32_t* Offset ); +void +CxPlatToeplitzHashComputeRss( + _In_ const CXPLAT_TOEPLITZ_HASH* Toeplitz, + _In_ const QUIC_ADDR* SrcAddr, + _In_ const QUIC_ADDR* DestAddr, + _Inout_ uint32_t* Key, + _Out_ uint32_t* Offset + ); + BOOLEAN CxPlatEventQInitialize( _Out_ CXPLAT_EVENTQ* queue diff --git a/src/platform/toeplitz.c b/src/platform/toeplitz.c index 1363df05a8..afd3317ee2 100644 --- a/src/platform/toeplitz.c +++ b/src/platform/toeplitz.c @@ -75,7 +75,7 @@ CxPlatToeplitzHashInitialize( // // Initialize the Toeplitz->LookupTables. // - for (uint32_t i = 0; i < CXPLAT_TOEPLITZ_LOOKUP_TABLE_COUNT; i++) { + for (uint32_t i = 0; i < Toeplitz->InputSize; i++) { // // First construct the 32-bit word that is obtained after // shifting the key left by i*4 bits. That goes into Word1 @@ -151,8 +151,10 @@ CxPlatToeplitzHashCompute( uint32_t BaseOffset = HashInputOffset * NIBBLES_PER_BYTE; uint32_t Result = 0; + CXPLAT_DBG_ASSERT(HashInputLength + HashInputOffset <= Toeplitz->InputSize); + CXPLAT_DBG_ASSERT( - (BaseOffset + HashInputLength * NIBBLES_PER_BYTE) <= CXPLAT_TOEPLITZ_LOOKUP_TABLE_COUNT); + (BaseOffset + HashInputLength * NIBBLES_PER_BYTE) <= (Toeplitz->InputSize * NIBBLES_PER_BYTE)); for (uint32_t i = 0; i < HashInputLength; i++) { Result ^= Toeplitz->LookupTableArray[BaseOffset].Table[(HashInput[i] >> 4) & 0xf]; diff --git a/src/platform/unittest/CMakeLists.txt b/src/platform/unittest/CMakeLists.txt index 481c852faa..4a1f905842 100644 --- a/src/platform/unittest/CMakeLists.txt +++ b/src/platform/unittest/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES DataPathTest.cpp PlatformTest.cpp # StorageTest.cpp + ToeplitzTest.cpp TlsTest.cpp ) diff --git a/src/platform/unittest/ToeplitzTest.cpp b/src/platform/unittest/ToeplitzTest.cpp new file mode 100644 index 0000000000..ad93252e7c --- /dev/null +++ b/src/platform/unittest/ToeplitzTest.cpp @@ -0,0 +1,182 @@ +/*++ + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +--*/ + +#define _CRT_SECURE_NO_WARNINGS 1 +#include "main.h" +#include "msquic.h" +#include "msquichelper.h" +#include "quic_toeplitz.h" +#include + +#ifdef QUIC_CLOG +#include "ToeplitzTest.cpp.clog.h" +#endif + +struct ToeplitzTest : public ::testing::Test +{ + protected: + static const char* HashKey; + + struct QuicBuffer + { + uint8_t* Data; + uint16_t Length; + + QuicBuffer(const char* HexBytes) + { + Length = (uint16_t)(strlen(HexBytes) / 2); + Data = new uint8_t[Length]; + + for (uint16_t i = 0; i < Length; ++i) { + Data[i] = + (DecodeHexChar(HexBytes[i * 2]) << 4) | + DecodeHexChar(HexBytes[i * 2 + 1]); + } + } + + ~QuicBuffer() + { + delete [] Data; + } + }; + + struct QuicTestAddress { + QUIC_ADDR Addr; + QuicTestAddress(const char* AddrStr, uint16_t Port) { + EXPECT_TRUE(QuicAddrFromString(AddrStr, Port, &Addr)); + } + }; + + static + auto + RunTest( + _In_ const char** ExpectedHashes, + _In_ const char** SourceAddresses, + _In_ const uint16_t* SourcePorts, + _In_ const char** DestinationAddresses, + _In_ const uint16_t* DestinationPorts, + _In_ uint32_t TestCaseCount, + _In_ QUIC_ADDRESS_FAMILY Family + ) + { + const QuicBuffer KeyBuffer(HashKey); + + CXPLAT_TOEPLITZ_HASH ToeplitzHash{}; + CxPlatCopyMemory(ToeplitzHash.HashKey, KeyBuffer.Data, KeyBuffer.Length); + ToeplitzHash.InputSize = CXPLAT_TOEPLITZ_INPUT_SIZE_IP; + CxPlatToeplitzHashInitialize(&ToeplitzHash); + + for (uint32_t i = 0; i < TestCaseCount; i++) { + printf("Testing Iteration %d...\n", i + 1); + + QuicBuffer ExpectedHash(ExpectedHashes[i]); + + QUIC_ADDR SrcAddr; + ASSERT_TRUE(QuicAddrFromString(SourceAddresses[i], SourcePorts[i], &SrcAddr)); + ASSERT_EQ(SrcAddr.si_family, Family); + + QUIC_ADDR DestAddr; + ASSERT_TRUE(QuicAddrFromString(DestinationAddresses[i], DestinationPorts[i], &DestAddr)); + ASSERT_EQ(DestAddr.si_family, Family); + + uint32_t Key = 0, Offset = 0; + CxPlatToeplitzHashComputeRss(&ToeplitzHash, &SrcAddr, &DestAddr, &Key, &Offset); + + // Flip the key around to match the expected hash array + Key = CxPlatByteSwapUint32(Key); + + if (memcmp(ExpectedHash.Data, &Key, 4)) { + QUIC_ADDR_STR PrintBuf{}; + printf("Expected Hash: %s, Actual Hash: %x\n", ExpectedHashes[i], Key); + QuicAddrToString(&SrcAddr, &PrintBuf); + printf("Source Address: %s\n", PrintBuf.Address); + QuicAddrToString(&DestAddr, &PrintBuf); + printf("Destination Address: %s\n", PrintBuf.Address); + ASSERT_TRUE(FALSE); + } + } + } + +}; + +const char* ToeplitzTest::HashKey = "6d5a56da255b0ec24167253d43a38fb0d0ca2bcbae7b30b477cb2da38030f20c6a42b73bbeac01fa"; + +TEST_F(ToeplitzTest, IPv4WithTcp) +{ + const char* ExpectedHashes[] = { + "51ccc178", + "c626b0ea", + "5c2b394a", + "afc7327f", + "10e828a2" + }; + const char* SourceAddresses[] = { + "66.9.149.187", + "199.92.111.2", + "24.19.198.95", + "38.27.205.30", + "153.39.163.191" + }; + const uint16_t SourcePorts[] = { + 2794, + 14230, + 12898, + 48228, + 44251 + }; + const char* DestinationAddresses[] = { + "161.142.100.80", + "65.69.140.83", + "12.22.207.184", + "209.142.163.6", + "202.188.127.2" + }; + const uint16_t DestinationPorts[] = { + 1766, + 4739, + 38024, + 2217, + 1303 + }; + + RunTest(ExpectedHashes, SourceAddresses, SourcePorts, DestinationAddresses, DestinationPorts, 5, QUIC_ADDRESS_FAMILY_INET); +} + +TEST_F(ToeplitzTest, IPv6WithTcp) +{ + const char* ExpectedHashes[] = { + "40207d3d", + "dde51bbf", + "02d1feef" + }; + + const char* SourceAddresses[] = { + "3ffe:2501:200:1fff::7", + "3ffe:501:8::260:97ff:fe40:efab", + "3ffe:1900:4545:3:200:f8ff:fe21:67cf" + }; + + const uint16_t SourcePorts[] = { + 2794, + 14230, + 44251 + }; + + const char* DestinationAddresses[] = { + "3ffe:2501:200:3::1", + "ff02::1", + "fe80::200:f8ff:fe21:67cf" + }; + + const uint16_t DestinationPorts[] = { + 1766, + 4739, + 38024 + }; + + RunTest(ExpectedHashes, SourceAddresses, SourcePorts, DestinationAddresses, DestinationPorts, 3, QUIC_ADDRESS_FAMILY_INET6); +} diff --git a/src/test/lib/TestHelpers.h b/src/test/lib/TestHelpers.h index f077d16fcc..c7ad9c3e83 100644 --- a/src/test/lib/TestHelpers.h +++ b/src/test/lib/TestHelpers.h @@ -931,7 +931,8 @@ struct LoadBalancerHelper : public DatapathHook uint32_t PrivateAddressesCount; LoadBalancerHelper(const QUIC_ADDR& Public, const QUIC_ADDR* Private, uint32_t PrivateCount) : PublicAddress(Public), PrivateAddresses(Private), PrivateAddressesCount(PrivateCount) { - CxPlatRandom(CXPLAT_TOEPLITZ_KEY_SIZE, &Toeplitz.HashKey); + CxPlatRandom(CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC, &Toeplitz.HashKey); + Toeplitz.InputSize = CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC; CxPlatToeplitzHashInitialize(&Toeplitz); DatapathHooks::Instance->AddHook(this); } diff --git a/src/tools/lb/loadbalancer.cpp b/src/tools/lb/loadbalancer.cpp index c95ac4e6f6..875f44c5a2 100644 --- a/src/tools/lb/loadbalancer.cpp +++ b/src/tools/lb/loadbalancer.cpp @@ -123,7 +123,8 @@ struct LbPublicInterface : public LbInterface { struct Hasher { CXPLAT_TOEPLITZ_HASH Toeplitz; Hasher() { - CxPlatRandom(CXPLAT_TOEPLITZ_KEY_SIZE, &Toeplitz.HashKey); + CxPlatRandom(CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC, &Toeplitz.HashKey); + Toeplitz.InputSize = CXPLAT_TOEPLITZ_INPUT_SIZE_QUIC; CxPlatToeplitzHashInitialize(&Toeplitz); } size_t operator() (const std::pair key) const {