Skip to content

Refactor TCP Security Config for Reuse on Client Connections #5015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/perf/lib/PerfClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,8 @@ PerfClientConnection::~PerfClientConnection() {
void
PerfClientConnection::Initialize() {
if (Client.UseTCP) {
auto CredConfig = MsQuicCredentialConfig(QUIC_CREDENTIAL_FLAG_CLIENT | QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION);
TcpConn = // TODO: replace new/delete with pool alloc/free
new (std::nothrow) TcpConnection(Client.Engine.get(), &CredConfig, this);
new (std::nothrow) TcpConnection(Client.Engine.get(), &Client.TcpConfig, this);
if (!TcpConn->IsInitialized()) {
Worker.ConnectionPool.Free(this);
return;
Expand Down
8 changes: 5 additions & 3 deletions src/perf/lib/PerfClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ struct PerfClient {
PerfClientWorker Workers[PERF_MAX_THREAD_COUNT];

UniquePtr<TcpEngine> Engine;
MsQuicCredentialConfig CredentialConfig {
QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION};
TcpConfiguration TcpConfig {&CredentialConfig};
MsQuicRegistration Registration {
"perf-client",
PerfDefaultExecutionProfile,
Expand All @@ -166,9 +170,7 @@ struct PerfClient {
.SetCongestionControlAlgorithm(PerfDefaultCongestionControl)
.SetEcnEnabled(PerfDefaultEcnEnabled)
.SetEncryptionOffloadAllowed(PerfDefaultQeoAllowed),
MsQuicCredentialConfig(
QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION)};
CredentialConfig};
// Target parameters
UniquePtr<char[]> Target;
QUIC_ADDRESS_FAMILY TargetFamily {QUIC_ADDRESS_FAMILY_UNSPEC};
Expand Down
4 changes: 3 additions & 1 deletion src/perf/lib/PerfServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class PerfServer {
public:
PerfServer(const QUIC_CREDENTIAL_CONFIG* CredConfig) :
Engine(TcpAcceptCallback, TcpConnectCallback, TcpReceiveCallback, TcpSendCompleteCallback, TcpDefaultExecutionProfile),
Server(&Engine, CredConfig, this) {
TcpConfig(CredConfig),
Server(&Engine, &TcpConfig, this) {
CxPlatZeroMemory(&LocalAddr, sizeof(LocalAddr));
QuicAddrSetFamily(&LocalAddr, QUIC_ADDRESS_FAMILY_UNSPEC);
QuicAddrSetPort(&LocalAddr, PERF_DEFAULT_PORT);
Expand Down Expand Up @@ -189,6 +190,7 @@ class PerfServer {
uint8_t PrintStats {FALSE};

TcpEngine Engine;
TcpConfiguration TcpConfig;
TcpServer Server;

uint32_t DelayMicroseconds {0};
Expand Down
97 changes: 43 additions & 54 deletions src/perf/lib/Tcp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,44 +49,45 @@ const uint8_t FixedIv[CXPLAT_MAX_IV_LENGTH] = { 0 };

const QUIC_HKDF_LABELS TcpHkdfLabels = { "tcp key", "tcp iv", "tcp hp", "tcp ku" };

struct LoadSecConfigHelper {
LoadSecConfigHelper() : SecConfig(nullptr) { CxPlatEventInitialize(&CallbackEvent, TRUE, FALSE); }
~LoadSecConfigHelper() { CxPlatEventUninitialize(CallbackEvent); }
CXPLAT_SEC_CONFIG* Load(const QUIC_CREDENTIAL_CONFIG* CredConfig) {
if (QUIC_FAILED(
CxPlatTlsSecConfigCreate(
CredConfig,
CXPLAT_TLS_CREDENTIAL_FLAG_NONE,
&TcpEngine::TlsCallbacks,
this,
SecConfigCallback))) {
return nullptr;
}
CxPlatEventWaitForever(CallbackEvent);
return SecConfig;
}
private:
static
_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(CXPLAT_SEC_CONFIG_CREATE_COMPLETE)
void
QUIC_API
SecConfigCallback(
_In_ const QUIC_CREDENTIAL_CONFIG* /* CredConfig */,
_In_opt_ void* Context,
_In_ QUIC_STATUS Status,
_In_opt_ CXPLAT_SEC_CONFIG* SecurityConfig
)
{
LoadSecConfigHelper* This = (LoadSecConfigHelper*)Context;
if (QUIC_SUCCEEDED(Status)) {
This->SecConfig = SecurityConfig;
}
CxPlatEventSet(This->CallbackEvent);
TcpConfiguration::TcpConfiguration(const QUIC_CREDENTIAL_CONFIG* CredConfig) noexcept
{
CxPlatEventInitialize(&CallbackEvent, TRUE, FALSE);
if (QUIC_FAILED(
CxPlatTlsSecConfigCreate(
CredConfig,
CXPLAT_TLS_CREDENTIAL_FLAG_NONE,
&TcpEngine::TlsCallbacks,
this,
SecConfigCallback))) {
CXPLAT_DBG_ASSERT(false);
}
CXPLAT_EVENT CallbackEvent;
CXPLAT_SEC_CONFIG* SecConfig;
};
CxPlatEventWaitForever(CallbackEvent);
CXPLAT_DBG_ASSERT(SecConfig != nullptr);
}

TcpConfiguration::~TcpConfiguration() noexcept
{
if (SecConfig) {
CxPlatTlsSecConfigDelete(SecConfig);
}
CxPlatEventUninitialize(CallbackEvent);
}

_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(CXPLAT_SEC_CONFIG_CREATE_COMPLETE)
void TcpConfiguration::SecConfigCallback(
_In_ const QUIC_CREDENTIAL_CONFIG* /* CredConfig */,
_In_opt_ void* Context,
_In_ QUIC_STATUS Status,
_In_opt_ CXPLAT_SEC_CONFIG* SecurityConfig
)
{
TcpConfiguration* This = (TcpConfiguration*)Context;
if (QUIC_SUCCEEDED(Status)) {
This->SecConfig = SecurityConfig;
}
CxPlatEventSet(This->CallbackEvent);
}

// ############################# ENGINE #############################

Expand Down Expand Up @@ -327,16 +328,12 @@ bool TcpWorker::QueueConnection(TcpConnection* Connection)

// ############################# SERVER #############################

TcpServer::TcpServer(TcpEngine* Engine, const QUIC_CREDENTIAL_CONFIG* CredConfig, void* Context) :
Initialized(false), Engine(Engine), SecConfig(nullptr), Listener(nullptr), Context(Context)
TcpServer::TcpServer(TcpEngine* Engine, TcpConfiguration* Config, void* Context) :
Initialized(false), Engine(Engine), SecConfig(Config->SecConfig), Listener(nullptr), Context(Context)
{
if (!Engine->IsInitialized()) {
return;
}
LoadSecConfigHelper Helper;
if ((SecConfig = Helper.Load(CredConfig)) == nullptr) {
return;
}
Initialized = true;
}

Expand Down Expand Up @@ -384,9 +381,9 @@ TcpServer::AcceptCallback(

TcpConnection::TcpConnection(
TcpEngine* Engine,
const QUIC_CREDENTIAL_CONFIG* CredConfig,
const TcpConfiguration* Config,
void* Context) :
IsServer(false), Engine(Engine), Context(Context)
IsServer(false), Engine(Engine), SecConfig(Config->SecConfig), Context(Context)
{
CxPlatRefInitialize(&Ref);
CxPlatEventInitialize(&CloseComplete, TRUE, FALSE);
Expand All @@ -399,11 +396,6 @@ TcpConnection::TcpConnection(
if (!Engine->IsInitialized()) {
return;
}
LoadSecConfigHelper Helper;
if ((SecConfig = Helper.Load(CredConfig)) == nullptr) {
WriteOutput("SecConfig load FAILED\n");
return;
}
QuicAddrSetFamily(&Route.LocalAddress, QUIC_ADDRESS_FAMILY_UNSPEC);
Initialized = true;
}
Expand Down Expand Up @@ -494,9 +486,6 @@ TcpConnection::~TcpConnection()

CxPlatSocketDelete(Socket);
}
if (!IsServer && SecConfig) {
CxPlatTlsSecConfigDelete(SecConfig);
}
CXPLAT_DBG_ASSERT(!QueuedOnWorker);
Engine->RemoveConnection(this);
CxPlatEventUninitialize(CloseComplete);
Expand Down Expand Up @@ -762,7 +751,7 @@ bool TcpConnection::ProcessTls(const uint8_t* Buffer, uint32_t BufferLength)
&BufferLength,
&TlsState);
if (Results & CXPLAT_TLS_RESULT_ERROR) {
WriteOutput("CxPlatTlsProcessData FAILED\n");
//WriteOutput("CxPlatTlsProcessData FAILED, Alert=0x%hx\n", TlsState.AlertCode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should delete rather than comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep for now, and might look into more later.

return false;
}

Expand Down
26 changes: 17 additions & 9 deletions src/perf/lib/Tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,23 +135,31 @@ class TcpWorker {
);
};

class TcpServer {
friend class TcpEngine;
bool Initialized;
TcpEngine* Engine;
CXPLAT_SEC_CONFIG* SecConfig;
CXPLAT_SOCKET* Listener;
class TcpConfiguration {
CXPLAT_EVENT CallbackEvent;
static
_IRQL_requires_max_(PASSIVE_LEVEL)
_Function_class_(CXPLAT_SEC_CONFIG_CREATE_COMPLETE)
void
QUIC_API
SecConfigCallback(
_In_ const QUIC_CREDENTIAL_CONFIG* CredConfig,
_In_ const QUIC_CREDENTIAL_CONFIG* /* CredConfig */,
_In_opt_ void* Context,
_In_ QUIC_STATUS Status,
_In_opt_ CXPLAT_SEC_CONFIG* SecurityConfig
);
public:
CXPLAT_SEC_CONFIG* SecConfig{nullptr};
TcpConfiguration(const QUIC_CREDENTIAL_CONFIG* CredConfig) noexcept;
~TcpConfiguration() noexcept;
};

class TcpServer {
friend class TcpEngine;
bool Initialized;
TcpEngine* Engine;
CXPLAT_SEC_CONFIG* SecConfig;
CXPLAT_SOCKET* Listener;
static
_IRQL_requires_max_(DISPATCH_LEVEL)
_Function_class_(CXPLAT_DATAPATH_ACCEPT_CALLBACK)
Expand All @@ -164,7 +172,7 @@ class TcpServer {
);
public:
void* Context; // App context
TcpServer(TcpEngine* Engine, const QUIC_CREDENTIAL_CONFIG* CredConfig, void* Context = nullptr);
TcpServer(TcpEngine* Engine, TcpConfiguration* Config, void* Context = nullptr);
~TcpServer();
bool IsInitialized() const { return Initialized; }
bool Start(const QUIC_ADDR* LocalAddress);
Expand Down Expand Up @@ -274,7 +282,7 @@ class TcpConnection {
void* Context{nullptr}; // App context
TcpConnection(
_In_ TcpEngine* Engine,
_In_ const QUIC_CREDENTIAL_CONFIG* CredConfig,
_In_ const TcpConfiguration* Config,
_In_ void* Context = nullptr);
bool IsInitialized() const { return Initialized; }
void Close();
Expand Down
8 changes: 2 additions & 6 deletions src/platform/datapath_epoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -1740,7 +1740,7 @@ CxPlatSocketHandleErrors(
SocketContext->Binding,
errno,
"getsockopt(SO_ERROR) failed");
} else {
} else if (ErrNum != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guards against a spurious call to the function, right?
Should we add an assert? Or is it expected it could happen?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's possible to get an ErrNum 0, but not positive.

QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Expand All @@ -1763,11 +1763,7 @@ CxPlatSocketHandleErrors(
&SocketContext->Binding->RemoteAddress);
}
}
} else if (ErrNum == ENOTSOCK ||
ErrNum == EINTR ||
ErrNum == ECANCELED ||
ErrNum == ECONNABORTED ||
ErrNum == ECONNRESET) {
} else {
if (!SocketContext->Binding->DisconnectIndicated) {
SocketContext->Binding->DisconnectIndicated = TRUE;
SocketContext->Binding->Datapath->TcpHandlers.Connect(
Expand Down
Loading