diff --git a/src/core/connection.c b/src/core/connection.c
index adb3f66697..d0c9edfd87 100644
--- a/src/core/connection.c
+++ b/src/core/connection.c
@@ -306,6 +306,36 @@ QuicConnAlloc(
     return Status;
 }
 
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicConnCreateOOB(
+    _In_ QUIC_REGISTRATION* Registration,
+    _Inout_ uint32_t* BufferLength,
+    _Out_writes_bytes_opt_(*BufferLength)
+        void* Buffer
+    )
+{
+    UNREFERENCED_PARAMETER(Registration);
+    UNREFERENCED_PARAMETER(BufferLength);
+    UNREFERENCED_PARAMETER(Buffer);
+    return QUIC_STATUS_NOT_SUPPORTED;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicConnSetOOB(
+    _In_ QUIC_CONNECTION* Connection,
+    _In_ uint32_t BufferLength,
+    _In_reads_bytes_(BufferLength)
+        const void* Buffer
+    )
+{
+    UNREFERENCED_PARAMETER(Connection);
+    UNREFERENCED_PARAMETER(BufferLength);
+    UNREFERENCED_PARAMETER(Buffer);
+    return QUIC_STATUS_NOT_SUPPORTED;
+}
+
 _IRQL_requires_max_(DISPATCH_LEVEL)
 void
 QuicConnFree(
@@ -6614,6 +6644,10 @@ QuicConnParamSet(
         return QUIC_STATUS_SUCCESS;
     }
 
+case QUIC_PARAM_CONN_OOB_INFO:
+    Status = QuicConnSetOOB(Connection, BufferLength, Buffer);
+    break;
+
     //
     // Private
     //
diff --git a/src/core/connection.h b/src/core/connection.h
index bfff21972a..dbe4163eba 100644
--- a/src/core/connection.h
+++ b/src/core/connection.h
@@ -991,6 +991,20 @@ QuicConnAlloc(
         QUIC_CONNECTION** NewConnection
     );
 
+//
+// Creates a new (server side) out-of-band connection and writes the info to the
+// output buffer for the peer (client) may use it to create an outgoing
+// connection.
+//
+_IRQL_requires_max_(PASSIVE_LEVEL)
+QUIC_STATUS
+QuicConnCreateOOB(
+    _In_ QUIC_REGISTRATION* Registration,
+    _Inout_ uint32_t* BufferLength,
+    _Out_writes_bytes_opt_(*BufferLength)
+        void* Buffer
+    );
+
 //
 // Called to free the memory for a connection.
 //
diff --git a/src/core/listener.c b/src/core/listener.c
index 8466f8302f..8b166990ac 100644
--- a/src/core/listener.c
+++ b/src/core/listener.c
@@ -855,6 +855,11 @@ QuicListenerParamGet(
         Status = QUIC_STATUS_SUCCESS;
         break;
 
+    case QUIC_PARAM_LISTENER_OOB_CONNECTION:
+        Status =
+            QuicConnCreateOOB(Listener->Registration, BufferLength, Buffer);
+        break;
+
     default:
         Status = QUIC_STATUS_INVALID_PARAMETER;
         break;
diff --git a/src/inc/msquic.h b/src/inc/msquic.h
index d9c25b8933..366b7d8d82 100644
--- a/src/inc/msquic.h
+++ b/src/inc/msquic.h
@@ -884,6 +884,7 @@ typedef struct QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W {
 #define QUIC_PARAM_LISTENER_STATS                       0x04000001  // QUIC_LISTENER_STATISTICS
 #ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
 #define QUIC_PARAM_LISTENER_CIBIR_ID                    0x04000002  // uint8_t[] {offset, id[]}
+#define QUIC_PARAM_LISTENER_OOB_CONNECTION              0x04000003  // uint8_t[]
 #endif
 
 //
@@ -918,6 +919,9 @@ 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_OOB_INFO                        0x05000019  // uint8_t[]
+#endif
 
 //
 // Parameters for TLS.
diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h
index f75e1a8aa7..e5c5e402dc 100644
--- a/src/test/MsQuicTests.h
+++ b/src/test/MsQuicTests.h
@@ -284,6 +284,13 @@ QuicTestShutdownDuringHandshake(
     _In_ bool ClientShutdown
     );
 
+#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
+void
+QuicTestOOBHandshake(
+    _In_ int Family
+    );
+#endif
+
 //
 // Negative Handshake Tests
 //
@@ -1330,4 +1337,8 @@ typedef struct {
     QUIC_CTL_CODE(125, METHOD_BUFFERED, FILE_WRITE_DATA)
     // BOOLEAN - EnableResumption
 
-#define QUIC_MAX_IOCTL_FUNC_CODE 125
+#define IOCTL_QUIC_RUN_OOB_HANDSHAKE \
+    QUIC_CTL_CODE(126, METHOD_BUFFERED, FILE_WRITE_DATA)
+    // int - Family
+
+#define QUIC_MAX_IOCTL_FUNC_CODE 126
diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp
index c717d79bb2..b70531a384 100644
--- a/src/test/bin/quic_gtest.cpp
+++ b/src/test/bin/quic_gtest.cpp
@@ -1496,6 +1496,17 @@ TEST_P(WithHandshakeArgs4, RandomLossResumeRejection) {
 #endif // QUIC_DISABLE_RESUMPTION
 #endif // QUIC_TEST_DATAPATH_HOOKS_ENABLED
 
+#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES
+TEST_P(WithFamilyArgs, OOBHandshake) {
+    TestLoggerT<ParamType> Logger("QuicTestOOBHandshake", GetParam());
+    if (TestingKernelMode) {
+        ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_OOB_HANDSHAKE, GetParam().Family));
+    } else {
+        QuicTestOOBHandshake(GetParam().Family);
+    }
+}
+#endif
+
 TEST_P(WithFamilyArgs, Unreachable) {
     if (GetParam().Family == 4 && IsWindows2019()) GTEST_SKIP(); // IPv4 unreachable doesn't work on 2019
     TestLoggerT<ParamType> Logger("QuicTestConnectUnreachable", GetParam());
diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp
index 839457729c..b31ba8d8bc 100644
--- a/src/test/bin/winkernel/control.cpp
+++ b/src/test/bin/winkernel/control.cpp
@@ -524,6 +524,7 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] =
     0,
     0,
     sizeof(BOOLEAN),
+    sizeof(INT32),
 };
 
 CXPLAT_STATIC_ASSERT(
@@ -1483,6 +1484,11 @@ QuicTestCtlEvtIoDeviceControl(
         QuicTestCtlRun(QuicTestTlsHandshakeInfo(Params->EnableResumption != 0));
         break;
 
+    case IOCTL_QUIC_RUN_OOB_HANDSHAKE:
+        CXPLAT_FRE_ASSERT(Params != nullptr);
+        QuicTestCtlRun(QuicTestOOBHandshake(Params->Family));
+        break;
+
     default:
         Status = STATUS_NOT_IMPLEMENTED;
         break;
diff --git a/src/test/lib/HandshakeTest.cpp b/src/test/lib/HandshakeTest.cpp
index 71cddd74c5..e596f03d33 100644
--- a/src/test/lib/HandshakeTest.cpp
+++ b/src/test/lib/HandshakeTest.cpp
@@ -4023,3 +4023,41 @@ QuicTestHandshakeSpecificLossPatterns(
         Listener.LastConnection->Shutdown(0, QUIC_CONNECTION_SHUTDOWN_FLAG_SILENT);
     }
 }
+
+void
+QuicTestOOBHandshake(
+    _In_ int Family
+    )
+{
+    MsQuicRegistration Registration;
+    TEST_QUIC_SUCCEEDED(Registration.GetInitStatus());
+
+    MsQuicSettings Settings;
+    Settings.SetIdleTimeoutMs(60000)
+        .SetDisconnectTimeoutMs(60000)
+        .SetInitialRttMs(20);
+
+    MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", Settings, ServerSelfSignedCredConfig);
+    TEST_QUIC_SUCCEEDED(ServerConfiguration.GetInitStatus());
+
+    MsQuicConfiguration ClientConfiguration(Registration, "MsQuicTest", Settings, MsQuicCredentialConfig());
+    TEST_QUIC_SUCCEEDED(ClientConfiguration.GetInitStatus());
+
+    QuicAddr ServerLocalAddr((Family == 4) ? QUIC_ADDRESS_FAMILY_INET : QUIC_ADDRESS_FAMILY_INET6);
+    MsQuicAutoAcceptListener Listener(Registration, ServerConfiguration, MsQuicConnection::NoOpCallback);
+    TEST_QUIC_SUCCEEDED(Listener.Start("MsQuicTest", &ServerLocalAddr.SockAddr));
+    TEST_QUIC_SUCCEEDED(Listener.GetInitStatus());
+    TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr));
+
+    uint32_t OOBLength = 0;
+    TEST_QUIC_SUCCEEDED(Listener.GetParam(QUIC_PARAM_LISTENER_OOB_CONNECTION, &OOBLength, nullptr));
+    UniquePtr<uint8_t[]> OOBData(new(std::nothrow) uint8_t[OOBLength]);
+    TEST_QUIC_SUCCEEDED(Listener.GetParam(QUIC_PARAM_LISTENER_OOB_CONNECTION, &OOBLength, OOBData.get()));
+
+    MsQuicConnection Connection(Registration);
+    TEST_QUIC_SUCCEEDED(Connection.GetInitStatus());
+    TEST_QUIC_SUCCEEDED(Connection.SetParam(QUIC_PARAM_CONN_OOB_INFO, OOBLength, OOBData.get()));
+    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(Connection.HandshakeComplete);
+}