Skip to content
Closed
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
28 changes: 28 additions & 0 deletions src/platform/datapath_winkernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,34 @@ SocketCreateUdp(
}
}

if (Config->CibirIdLength > 0 &&
Config->LocalAddress &&
Config->LocalAddress->Ipv4.sin_port != 0) {
//
// When CIBIR is in use, multiple listeners may share the same
// port (distinguished by CIBIR ID). Use SO_REUSEADDR instead
// of exclusive binding to allow port sharing.
//
Option = TRUE;
Status =
CxPlatDataPathSetControlSocket(
Binding,
WskSetOption,
SO_REUSEADDR,
SOL_SOCKET,
sizeof(Option),
&Option);
if (QUIC_FAILED(Status)) {
QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Binding,
Status,
"Set SO_REUSEADDR");
goto Error;
}
}

IoReuseIrp(&Binding->Irp, STATUS_SUCCESS);
IoSetCompletionRoutine(
&Binding->Irp,
Expand Down
29 changes: 28 additions & 1 deletion src/platform/datapath_winuser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1694,7 +1694,34 @@ SocketCreateUdp(
}
}

if (Datapath->Features & CXPLAT_DATAPATH_FEATURE_PORT_RESERVATIONS &&
if (Config->CibirIdLength > 0 &&
Config->LocalAddress &&
Config->LocalAddress->Ipv4.sin_port != 0) {
//
// When CIBIR is in use, multiple listeners may share the same
// port (distinguished by CIBIR ID). Use SO_REUSEADDR instead
// of exclusive port reservations to allow port sharing.
//
Option = TRUE;
Result =
setsockopt(
SocketProc->Socket,
SOL_SOCKET,
SO_REUSEADDR,
(char*)&Option,
sizeof(Option));
if (Result == SOCKET_ERROR) {
int WsaError = WSAGetLastError();
QuicTraceEvent(
DatapathErrorStatus,
"[data][%p] ERROR, %u, %s.",
Socket,
WsaError,
"Set SO_REUSEADDR");
Status = HRESULT_FROM_WIN32(WsaError);
goto Error;
}
} else if (Datapath->Features & CXPLAT_DATAPATH_FEATURE_PORT_RESERVATIONS &&
Config->LocalAddress &&
Config->LocalAddress->Ipv4.sin_port != 0) {
if (i == 0) {
Expand Down
11 changes: 10 additions & 1 deletion src/test/MsQuicTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,11 @@ void
QuicTestCibirExtension(
const CibirExtensionParams& Params
);

void
QuicTestCibirSharedPortListeners(
const FamilyArgs& Params
);
#endif

void
Expand Down Expand Up @@ -1488,6 +1493,10 @@ typedef struct {
QUIC_CTL_CODE(85, METHOD_BUFFERED, FILE_WRITE_DATA)
// QUIC_RUN_CIBIR_EXTENSION

#define IOCTL_QUIC_RUN_CIBIR_SHARED_PORT_LISTENERS \
QUIC_CTL_CODE(139, METHOD_BUFFERED, FILE_WRITE_DATA)
// int - Family

#define IOCTL_QUIC_RUN_STREAM_PRIORITY_INFINITE_LOOP \
QUIC_CTL_CODE(86, METHOD_BUFFERED, FILE_WRITE_DATA)

Expand Down Expand Up @@ -1689,7 +1698,7 @@ struct QUIC_RUN_CONNECTION_POOL_CREATE_PARAMS {
QUIC_CTL_CODE(138, METHOD_BUFFERED, FILE_WRITE_DATA)
// int - Family

#define QUIC_MAX_IOCTL_FUNC_CODE 138
#define QUIC_MAX_IOCTL_FUNC_CODE 139

// Generic IOCTL for invoking functions

Expand Down
9 changes: 9 additions & 0 deletions src/test/bin/quic_gtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,15 @@ INSTANTIATE_TEST_SUITE_P(
WithCibirExtensionParams,
testing::ValuesIn(WithCibirExtensionParams::Generate()));

TEST_P(WithFamilyArgs, CibirSharedPortListeners) {
TestLoggerT<ParamType> Logger("QuicTestCibirSharedPortListeners", GetParam());
if (TestingKernelMode) {
ASSERT_TRUE(InvokeKernelTest(FUNC(QuicTestCibirSharedPortListeners), GetParam()));
} else {
QuicTestCibirSharedPortListeners(GetParam());
}
}

#endif

#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
Expand Down
2 changes: 2 additions & 0 deletions src/test/bin/winkernel/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] =
0,
0,
sizeof(INT32),
sizeof(INT32), // IOCTL_QUIC_RUN_CIBIR_SHARED_PORT_LISTENERS
};

CXPLAT_STATIC_ASSERT(
Expand Down Expand Up @@ -736,6 +737,7 @@ ExecuteTestRequest(
RegisterTestFunction(QuicTestCustomClientCertificateValidation);
RegisterTestFunction(QuicTestConnectClientCertificate);
RegisterTestFunction(QuicTestCibirExtension);
RegisterTestFunction(QuicTestCibirSharedPortListeners);
#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
#if QUIC_TEST_DISABLE_VNE_TP_GENERATION
RegisterTestFunction(QuicTestVNTPOddSize);
Expand Down
64 changes: 64 additions & 0 deletions src/test/lib/HandshakeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3935,6 +3935,70 @@ QuicTestCibirExtension(
TEST_EQUAL(Connection.HandshakeComplete, ShouldConnnect);
}

void
QuicTestCibirSharedPortListeners(
const FamilyArgs& Params
)
{
const int Family = Params.Family;

//
// Two different CIBIR IDs to distinguish multiple listeners on the
// same port.
//
const uint8_t CibirId1[] = { 0 /* offset */, 4, 3, 2, 1 };
const uint8_t CibirId2[] = { 0 /* offset */, 5, 6, 7, 8 };
const uint8_t CibirIdLength = sizeof(CibirId1);

MsQuicRegistration Registration(true);
TEST_QUIC_SUCCEEDED(Registration.GetInitStatus());

MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", ServerSelfSignedCredConfig);
TEST_QUIC_SUCCEEDED(ServerConfiguration.GetInitStatus());

MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", MsQuicCredentialConfig());
TEST_QUIC_SUCCEEDED(ClientConfiguration.GetInitStatus());

QUIC_ADDRESS_FAMILY QuicAddrFamily = (Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6;

//
// Start the first CIBIR-enabled listener and let it pick a port.
//
QuicAddr ServerLocalAddr(QuicAddrFamily);
MsQuicAutoAcceptListener Listener1(Registration, ServerConfiguration, MsQuicConnection::NoOpCallback);
TEST_QUIC_SUCCEEDED(Listener1.SetCibirId(CibirId1, CibirIdLength));
TEST_QUIC_SUCCEEDED(Listener1.Start("MsQuicTest", &ServerLocalAddr.SockAddr));
TEST_QUIC_SUCCEEDED(Listener1.GetInitStatus());
TEST_QUIC_SUCCEEDED(Listener1.GetLocalAddr(ServerLocalAddr));

//
// Start the second CIBIR-enabled listener on the SAME port.
// This exercises the SO_REUSEADDR path added for CIBIR port sharing.
//
QuicAddr ServerLocalAddr2(QuicAddrFamily);
ServerLocalAddr2.SetPort(ServerLocalAddr.GetPort());
MsQuicAutoAcceptListener Listener2(Registration, ServerConfiguration, MsQuicConnection::NoOpCallback);
TEST_QUIC_SUCCEEDED(Listener2.SetCibirId(CibirId2, CibirIdLength));
TEST_QUIC_SUCCEEDED(Listener2.Start("MsQuicTest", &ServerLocalAddr2.SockAddr));
TEST_QUIC_SUCCEEDED(Listener2.GetInitStatus());

//
// Verify a client with the first CIBIR ID can connect via the shared port.
//
MsQuicConnection Connection(Registration);
TEST_QUIC_SUCCEEDED(Connection.GetInitStatus());
TEST_QUIC_SUCCEEDED(Connection.SetShareUdpBinding());
TEST_QUIC_SUCCEEDED(Connection.SetCibirId(CibirId1, CibirIdLength));
TEST_QUIC_SUCCEEDED(
Connection.Start(
ClientConfiguration,
ServerLocalAddr.GetFamily(),
QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()),
ServerLocalAddr.GetPort()));
Connection.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout);
TEST_TRUE(Connection.HandshakeComplete);
}

#endif // QUIC_API_ENABLE_PREVIEW_FEATURES

void
Expand Down
Loading