From 312be3cc9f25465d4368ad1153fe7598caa32a98 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 21 Feb 2023 22:13:54 +0800 Subject: [PATCH] Implementing QUIC support on the server side, ref ryzom/ryzomcore#628 --- nel/include/nel/misc/types_nl.h | 2 + .../src/frontend_service/fe_receive_sub.cpp | 68 ++- .../src/frontend_service/fe_receive_sub.h | 21 +- .../src/frontend_service/fe_receive_task.cpp | 9 +- .../src/frontend_service/fe_receive_task.h | 2 +- .../src/frontend_service/quic_transceiver.cpp | 447 ++++++++++++++++++ .../src/frontend_service/quic_transceiver.h | 126 +++++ 7 files changed, 636 insertions(+), 39 deletions(-) create mode 100644 ryzom/server/src/frontend_service/quic_transceiver.cpp create mode 100644 ryzom/server/src/frontend_service/quic_transceiver.h diff --git a/nel/include/nel/misc/types_nl.h b/nel/include/nel/misc/types_nl.h index 850654b2ba..6e0dfec890 100644 --- a/nel/include/nel/misc/types_nl.h +++ b/nel/include/nel/misc/types_nl.h @@ -189,6 +189,8 @@ # define NL_CPP11 #endif +#define null nullptr + #ifdef NL_CPP14 #define NL_ALIGNLIKE(type) alignas(alignof(type)) #else diff --git a/ryzom/server/src/frontend_service/fe_receive_sub.cpp b/ryzom/server/src/frontend_service/fe_receive_sub.cpp index bd6fd4665d..4dee96f6b1 100644 --- a/ryzom/server/src/frontend_service/fe_receive_sub.cpp +++ b/ryzom/server/src/frontend_service/fe_receive_sub.cpp @@ -46,6 +46,7 @@ #include "uid_impulsions.h" #include "id_impulsions.h" +#include "quic_transceiver.h" #include "game_share/ryzom_entity_id.h" #include "game_share/system_message.h" @@ -124,15 +125,22 @@ void CFeReceiveSub::init( uint16 firstAcceptableFrontendPort, uint16 lastAccepta nlassert( (_ReceiveTask==NULL) && (_ReceiveThread==NULL) ); // Start external datagram socket - nlinfo( "FERECV: Starting external datagram socket" ); - _ReceiveTask = new CFEReceiveTask( firstAcceptableFrontendPort, lastAcceptableFrontendPort, dgrammaxlength ); - _CurrentReadQueue = &_Queue2; - _ReceiveTask->setWriteQueue( &_Queue1 ); - nlassert( _ReceiveTask != NULL ); - _ReceiveThread = IThread::create( _ReceiveTask ); - nlassert( _ReceiveThread != NULL ); + nlinfo("FERECV: Starting external datagram socket"); + _ReceiveTask = new CFEReceiveTask(firstAcceptableFrontendPort, lastAcceptableFrontendPort, dgrammaxlength); + nlassert(_ReceiveTask != NULL); + m_CurrentReadQueue = &m_Queues[1]; + _ReceiveTask->swapWriteQueue(&m_Queues[0]); + _ReceiveThread = IThread::create(_ReceiveTask); + nlassert(_ReceiveThread != NULL); _ReceiveThread->start(); + // Start QUIC transceiver + nlinfo("FERECV: Starting QUIC transceiver"); + m_QuicTransceiver = new CQuicTransceiver(firstAcceptableFrontendPort - 5000, lastAcceptableFrontendPort - 5000, dgrammaxlength); + m_CurrentQuicReadQueue = &m_Queues[3]; + m_QuicTransceiver->swapWriteQueue(&m_Queues[2]); + m_QuicTransceiver->start(); + // Setup current message placeholder _CurrentInMsg = new TReceivedMessage(); @@ -237,14 +245,32 @@ void CFeReceiveSub::readIncomingData() } // Read queue of messages received from clients - while ( ! _CurrentReadQueue->empty() ) + _CurrentInMsg->AddrFrom.setNull(); // FIXME: QUIC + while (!m_CurrentQuicReadQueue->empty()) { - //nlinfo( "Read queue size = %u", _CurrentReadQueue->size() ); - _CurrentReadQueue->front( _CurrentInMsg->data() ); - _CurrentReadQueue->pop(); - nlassert( ! _CurrentReadQueue->empty() ); - _CurrentReadQueue->front( _CurrentInMsg->VAddrFrom ); - _CurrentReadQueue->pop(); + // nlinfo( "Read queue size = %u", _CurrentReadQueue->size() ); + m_CurrentQuicReadQueue->front(_CurrentInMsg->data()); + m_CurrentQuicReadQueue->pop(); + nlassert(!m_CurrentQuicReadQueue->empty()); + m_CurrentQuicReadQueue->front(_CurrentInMsg->VAddrFrom); + m_CurrentQuicReadQueue->pop(); + // _CurrentInMsg->vectorToAddress(); // FIXME: QUIC + +#ifndef MEASURE_RECEIVE_TASK + handleIncomingMsg(); +#endif + } + + // _CurrentInMsg->AddrFrom.setNull(); // FIXME: QUIC + // Read queue of messages received from clients + while (!m_CurrentReadQueue->empty()) + { + // nlinfo( "Read queue size = %u", m_CurrentReadQueue->size() ); + m_CurrentReadQueue->front(_CurrentInMsg->data()); + m_CurrentReadQueue->pop(); + nlassert(!m_CurrentReadQueue->empty()); + m_CurrentReadQueue->front(_CurrentInMsg->VAddrFrom); + m_CurrentReadQueue->pop(); _CurrentInMsg->vectorToAddress(); #ifndef MEASURE_RECEIVE_TASK @@ -299,18 +325,8 @@ void CFeReceiveSub::readIncomingData() */ void CFeReceiveSub::swapReadQueues() { - if ( _CurrentReadQueue == &_Queue1 ) - { - _CurrentReadQueue = &_Queue2; - _ReceiveTask->setWriteQueue( &_Queue1 ); - //nlinfo( "** Write1 Read2 ** Read=%p Write=%p", &_Queue2, &_Queue1 ); - } - else - { - _CurrentReadQueue = &_Queue1; - _ReceiveTask->setWriteQueue( &_Queue2 ); - //nlinfo( "** Read1 Write2 ** Read=%p Write=%p", &_Queue1, &_Queue2 ); - } + m_CurrentReadQueue = _ReceiveTask->swapWriteQueue(m_CurrentReadQueue); + m_CurrentQuicReadQueue = m_QuicTransceiver->swapWriteQueue(m_CurrentQuicReadQueue); } diff --git a/ryzom/server/src/frontend_service/fe_receive_sub.h b/ryzom/server/src/frontend_service/fe_receive_sub.h index 2dd2b9d3af..600705febd 100644 --- a/ryzom/server/src/frontend_service/fe_receive_sub.h +++ b/ryzom/server/src/frontend_service/fe_receive_sub.h @@ -47,6 +47,7 @@ class CHistory; class CVision; class CVisionData; +class CQuicTransceiver; /// Type of remove list typedef std::list< std::pair > TClientsToRemove; @@ -94,11 +95,11 @@ class CFeReceiveSub EntityToClient(), _ReceiveTask(NULL), _ReceiveThread(NULL), + m_QuicTransceiver(NULL), _ClientMap(), _ClientIdCont(NULL), - _Queue1(), - _Queue2(), - _CurrentReadQueue(NULL), + m_CurrentReadQueue(NULL), + m_CurrentQuicReadQueue(NULL), _CurrentInMsg(), _RcvCounter(0), _RcvBytes(0), @@ -214,6 +215,9 @@ class CFeReceiveSub /// Receive thread NLMISC::IThread *_ReceiveThread; + + /// QUIC receiver and sender + CQuicTransceiver *m_QuicTransceiver; /// Client map by address THostMap _ClientMap; @@ -221,14 +225,13 @@ class CFeReceiveSub /// Client map by id (belonging to the send subsystem) TClientIdCont *_ClientIdCont; - /// First queue - NLMISC::CBufFIFO _Queue1; - - /// Second queue - NLMISC::CBufFIFO _Queue2; + /// Queues + NLMISC::CBufFIFO m_Queues[4]; /// Current read queue - NLMISC::CBufFIFO *_CurrentReadQueue; + NLMISC::CBufFIFO *m_CurrentReadQueue; + + NLMISC::CBufFIFO *m_CurrentQuicReadQueue; /// Current incoming message TReceivedMessage *_CurrentInMsg; diff --git a/ryzom/server/src/frontend_service/fe_receive_task.cpp b/ryzom/server/src/frontend_service/fe_receive_task.cpp index 9f6e9abec3..d6e0ae9e2d 100644 --- a/ryzom/server/src/frontend_service/fe_receive_task.cpp +++ b/ryzom/server/src/frontend_service/fe_receive_task.cpp @@ -205,9 +205,12 @@ void CFEReceiveTask::run() /* * Set new write queue */ -void CFEReceiveTask::setWriteQueue( CBufFIFO *writequeue ) +CBufFIFO *CFEReceiveTask::swapWriteQueue(CBufFIFO *writeQueue) { - CSynchronized::CAccessor wq( &_WriteQueue ); - wq.value() = writequeue; + CSynchronized::CAccessor wq(&_WriteQueue); + CBufFIFO *previous = wq.value(); + wq.value() = writeQueue; + return previous; } +/* end of file */ diff --git a/ryzom/server/src/frontend_service/fe_receive_task.h b/ryzom/server/src/frontend_service/fe_receive_task.h index 42af3f9594..837243d5e8 100644 --- a/ryzom/server/src/frontend_service/fe_receive_task.h +++ b/ryzom/server/src/frontend_service/fe_receive_task.h @@ -108,7 +108,7 @@ class CFEReceiveTask : public NLMISC::IRunnable virtual void run(); /// Set new write queue (thread-safe because mutexed) - void setWriteQueue( NLMISC::CBufFIFO *writequeue ); + CBufFIFO *swapWriteQueue(NLMISC::CBufFIFO *writeQueue); /// Require exit (thread-safe because atomic assignment) void requireExit() { _ExitRequired = true; } diff --git a/ryzom/server/src/frontend_service/quic_transceiver.cpp b/ryzom/server/src/frontend_service/quic_transceiver.cpp new file mode 100644 index 0000000000..45d0d98230 --- /dev/null +++ b/ryzom/server/src/frontend_service/quic_transceiver.cpp @@ -0,0 +1,447 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This source file has been modified by the following contributors: +// Copyright (C) 2014 Jan BOON (Kaetemi) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdpch.h" +#include "quic_transceiver.h" + +#include "nel/misc/mutex.h" +#include "nel/misc/buf_fifo.h" +#include "nel/misc/string_view.h" + +#include "config.h" + +#ifdef NL_MSQUIC_AVAILABLE +#include + +#define MsQuic m->Api + +using namespace NLMISC; +using namespace NLNET; + +// This really hammers fast +class CAtomicFlagLock +{ +public: + CAtomicFlagLock(std::atomic_flag &flag) + : m_Flag(flag) + { + while (m_Flag.test_and_set(std::memory_order_acquire)) + ; + } + + ~CAtomicFlagLock() + { + m_Flag.clear(std::memory_order_release); + } + +private: + std::atomic_flag &m_Flag; +}; + +// This is a bit more relaxed +class CAtomicFlagLockYield +{ +public: + CAtomicFlagLockYield(std::atomic_flag &flag) + : m_Flag(flag) + { + while (m_Flag.test_and_set(std::memory_order_acquire)) + std::this_thread::yield(); + } + + ~CAtomicFlagLockYield() + { + m_Flag.clear(std::memory_order_release); + } + +private: + std::atomic_flag &m_Flag; +}; + +class CQuicTransceiverImpl +{ +public: + const QUIC_API_TABLE *Api = null; + HQUIC Registration = null; + HQUIC Configuration = null; + HQUIC Listener = null; + + std::atomic_flag BufferMutex = ATOMIC_FLAG_INIT; + NLMISC::CBufFIFO *Buffer = null; + + std::atomic Listening; + + // Some salt for generating the token address + uint64 SaltA0 = (((uint64)std::random_device()()) << 32) | std::random_device()(); + uint64 SaltA1 = (((uint64)std::random_device()()) << 32) | std::random_device()(); + uint32 SaltB0 = std::random_device()(); + uint32 SaltB1 = std::random_device()(); + std::atomic AddrA; + std::atomic AddrB; + + // IPv6 unique local address range to use as a token address for each connection + CInetAddress TokenSubnet = CInetAddress("[fdd5:d66b:8698::]:0"); + + static QUIC_STATUS listenerCallback(HQUIC listener, void *context, QUIC_LISTENER_EVENT *ev); + static QUIC_STATUS connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *ev); +}; + +CQuicTransceiver::CQuicTransceiver(uint32 msgsize) + : m(std::make_unique()) + , m_MsgSize(msgsize) +{ + // Open library + QUIC_STATUS status = MsQuicOpenVersion(QUIC_API_VERSION_2, (const void **)&MsQuic); + if (QUIC_FAILED(status)) + { + nlwarning("MsQuicOpenVersion failed with status 0x%x", status); + return; + } + + // Registration, this creates the worker threads + QUIC_REGISTRATION_CONFIG regConfig = { 0 }; + regConfig.AppName = "Ryzom Core (FES)"; + regConfig.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; + status = MsQuic->RegistrationOpen(®Config, &m->Registration); + if (QUIC_FAILED(status)) + { + nlwarning("MsQuic->RegistrationOpen failed with status 0x%x", status); + release(); + return; + } +} + +CQuicTransceiver::~CQuicTransceiver() +{ + stop(); + release(); +} + +void CQuicTransceiver::start(uint16 port) +{ + stop(); + + if (!MsQuic) + { + nlwarning("QUIC API not available"); + } + + static const char *protocolName = "ryzomcore4"; + static const QUIC_BUFFER alpn = { sizeof(protocolName) - 1, (uint8_t *)protocolName }; + + // Configuration, initialized in start, but destroyed on release only (may attempt more than once) + QUIC_STATUS status = QUIC_STATUS_SUCCESS; + if (!m->Configuration) + { + QUIC_SETTINGS settings = { 0 }; + settings.DatagramReceiveEnabled = TRUE; + settings.IsSet.DatagramReceiveEnabled = TRUE; + settings.MigrationEnabled = TRUE; + settings.IsSet.MigrationEnabled = TRUE; + settings.PeerBidiStreamCount = 0; + settings.IsSet.PeerBidiStreamCount = TRUE; + settings.PeerUnidiStreamCount = 0; // TODO: Configured from msg.xml + settings.IsSet.PeerUnidiStreamCount = TRUE; + // settings.SendBufferingEnabled = TRUE; + // settings.IsSet.SendBufferingEnabled = TRUE; + // settings.GreaseQuicBitEnabled = TRUE; + // settings.IsSet.GreaseQuicBitEnabled = TRUE; + status = MsQuic->ConfigurationOpen(m->Registration, &alpn, 1, &settings, sizeof(settings), NULL, &m->Configuration); + if (QUIC_FAILED(status)) + { + nlwarning("MsQuic->ConfigurationOpen failed with status 0x%x", status); + return; + } + + // Server credentials + QUIC_CREDENTIAL_CONFIG credConfig; + memset(&credConfig, 0, sizeof(credConfig)); + credConfig.Flags = QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION; + credConfig.Type = QUIC_CREDENTIAL_TYPE_NONE; // FIXME: Supposedly doesn't work on server + status = MsQuic->ConfigurationLoadCredential(m->Configuration, &credConfig); + } + + // Open listener listening using MSQUIC on e.g. [::]:5000 (port) + QUIC_ADDR addr = { 0 }; + QuicAddrSetFamily(&addr, QUIC_ADDRESS_FAMILY_UNSPEC); + QuicAddrSetPort(&addr, port); + status = MsQuic->ListenerOpen(m->Registration, CQuicTransceiverImpl::listenerCallback, this, &m->Listener); + if (QUIC_FAILED(status)) + { + stop(); + nlwarning("MsQuic->ListenerOpen failed with status 0x%x", status); + return; + } + + // Start listening + status = MsQuic->ListenerStart(m->Listener, &alpn, 1, &addr); + if (QUIC_FAILED(status)) + { + stop(); + nlwarning("MsQuic->ListenerStart failed with status 0x%x", status); + return; + } + + // Ok + m->Listening.store(true, std::memory_order_release); +} + +void CQuicTransceiver::stop() +{ + // Stop listening + if (m->Listener) + { + if (m->Listening) + { + MsQuic->ListenerStop(m->Listener); + } + MsQuic->ListenerClose(m->Listener); + m->Listener = null; + } +} + +void CQuicTransceiver::release() +{ + // Close configuration + if (m->Configuration) + { + MsQuic->ConfigurationClose(m->Configuration); + m->Configuration = null; + } + + // Close registration + if (m->Registration) + { + MsQuic->RegistrationClose(m->Registration); + m->Registration = null; + } + + // Close library + if (MsQuic) + { + MsQuicClose(MsQuic); + MsQuic = null; + } +} + +QUIC_STATUS CQuicTransceiverImpl::listenerCallback(HQUIC listener, void *context, QUIC_LISTENER_EVENT *ev) +{ + CQuicTransceiver *self = (CQuicTransceiver *)context; + CQuicTransceiverImpl *m = self->m.get(); + QUIC_STATUS status = QUIC_STATUS_NOT_SUPPORTED; + switch (ev->Type) + { + case QUIC_LISTENER_EVENT_NEW_CONNECTION: { + // Create user context + CQuicUserContext *user = new CQuicUserContext(); + user->Transceiver = self; + user->TokenAddr = self->generateTokenAddr(); + TReceivedMessage msg; + msg.AddrFrom = user->TokenAddr; + msg.addressToVector(); + user->VTokenAddr = msg.VAddrFrom; + // They're in. + MsQuic->SetCallbackHandler(ev->NEW_CONNECTION.Connection, CQuicTransceiverImpl::connectionCallback, (void *)user); + status = MsQuic->ConnectionSetConfiguration(ev->NEW_CONNECTION.Connection, m->Configuration); + nlwarning("New QUIC connection"); + break; + } + case QUIC_LISTENER_EVENT_STOP_COMPLETE: { + // TODO: Set flag and attempt restart from the service + nlwarning("QUIC listener stopped"); + status = QUIC_STATUS_SUCCESS; + break; + } + default: { + nlwarning("Unknown event type %d", ev->Type); + break; + } + } + return status; +} + +QUIC_STATUS CQuicTransceiverImpl::connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *ev) +{ + CQuicUserContext *user = (CQuicUserContext *)context; + CQuicTransceiver *self = user->Transceiver; + CQuicTransceiverImpl *m = self->m.get(); + QUIC_STATUS status = QUIC_STATUS_NOT_SUPPORTED; + switch (ev->Type) + { + case QUIC_CONNECTION_EVENT_CONNECTED: + nlinfo("Connected"); + nlassert(CStringView((const char *)ev->CONNECTED.NegotiatedAlpn, ev->CONNECTED.NegotiatedAlpnLength) == "ryzomcore4"); + MsQuic->ConnectionSendResumptionTicket(connection, QUIC_SEND_RESUMPTION_FLAG_NONE, 0, NULL); // What does this even do? + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: + nlinfo("Shutdown initiated by transport"); + status = QUIC_STATUS_SUCCESS; + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + nlinfo("Shutdown initiated by peer"); + status = QUIC_STATUS_SUCCESS; + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: + nlinfo("Shutdown complete"); + nlassert(!ev->SHUTDOWN_COMPLETE.AppCloseInProgress); // Only applicable on client, but assert to be sure + if (!ev->SHUTDOWN_COMPLETE.AppCloseInProgress) + { + MsQuic->ConnectionClose(connection); + } + // TODO: Report to the boss + delete user; + status = QUIC_STATUS_SUCCESS; + break; + case QUIC_CONNECTION_EVENT_DATAGRAM_RECEIVED: + nlinfo("Datagram received"); + // YES PLEASE + self->datagramReceived(user, ev->DATAGRAM_RECEIVED.Buffer->Buffer, ev->DATAGRAM_RECEIVED.Buffer->Length); + break; + case QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED: + nlinfo("Datagram state changed"); + // ev->DATAGRAM_STATE_CHANGED.SendEnabled + // ev->DATAGRAM_STATE_CHANGED.MaxSendLength + break; + case QUIC_CONNECTION_EVENT_LOCAL_ADDRESS_CHANGED: + case QUIC_CONNECTION_EVENT_PEER_ADDRESS_CHANGED: + case QUIC_CONNECTION_EVENT_IDEAL_PROCESSOR_CHANGED: + case QUIC_CONNECTION_EVENT_DATAGRAM_SEND_STATE_CHANGED: + case QUIC_CONNECTION_EVENT_RESUMED: + case QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED: + case QUIC_CONNECTION_EVENT_STREAMS_AVAILABLE: // TODO: Match with msg.xml + // Don't care + status = QUIC_STATUS_SUCCESS; + break; + case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED: + case QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS: + case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED: + // Not supported + break; + } + return status; +} + +CInetAddress CQuicTransceiver::generateTokenAddr() +{ + // TODO: We could wrap a pointer and refcount CQuicUserContext somehow + // But it'll need to be correctly released from the receive sub + + // That way we can entirely bypass stupid lookup tables and get the real user context + // As well as get the connection context when sending + + // Use the same address trick to signal that it's a QUIC connection and not a real address + + uint64 addrA = m->AddrA++; + uint32 addrB = m->AddrB++; + addrA = NLMISC::wangHash64(addrA ^ m->SaltA0) ^ m->SaltA1; + addrB = NLMISC::wangHash(addrB ^ m->SaltB0) ^ m->SaltB1; + sockaddr_in6 sa; + m->TokenSubnet.toSockAddrInet6(&sa); + // Keep the first 6 bytes of sa.sin6_addr.s6_addr + // Then 2 bytes from addrB + // Then 8 bytes from addrA + memcpy(&sa.sin6_addr.s6_addr[6], &addrB, 2); + memcpy(&sa.sin6_addr.s6_addr[8], &addrA, 8); + // And the remaining 2 bytes from addrB are to set the port + sa.sin6_port = htons(((uint16 *)&addrB)[1]); + CInetAddress res(false); + res.fromSockAddrInet6(&sa); + return res; + + // This make a copy of the shared ptr into a memory block + //uint8 alignas(std::shared_ptr) memory[sizeof(std::shared_ptr)]; + //std::shared_ptr *ptr = new (memory) std::shared_ptr(user); + + // Can do the same with NLMISC::CSmartPtr +} + +bool CQuicTransceiver::listening() +{ + return m->Listening.load(std::memory_order_acquire); +} + +void CQuicTransceiver::datagramReceived(const CQuicUserContext *user, const uint8 *buffer, uint32 length) +{ + CAtomicFlagLock(m->BufferMutex); + m->Buffer->push(buffer, length); + m->Buffer->push(user->VTokenAddr); +} + +NLMISC::CBufFIFO *CQuicTransceiver::swapWriteQueue(NLMISC::CBufFIFO *writeQueue) +{ + CAtomicFlagLockYield(m->BufferMutex); + CBufFIFO *previous = m->Buffer; + m->Buffer = writeQueue; + return previous; +} + +#else + +class CQuicTransceiverImpl +{ +public: + NLMISC::CBufFIFO *Buffer = null; +}; + +CQuicTransceiver::CQuicTransceiver(uint32 msgsize) + : m(std::make_unique()) + , m_MsgSize(msgsize) +{ +} + +CQuicTransceiver::~CQuicTransceiver() +{ +} + +void CQuicTransceiver::start() +{ +} + +void CQuicTransceiver::stop() +{ +} + +CInetAddress CQuicTransceiver::generateTokenAddr() +{ + return CInetAddress(false); +} + +bool CQuicTransceiver::listening() +{ + return false; +} + +void CQuicTransceiver::datagramReceived(const CQuicUserContext *user, const uint8 *buffer, uint32 length) +{ + // LOCK + // m->Buffer->push(buffer, length); + // m->Buffer->push(user->VTokenAddr); +} + +NLMISC::CBufFIFO *CQuicTransceiver::swapWriteQueue(NLMISC::CBufFIFO *writeQueue) +{ + // LOCK + CBufFIFO *previous = m->Buffer; + m->Buffer = writeQueue; + return previous; +} + +#endif + +/* end of file */ diff --git a/ryzom/server/src/frontend_service/quic_transceiver.h b/ryzom/server/src/frontend_service/quic_transceiver.h new file mode 100644 index 0000000000..4770cb82b0 --- /dev/null +++ b/ryzom/server/src/frontend_service/quic_transceiver.h @@ -0,0 +1,126 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2023 Jan BOON (Kaetemi) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef NL_QUIC_TRANSCEIVER_H +#define NL_QUIC_TRANSCEIVER_H + +#include "nel/misc/types_nl.h" + +#include "nel/net/inet_address.h" + +#include "fe_receive_task.h" + +class CQuicTransceiverImpl; + +struct CQuicUserContext +{ + CQuicTransceiver *Transceiver; + + // Not a real address, just a token to identify the connection + // Give everyone an IPv6 in unique local network fdd5:d66b:8698::/48 + // The addresses are a sequential hash sequence, so should be unique for a very very long time + // They should not be used for security purposes + CInetAddress TokenAddr; + std::vector VTokenAddr; +}; + +class CQuicTransceiver +{ +public: + CQuicTransceiver(uint32 msgsize); + ~CQuicTransceiver(); + + /// Start listening + void start(uint16 port); + + /// Stop listening + void stop(); + + /// Release. Instance is useless after this call + void release(); + + /// Set new write queue for incoming messages (thread-safe because mutexed) + NLMISC::CBufFIFO *swapWriteQueue(NLMISC::CBufFIFO *writeQueue); + + /// Check if still listening + bool listening(); + +private: + friend CQuicTransceiverImpl; + + /// Internal implementation specific + std::unique_ptr m; + + /// User configuration + uint32 m_MsgSize; + + /// Received datagram + void datagramReceived(const CQuicUserContext *user, const uint8 *buffer, uint32 length); + + /// Generates a token address to identify the connection with existing code + CInetAddress generateTokenAddr(); + + // public: + // /// Constructor + // CFEReceiveTask(uint16 firstAcceptablePort, uint16 lastAcceptablePort, uint32 msgsize); + // + // /// Destructor + // ~CFEReceiveTask(); + // + // /// Run + // virtual void run(); + // + // /// Set new write queue (thread-safe because mutexed) + // void setWriteQueue(NLMISC::CBufFIFO *writequeue); + // + // /// Require exit (thread-safe because atomic assignment) + // void requireExit() { _ExitRequired = true; } + // + // /// Return the number of rejected datagrams since the last call (thread-safe because atomic assignment) + // uint nbNewRejectedDatagrams() + // { + // uint nb = _NbRejectedDatagrams; + // _NbRejectedDatagrams = 0; + // return nb; + // } + // + // private: + // /// Datagram length + // uint _DatagramLength; + // + // /// Received message + // TReceivedMessage _ReceivedMessage; + // + // /// Write queue access + // NLMISC::CSynchronized _WriteQueue; + // + // /// Number of datagrams not copied because too big + // volatile uint _NbRejectedDatagrams; + // + // /// Exit required + // volatile bool _ExitRequired; + // + // public: + // /// External datagram socket + // NLNET::CUdpSock *DataSock; + // + // /// The date of the last UPD packet recevied + // static volatile uint32 LastUDPPacketReceived; +}; + +#endif /* NL_QUIC_TRANSCEIVER_H */ + +/* end of file */