From 327ce45124ae89722a898332e7b94a496f34b123 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Thu, 1 Aug 2024 16:02:45 -0400 Subject: [PATCH] Refactor network implementations to be more reusable and less buggy (#2107) encapsulate network interfaces --- src/Platform.h | 1 + src/frontend/qt_sdl/CMakeLists.txt | 4 + src/frontend/qt_sdl/EmuInstance.cpp | 9 +- src/frontend/qt_sdl/Platform.cpp | 25 +- src/frontend/qt_sdl/WifiSettingsDialog.cpp | 35 +- src/frontend/qt_sdl/WifiSettingsDialog.h | 3 + src/frontend/qt_sdl/Window.cpp | 3 +- src/frontend/qt_sdl/main.cpp | 47 ++- src/net/CMakeLists.txt | 1 + src/net/LocalMP.cpp | 138 ++------ src/net/LocalMP.h | 73 +++- src/net/Net.cpp | 69 +--- src/net/Net.h | 42 ++- src/net/NetDriver.h | 35 ++ src/net/Net_PCap.cpp | 370 +++++++++++++-------- src/net/Net_PCap.h | 103 +++++- src/net/Net_Slirp.cpp | 133 +++++--- src/net/Net_Slirp.h | 47 ++- src/net/PacketDispatcher.cpp | 12 +- src/net/PacketDispatcher.h | 4 +- 20 files changed, 715 insertions(+), 439 deletions(-) create mode 100644 src/net/NetDriver.h diff --git a/src/Platform.h b/src/Platform.h index 425f4f4e..90791100 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -305,6 +305,7 @@ u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask, void* userdata); // packet type: Ethernet (802.3) int Net_SendPacket(u8* data, int len, void* userdata); int Net_RecvPacket(u8* data, void* userdata); +using SendPacketCallback = std::function; // interface for camera emulation diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 8afadddb..559b7a2d 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -163,6 +163,10 @@ endif() target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..") +target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../net") +target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../net/libslirp/src") +get_target_property(SLIRP_BINARY_DIR slirp BINARY_DIR) +target_include_directories(melonDS PUBLIC "${SLIRP_BINARY_DIR}") # for libslirp-version.h if (USE_QT6) target_include_directories(melonDS PUBLIC ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) else() diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 2c8e52a0..188d42f7 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -62,6 +62,8 @@ using namespace melonDS::Platform; MainWindow* topWindow = nullptr; const string kWifiSettingsPath = "wfcsettings.bin"; +extern LocalMP localMp; +extern Net net; EmuInstance::EmuInstance(int inst) : instanceID(inst), @@ -97,7 +99,7 @@ EmuInstance::EmuInstance(int inst) : instanceID(inst), audioInit(); inputInit(); - Net::RegisterInstance(instanceID); + net.RegisterInstance(instanceID); emuThread = new EmuThread(this); @@ -116,14 +118,13 @@ EmuInstance::EmuInstance(int inst) : instanceID(inst), EmuInstance::~EmuInstance() { // TODO window cleanup and shit? - - LocalMP::End(instanceID); + localMp.End(instanceID); emuThread->emuExit(); emuThread->wait(); delete emuThread; - Net::UnregisterInstance(instanceID); + net.UnregisterInstance(instanceID); audioDeInit(); inputDeInit(); diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 9455cf51..217f1b45 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -47,7 +47,8 @@ #endif // __WIN32__ extern CameraManager* camManager[2]; - +extern melonDS::LocalMP localMp; +extern melonDS::Net net; namespace melonDS::Platform { @@ -457,69 +458,69 @@ void WriteDateTime(int year, int month, int day, int hour, int minute, int secon void MP_Begin(void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - LocalMP::Begin(inst); + localMp.Begin(inst); } void MP_End(void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - LocalMP::End(inst); + localMp.End(inst); } int MP_SendPacket(u8* data, int len, u64 timestamp, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::SendPacket(inst, data, len, timestamp); + return localMp.SendPacket(inst, data, len, timestamp); } int MP_RecvPacket(u8* data, u64* timestamp, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::RecvPacket(inst, data, timestamp); + return localMp.RecvPacket(inst, data, timestamp); } int MP_SendCmd(u8* data, int len, u64 timestamp, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::SendCmd(inst, data, len, timestamp); + return localMp.SendCmd(inst, data, len, timestamp); } int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::SendReply(inst, data, len, timestamp, aid); + return localMp.SendReply(inst, data, len, timestamp, aid); } int MP_SendAck(u8* data, int len, u64 timestamp, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::SendAck(inst, data, len, timestamp); + return localMp.SendAck(inst, data, len, timestamp); } int MP_RecvHostPacket(u8* data, u64* timestamp, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::RecvHostPacket(inst, data, timestamp); + return localMp.RecvHostPacket(inst, data, timestamp); } u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return LocalMP::RecvReplies(inst, data, timestamp, aidmask); + return localMp.RecvReplies(inst, data, timestamp, aidmask); } int Net_SendPacket(u8* data, int len, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - Net::SendPacket(data, len, inst); + net.SendPacket(data, len, inst); return 0; } int Net_RecvPacket(u8* data, void* userdata) { int inst = ((EmuInstance*)userdata)->getInstanceID(); - return Net::RecvPacket(data, inst); + return net.RecvPacket(data, inst); } diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index 183fd9e7..c3f988b1 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -37,11 +37,14 @@ #define PCAP_NAME "libpcap" #endif +extern std::optional pcap; +extern melonDS::Net net; WifiSettingsDialog* WifiSettingsDialog::currentDlg = nullptr; bool WifiSettingsDialog::needsReset = false; +void NetInit(); WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::WifiSettingsDialog) { @@ -51,8 +54,12 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne emuInstance = ((MainWindow*)parent)->getEmuInstance(); auto& cfg = emuInstance->getGlobalConfig(); - Net::DeInit(); - haspcap = Net_PCap::InitAdapterList(); + if (!pcap) + pcap = melonDS::LibPCap::New(); + + haspcap = pcap.has_value(); + if (pcap) + adapters = pcap->GetAdapters(); ui->rbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)"); @@ -60,13 +67,13 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne ui->lblAdapterIP->setText("(none)"); int sel = 0; - for (int i = 0; i < Net_PCap::NumAdapters; i++) + for (int i = 0; i < adapters.size(); i++) { - Net_PCap::AdapterData* adapter = &Net_PCap::Adapters[i]; + melonDS::AdapterData& adapter = adapters[i]; - ui->cbxDirectAdapter->addItem(QString(adapter->FriendlyName)); + ui->cbxDirectAdapter->addItem(QString(adapter.FriendlyName)); - if (!strncmp(adapter->DeviceName, cfg.GetString("LAN.Device").c_str(), 128)) + if (!strncmp(adapter.DeviceName, cfg.GetString("LAN.Device").c_str(), 128)) sel = i; } ui->cbxDirectAdapter->setCurrentIndex(sel); @@ -96,24 +103,23 @@ void WifiSettingsDialog::done(int r) cfg.SetBool("LAN.DirectMode", ui->rbDirectMode->isChecked()); int sel = ui->cbxDirectAdapter->currentIndex(); - if (sel < 0 || sel >= Net_PCap::NumAdapters) sel = 0; - if (Net_PCap::NumAdapters < 1) + if (sel < 0 || sel >= adapters.size()) sel = 0; + if (adapters.empty()) { cfg.SetString("LAN.Device", ""); } else { - cfg.SetString("LAN.Device", Net_PCap::Adapters[sel].DeviceName); + cfg.SetString("LAN.Device", adapters[sel].DeviceName); } Config::Save(); } - Net_PCap::DeInit(); Config::Table cfg = Config::GetGlobalTable(); - bool direct = cfg.GetBool("LAN.DirectMode"); std::string devicename = cfg.GetString("LAN.Device"); - Net::Init(direct, devicename.c_str()); + + NetInit(); QDialog::done(r); @@ -134,10 +140,9 @@ void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel) { if (!haspcap) return; - if (sel < 0 || sel >= Net_PCap::NumAdapters) return; - if (Net_PCap::NumAdapters < 1) return; + if (sel < 0 || sel >= adapters.size() || adapters.empty()) return; - Net_PCap::AdapterData* adapter = &Net_PCap::Adapters[sel]; + melonDS::AdapterData* adapter = &adapters[sel]; char tmp[64]; sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.h b/src/frontend/qt_sdl/WifiSettingsDialog.h index 8f2e4a1d..d78b0f65 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.h +++ b/src/frontend/qt_sdl/WifiSettingsDialog.h @@ -20,6 +20,8 @@ #define WIFISETTINGSDIALOG_H #include +#include +#include "Net_PCap.h" namespace Ui { class WifiSettingsDialog; } class WifiSettingsDialog; @@ -68,6 +70,7 @@ private: bool haspcap; void updateAdapterControls(); + std::vector adapters; }; #endif // WIFISETTINGSDIALOG_H diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index e4e23755..f39bbb37 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -87,6 +87,7 @@ using namespace melonDS; extern CameraManager* camManager[2]; extern bool camStarted[2]; +extern LocalMP localMp; QString NdsRomMimeType = "application/x-nintendo-ds-rom"; @@ -1832,7 +1833,7 @@ void MainWindow::onMPSettingsFinished(int res) { emuInstance->mpAudioMode = globalCfg.GetInt("MP.AudioMode"); emuInstance->audioMute(); - LocalMP::SetRecvTimeout(globalCfg.GetInt("MP.RecvTimeout")); + localMp.SetRecvTimeout(globalCfg.GetInt("MP.RecvTimeout")); emuThread->emuUnpause(); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 69285e97..ba2dd95f 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -79,6 +79,9 @@ #include "CLI.h" +#include "Net_PCap.h" +#include "Net_Slirp.h" + using namespace melonDS; QString* systemThemeName; @@ -92,6 +95,38 @@ EmuInstance* emuInstances[kMaxEmuInstances]; CameraManager* camManager[2]; bool camStarted[2]; +LocalMP localMp; +std::optional pcap; +Net net; + +void NetInit() +{ + Config::Table cfg = Config::GetGlobalTable(); + if (cfg.GetBool("LAN.DirectMode")) + { + if (!pcap) + pcap = LibPCap::New(); + + if (pcap) + { + std::string devicename = cfg.GetString("LAN.Device"); + std::unique_ptr netPcap = pcap->Open(devicename, [](const u8* data, int len) { + net.RXEnqueue(data, len); + }); + + if (netPcap) + { + net.SetDriver(std::move(netPcap)); + } + } + } + else + { + net.SetDriver(std::make_unique([](const u8* data, int len) { + net.RXEnqueue(data, len); + })); + } +} bool createEmuInstance() @@ -273,13 +308,8 @@ int main(int argc, char** argv) } } - LocalMP::Init(); - { - Config::Table cfg = Config::GetGlobalTable(); - bool direct = cfg.GetBool("LAN.DirectMode"); - std::string devicename = cfg.GetString("LAN.Device"); - Net::Init(direct, devicename.c_str()); - } + // localMp is initialized at this point + NetInit(); createEmuInstance(); @@ -316,9 +346,6 @@ int main(int argc, char** argv) // but with this we make extra sure they are all deleted deleteAllEmuInstances(); - LocalMP::DeInit(); - Net::DeInit(); - delete camManager[0]; delete camManager[1]; diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index f1a9eaf7..082c00fb 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -16,5 +16,6 @@ if (USE_SYSTEM_LIBSLIRP) target_link_libraries(net-utils PRIVATE PkgConfig::Slirp) else() add_subdirectory(libslirp EXCLUDE_FROM_ALL) + target_include_directories(net-utils SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/libslirp/glib") target_link_libraries(net-utils PRIVATE slirp) endif() diff --git a/src/net/LocalMP.cpp b/src/net/LocalMP.cpp index 89f98c8c..0f6889ac 100644 --- a/src/net/LocalMP.cpp +++ b/src/net/LocalMP.cpp @@ -28,99 +28,31 @@ using namespace melonDS::Platform; using Platform::Log; using Platform::LogLevel; -namespace LocalMP +namespace melonDS { -struct MPStatusData +LocalMP::LocalMP() noexcept : + MPQueueLock(Mutex_Create()) { - u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive packets - u32 PacketWriteOffset; - u32 ReplyWriteOffset; - u16 MPHostinst; // instance ID from which the last CMD frame was sent - u16 MPReplyBitmask; // bitmask of which clients replied in time -}; - -struct MPPacketHeader -{ - u32 Magic; - u32 SenderID; - u32 Type; // 0=regular 1=CMD 2=reply 3=ack - u32 Length; - u64 Timestamp; -}; - -const u32 kPacketQueueSize = 0x10000; -const u32 kReplyQueueSize = 0x10000; -const u32 kMaxFrameSize = 0x948; - -Mutex* MPQueueLock; -MPStatusData MPStatus; -u8 MPPacketQueue[kPacketQueueSize]; -u8 MPReplyQueue[kReplyQueueSize]; -u32 PacketReadOffset[16]; -u32 ReplyReadOffset[16]; - -int RecvTimeout; - -int LastHostID; - - -Semaphore* SemPool[32]; - -void SemPoolInit() -{ - for (int i = 0; i < 32; i++) - { - SemPool[i] = Semaphore_Create(); - } -} - -bool SemPost(int num) -{ - Semaphore_Post(SemPool[num]); - return true; -} - -bool SemWait(int num, int timeout) -{ - return Semaphore_TryWait(SemPool[num], timeout); -} - -void SemReset(int num) -{ - Semaphore_Reset(SemPool[num]); -} - - -bool Init() -{ - MPQueueLock = Mutex_Create(); - Mutex_Lock(MPQueueLock); - memset(MPPacketQueue, 0, kPacketQueueSize); memset(MPReplyQueue, 0, kReplyQueueSize); memset(&MPStatus, 0, sizeof(MPStatus)); memset(PacketReadOffset, 0, sizeof(PacketReadOffset)); memset(ReplyReadOffset, 0, sizeof(ReplyReadOffset)); - Mutex_Unlock(MPQueueLock); - // prepare semaphores // semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame // semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply - SemPoolInit(); - - LastHostID = -1; + for (int i = 0; i < 32; i++) + { + SemPool[i] = Semaphore_Create(); + } Log(LogLevel::Info, "MP comm init OK\n"); - - RecvTimeout = 25; - - return true; } -void DeInit() +LocalMP::~LocalMP() noexcept { for (int i = 0; i < 32; i++) { @@ -131,30 +63,25 @@ void DeInit() Mutex_Free(MPQueueLock); } -void SetRecvTimeout(int timeout) -{ - RecvTimeout = timeout; -} - -void Begin(int inst) +void LocalMP::Begin(int inst) { Mutex_Lock(MPQueueLock); PacketReadOffset[inst] = MPStatus.PacketWriteOffset; ReplyReadOffset[inst] = MPStatus.ReplyWriteOffset; - SemReset(inst); - SemReset(16+inst); + Semaphore_Reset(SemPool[inst]); + Semaphore_Reset(SemPool[16 + inst]); MPStatus.ConnectedBitmask |= (1 << inst); Mutex_Unlock(MPQueueLock); } -void End(int inst) +void LocalMP::End(int inst) { Mutex_Lock(MPQueueLock); MPStatus.ConnectedBitmask &= ~(1 << inst); Mutex_Unlock(MPQueueLock); } -void FIFORead(int inst, int fifo, void* buf, int len) +void LocalMP::FIFORead(int inst, int fifo, void* buf, int len) noexcept { u8* data; @@ -189,7 +116,7 @@ void FIFORead(int inst, int fifo, void* buf, int len) else ReplyReadOffset[inst] = offset; } -void FIFOWrite(int inst, int fifo, void* buf, int len) +void LocalMP::FIFOWrite(int inst, int fifo, void* buf, int len) noexcept { u8* data; @@ -224,7 +151,7 @@ void FIFOWrite(int inst, int fifo, void* buf, int len) else MPStatus.ReplyWriteOffset = offset; } -int SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp) +int LocalMP::SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp) noexcept { if (len > kMaxFrameSize) { @@ -258,7 +185,7 @@ int SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp) MPStatus.MPHostinst = inst; MPStatus.MPReplyBitmask = 0; ReplyReadOffset[inst] = MPStatus.ReplyWriteOffset; - SemReset(16 + inst); + Semaphore_Reset(SemPool[16 + inst]); } else if (type == 2) { @@ -269,39 +196,39 @@ int SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp) if (type == 2) { - SemPost(16 + MPStatus.MPHostinst); + Semaphore_Post(SemPool[16 + MPStatus.MPHostinst]); } else { for (int i = 0; i < 16; i++) { if (mask & (1< #include #include "Net.h" -#include "Net_PCap.h" -#include "Net_Slirp.h" #include "PacketDispatcher.h" #include "Platform.h" -using namespace melonDS; - -namespace Net +namespace melonDS { using Platform::Log; using Platform::LogLevel; -bool Inited = false; -bool DirectMode; - -PacketDispatcher Dispatcher; - - -bool Init(bool direct, const char* devicename) -{ - if (Inited) DeInit(); - - Dispatcher.clear(); - - DirectMode = direct; - - bool ret = false; - if (DirectMode) - ret = Net_PCap::Init(devicename); - else - ret = Net_Slirp::Init(); - - Inited = ret; - return ret; -} - -void DeInit() -{ - if (!Inited) return; - - if (DirectMode) - Net_PCap::DeInit(); - else - Net_Slirp::DeInit(); - - Inited = false; -} - - -void RegisterInstance(int inst) +void Net::RegisterInstance(int inst) { Dispatcher.registerInstance(inst); } -void UnregisterInstance(int inst) +void Net::UnregisterInstance(int inst) { Dispatcher.unregisterInstance(inst); } -void RXEnqueue(const void* buf, int len) +void Net::RXEnqueue(const void* buf, int len) { Dispatcher.sendPacket(nullptr, 0, buf, len, 16, 0xFFFF); } -int SendPacket(u8* data, int len, int inst) +int Net::SendPacket(u8* data, int len, int inst) { - if (DirectMode) - return Net_PCap::SendPacket(data, len); - else - return Net_Slirp::SendPacket(data, len); + if (!Driver) + return 0; + + return Driver->SendPacket(data, len); } -int RecvPacket(u8* data, int inst) +int Net::RecvPacket(u8* data, int inst) { - if (DirectMode) - Net_PCap::RecvCheck(); - else - Net_Slirp::RecvCheck(); + if (!Driver) + return 0; + + Driver->RecvCheck(); int ret = 0; if (!Dispatcher.recvPacket(nullptr, nullptr, data, &ret, inst)) diff --git a/src/net/Net.h b/src/net/Net.h index 31418831..4229468c 100644 --- a/src/net/Net.h +++ b/src/net/Net.h @@ -19,26 +19,42 @@ #ifndef NET_H #define NET_H +#include + #include "types.h" +#include "PacketDispatcher.h" +#include "NetDriver.h" -namespace Net +namespace melonDS { -using namespace melonDS; -/// -/// @param direct Whether to use direct or indirect mode -/// @param devicename The name of the network device to use; ignored if direct is false -/// @return true if initialization succeeded -bool Init(bool direct, const char* devicename); -void DeInit(); +class Net +{ +public: + Net() noexcept = default; + Net(const Net&) = delete; + Net& operator=(const Net&) = delete; + // Not movable because of callbacks that point to this object + Net(Net&& other) = delete; + Net& operator=(Net&& other) = delete; + ~Net() noexcept = default; -void RegisterInstance(int inst); -void UnregisterInstance(int inst); + void RegisterInstance(int inst); + void UnregisterInstance(int inst); -void RXEnqueue(const void* buf, int len); + void RXEnqueue(const void* buf, int len); -int SendPacket(u8* data, int len, int inst); -int RecvPacket(u8* data, int inst); + int SendPacket(u8* data, int len, int inst); + int RecvPacket(u8* data, int inst); + + void SetDriver(std::unique_ptr&& driver) noexcept { Driver = std::move(driver); } + [[nodiscard]] std::unique_ptr& GetDriver() noexcept { return Driver; } + [[nodiscard]] const std::unique_ptr& GetDriver() const noexcept { return Driver; } + +private: + PacketDispatcher Dispatcher {}; + std::unique_ptr Driver = nullptr; +}; } diff --git a/src/net/NetDriver.h b/src/net/NetDriver.h new file mode 100644 index 00000000..2575fdea --- /dev/null +++ b/src/net/NetDriver.h @@ -0,0 +1,35 @@ +/* + Copyright 2016-2024 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_NETDRIVER_H +#define MELONDS_NETDRIVER_H + +#include "types.h" + +namespace melonDS +{ +class NetDriver +{ +public: + virtual ~NetDriver() = default; + virtual int SendPacket(u8* data, int len) noexcept = 0; + virtual void RecvCheck() noexcept = 0; +}; +} + +#endif //MELONDS_NETDRIVER_H diff --git a/src/net/Net_PCap.cpp b/src/net/Net_PCap.cpp index f6ce59e6..e92ad06d 100644 --- a/src/net/Net_PCap.cpp +++ b/src/net/Net_PCap.cpp @@ -45,23 +45,7 @@ using Platform::LogLevel; #define PCAP_OPENFLAG_PROMISCUOUS 1 #endif - -#define DECL_PCAP_FUNC(ret, name, args, args2) \ - typedef ret (*type_##name) args; \ - type_##name ptr_##name = nullptr; \ - ret name args { return ptr_##name args2; } - -DECL_PCAP_FUNC(int, pcap_findalldevs, (pcap_if_t** alldevs, char* errbuf), (alldevs,errbuf)) -DECL_PCAP_FUNC(void, pcap_freealldevs, (pcap_if_t* alldevs), (alldevs)) -DECL_PCAP_FUNC(pcap_t*, pcap_open_live, (const char* src, int snaplen, int flags, int readtimeout, char* errbuf), (src,snaplen,flags,readtimeout,errbuf)) -DECL_PCAP_FUNC(void, pcap_close, (pcap_t* dev), (dev)) -DECL_PCAP_FUNC(int, pcap_setnonblock, (pcap_t* dev, int nonblock, char* errbuf), (dev,nonblock,errbuf)) -DECL_PCAP_FUNC(int, pcap_sendpacket, (pcap_t* dev, const u_char* data, int len), (dev,data,len)) -DECL_PCAP_FUNC(int, pcap_dispatch, (pcap_t* dev, int num, pcap_handler callback, u_char* data), (dev,num,callback,data)) -DECL_PCAP_FUNC(const u_char*, pcap_next, (pcap_t* dev, struct pcap_pkthdr* hdr), (dev,hdr)) - - -namespace Net_PCap +namespace melonDS { const char* PCapLibNames[] = @@ -80,96 +64,152 @@ const char* PCapLibNames[] = nullptr }; -AdapterData* Adapters = nullptr; -int NumAdapters = 0; - -Platform::DynamicLibrary* PCapLib = nullptr; -pcap_t* PCapAdapter = nullptr; -AdapterData* PCapAdapterData; - - -#define LOAD_PCAP_FUNC(sym) \ - ptr_##sym = (type_##sym)DynamicLibrary_LoadFunction(lib, #sym); \ - if (!ptr_##sym) return false; - -bool TryLoadPCap(Platform::DynamicLibrary *lib) +std::optional LibPCap::New() noexcept { - LOAD_PCAP_FUNC(pcap_findalldevs) - LOAD_PCAP_FUNC(pcap_freealldevs) - LOAD_PCAP_FUNC(pcap_open_live) - LOAD_PCAP_FUNC(pcap_close) - LOAD_PCAP_FUNC(pcap_setnonblock) - LOAD_PCAP_FUNC(pcap_sendpacket) - LOAD_PCAP_FUNC(pcap_dispatch) - LOAD_PCAP_FUNC(pcap_next) + for (int i = 0; PCapLibNames[i]; i++) + { + Platform::DynamicLibrary* lib = Platform::DynamicLibrary_Load(PCapLibNames[i]); + if (!lib) continue; + + LibPCap pcap; + // Use a custom deleter to clean up the DLL automatically + // (in this case, the deleter is the DynamicLibrary_Unload function) + pcap.PCapLib = std::shared_ptr(lib, Platform::DynamicLibrary_Unload); + + if (!TryLoadPCap(pcap, lib)) + { + Platform::DynamicLibrary_Unload(lib); + continue; + } + + Log(LogLevel::Info, "PCap: lib %s, init successful\n", PCapLibNames[i]); + return pcap; + } + + Log(LogLevel::Error, "PCap: init failed\n"); + return std::nullopt; +} + +LibPCap::LibPCap(LibPCap&& other) noexcept +{ + PCapLib = std::move(other.PCapLib); + findalldevs = other.findalldevs; + freealldevs = other.freealldevs; + open_live = other.open_live; + close = other.close; + setnonblock = other.setnonblock; + sendpacket = other.sendpacket; + dispatch = other.dispatch; + next = other.next; + + other.PCapLib = nullptr; + other.findalldevs = nullptr; + other.freealldevs = nullptr; + other.open_live = nullptr; + other.close = nullptr; + other.setnonblock = nullptr; + other.sendpacket = nullptr; + other.dispatch = nullptr; + other.next = nullptr; +} + +LibPCap& LibPCap::operator=(LibPCap&& other) noexcept +{ + if (this != &other) + { + PCapLib = nullptr; + // Unloads the DLL due to the custom deleter + + PCapLib = std::move(other.PCapLib); + findalldevs = other.findalldevs; + freealldevs = other.freealldevs; + open_live = other.open_live; + close = other.close; + setnonblock = other.setnonblock; + sendpacket = other.sendpacket; + dispatch = other.dispatch; + next = other.next; + + other.PCapLib = nullptr; + other.findalldevs = nullptr; + other.freealldevs = nullptr; + other.open_live = nullptr; + other.close = nullptr; + other.setnonblock = nullptr; + other.sendpacket = nullptr; + other.dispatch = nullptr; + other.next = nullptr; + } + + return *this; +} + +bool LibPCap::TryLoadPCap(LibPCap& pcap, Platform::DynamicLibrary *lib) noexcept +{ + pcap.findalldevs = (pcap_findalldevs_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_findalldevs"); + if (!pcap.findalldevs) return false; + + pcap.freealldevs = (pcap_freealldevs_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_freealldevs"); + if (!pcap.freealldevs) return false; + + pcap.open_live = (pcap_open_live_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_open_live"); + if (!pcap.open_live) return false; + + pcap.close = (pcap_close_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_close"); + if (!pcap.close) return false; + + pcap.setnonblock = (pcap_setnonblock_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_setnonblock"); + if (!pcap.setnonblock) return false; + + pcap.sendpacket = (pcap_sendpacket_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_sendpacket"); + if (!pcap.sendpacket) return false; + + pcap.dispatch = (pcap_dispatch_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_dispatch"); + if (!pcap.dispatch) return false; + + pcap.next = (pcap_next_t)Platform::DynamicLibrary_LoadFunction(lib, "pcap_next"); + if (!pcap.next) return false; return true; } -bool InitAdapterList() +std::vector LibPCap::GetAdapters() const noexcept { - NumAdapters = 0; - // TODO: how to deal with cases where an adapter is unplugged or changes config?? - if (!PCapLib) + if (!IsValid()) { - PCapLib = nullptr; - PCapAdapter = nullptr; - - for (int i = 0; PCapLibNames[i]; i++) - { - Platform::DynamicLibrary* lib = Platform::DynamicLibrary_Load(PCapLibNames[i]); - if (!lib) continue; - - if (!TryLoadPCap(lib)) - { - Platform::DynamicLibrary_Unload(lib); - continue; - } - - Log(LogLevel::Info, "PCap: lib %s, init successful\n", PCapLibNames[i]); - PCapLib = lib; - break; - } - - if (PCapLib == nullptr) - { - Log(LogLevel::Error, "PCap: init failed\n"); - return false; - } + Log(LogLevel::Error, "PCap: instance not initialized\n"); + return {}; } char errbuf[PCAP_ERRBUF_SIZE]; - int ret; - pcap_if_t* alldevs; - ret = pcap_findalldevs(&alldevs, errbuf); - if (ret < 0 || alldevs == nullptr) - { - Log(LogLevel::Warn, "PCap: no devices available\n"); - return false; + pcap_if_t* alldevs = nullptr; + if (int ret = findalldevs(&alldevs, errbuf); ret < 0) + { // If there was an error... + errbuf[PCAP_ERRBUF_SIZE - 1] = '\0'; + Log(LogLevel::Error, "PCap: Error %d finding devices: %s\n", ret, errbuf); } - pcap_if_t* dev = alldevs; - while (dev) { NumAdapters++; dev = dev->next; } + if (alldevs == nullptr) + { // If no devices were found... + Log(LogLevel::Warn, "PCap: no devices available\n"); + return {}; + } - Adapters = new AdapterData[NumAdapters]; - memset(Adapters, 0, sizeof(AdapterData)*NumAdapters); - - AdapterData* adata = &Adapters[0]; - dev = alldevs; - while (dev) + std::vector adapters; + for (pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) { - strncpy(adata->DeviceName, dev->name, 127); - adata->DeviceName[127] = '\0'; + adapters.emplace_back(); // Add a new (empty) adapter to the list + AdapterData& adata = adapters.back(); + strncpy(adata.DeviceName, dev->name, 127); + adata.DeviceName[127] = '\0'; + adata.Flags = dev->flags; #ifndef __WIN32__ - strncpy(adata->FriendlyName, adata->DeviceName, 127); - adata->FriendlyName[127] = '\0'; + strncpy(adata.FriendlyName, adata.DeviceName, 127); + adata.FriendlyName[127] = '\0'; #endif // __WIN32__ - - dev = dev->next; - adata++; } #ifdef __WIN32__ @@ -186,33 +226,33 @@ bool InitAdapterList() if (uret != ERROR_SUCCESS) { Log(LogLevel::Error, "GetAdaptersAddresses() shat itself: %08X\n", uret); - return false; + freealldevs(alldevs); + return {}; } - for (int i = 0; i < NumAdapters; i++) + for (AdapterData& adata : adapters) { - adata = &Adapters[i]; IP_ADAPTER_ADDRESSES* addr = buf; while (addr) { - if (strcmp(addr->AdapterName, &adata->DeviceName[12])) + if (strcmp(addr->AdapterName, &adata.DeviceName[12])) { addr = addr->Next; continue; } - WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, nullptr, nullptr); - adata->FriendlyName[127] = '\0'; + WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata.FriendlyName, 127, nullptr, nullptr); + adata.FriendlyName[127] = '\0'; - WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, nullptr, nullptr); - adata->Description[127] = '\0'; + WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata.Description, 127, nullptr, nullptr); + adata.Description[127] = '\0'; if (addr->PhysicalAddressLength != 6) { Log(LogLevel::Warn, "weird MAC addr length %d for %s\n", addr->PhysicalAddressLength, addr->AdapterName); } else - memcpy(adata->MAC, addr->PhysicalAddress, 6); + memcpy(adata.MAC, addr->PhysicalAddress, 6); IP_ADAPTER_UNICAST_ADDRESS* ipaddr = addr->FirstUnicastAddress; while (ipaddr) @@ -221,7 +261,7 @@ bool InitAdapterList() if (sa->sa_family == AF_INET) { struct in_addr sa4 = ((sockaddr_in*)sa)->sin_addr; - memcpy(adata->IP_v4, &sa4, 4); + memcpy(adata.IP_v4, &sa4, 4); } ipaddr = ipaddr->Next; @@ -239,16 +279,15 @@ bool InitAdapterList() if (getifaddrs(&addrs) != 0) { Log(LogLevel::Error, "getifaddrs() shat itself :(\n"); - return false; + return {}; } - for (int i = 0; i < NumAdapters; i++) + for (const AdapterData& adata : adapters) { - adata = &Adapters[i]; struct ifaddrs* curaddr = addrs; while (curaddr) { - if (strcmp(curaddr->ifa_name, adata->DeviceName)) + if (strcmp(curaddr->ifa_name, adata.DeviceName)) { curaddr = curaddr->ifa_next; continue; @@ -265,7 +304,7 @@ bool InitAdapterList() if (af == AF_INET) { struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr; - memcpy(adata->IP_v4, &sa->sin_addr, 4); + memcpy((void*)adata.IP_v4, &sa->sin_addr, 4); } #ifdef __linux__ else if (af == AF_PACKET) @@ -274,7 +313,7 @@ bool InitAdapterList() if (sa->sll_halen != 6) Log(LogLevel::Warn, "weird MAC length %d for %s\n", sa->sll_halen, curaddr->ifa_name); else - memcpy(adata->MAC, sa->sll_addr, 6); + memcpy((void*)adata.MAC, sa->sll_addr, 6); } #else else if (af == AF_LINK) @@ -283,7 +322,7 @@ bool InitAdapterList() if (sa->sdl_alen != 6) Log(LogLevel::Warn, "weird MAC length %d for %s\n", sa->sdl_alen, curaddr->ifa_name); else - memcpy(adata->MAC, LLADDR(sa), 6); + memcpy((void*)adata.MAC, LLADDR(sa), 6); } #endif curaddr = curaddr->ifa_next; @@ -294,67 +333,110 @@ bool InitAdapterList() #endif // __WIN32__ - pcap_freealldevs(alldevs); - return true; + freealldevs(alldevs); + return adapters; } -bool Init(std::string_view devicename) +std::unique_ptr LibPCap::Open(const AdapterData& device, const Platform::SendPacketCallback& handler) const noexcept { - if (!PCapLib) PCapAdapter = nullptr; - if (PCapAdapter) pcap_close(PCapAdapter); - - InitAdapterList(); - - // open pcap device - PCapAdapterData = &Adapters[0]; - for (int i = 0; i < NumAdapters; i++) - { - if (!strncmp(Adapters[i].DeviceName, devicename.data(), 128)) - PCapAdapterData = &Adapters[i]; - } + return Open(device.DeviceName, handler); +} +std::unique_ptr LibPCap::Open(std::string_view devicename, const Platform::SendPacketCallback& handler) const noexcept +{ char errbuf[PCAP_ERRBUF_SIZE]; - PCapAdapter = pcap_open_live(PCapAdapterData->DeviceName, 2048, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf); - if (!PCapAdapter) + pcap_t* adapter = open_live(devicename.data(), 2048, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf); + if (!adapter) { - Log(LogLevel::Error, "PCap: failed to open adapter %s\n", errbuf); - return false; + errbuf[PCAP_ERRBUF_SIZE - 1] = '\0'; + Log(LogLevel::Error, "PCap: failed to open adapter: %s\n", errbuf); + return nullptr; } - if (pcap_setnonblock(PCapAdapter, 1, errbuf) < 0) + if (int err = setnonblock(adapter, 1, errbuf); err < 0) { - Log(LogLevel::Error, "PCap: failed to set nonblocking mode\n"); - pcap_close(PCapAdapter); PCapAdapter = nullptr; - return false; + errbuf[PCAP_ERRBUF_SIZE - 1] = '\0'; + Log(LogLevel::Error, "PCap: failed to set nonblocking mode with %d: %s\n", err, errbuf); + close(adapter); + return nullptr; } - return true; + std::unique_ptr pcap = std::make_unique(); + pcap->PCapAdapter = adapter; + pcap->Callback = handler; + pcap->PCapLib = PCapLib; + pcap->close = close; + pcap->sendpacket = sendpacket; + pcap->dispatch = dispatch; + + return pcap; } -void DeInit() +Net_PCap::Net_PCap(Net_PCap&& other) noexcept { - if (PCapLib) + PCapAdapter = other.PCapAdapter; + PCapLib = std::move(other.PCapLib); + close = other.close; + sendpacket = other.sendpacket; + dispatch = other.dispatch; + Callback = std::move(other.Callback); + + other.PCapAdapter = nullptr; + other.close = nullptr; + other.PCapLib = nullptr; + other.sendpacket = nullptr; + other.dispatch = nullptr; + other.Callback = nullptr; +} + +Net_PCap& Net_PCap::operator=(Net_PCap&& other) noexcept +{ + if (this != &other) { - if (PCapAdapter) + if (close && PCapAdapter) { - pcap_close(PCapAdapter); + close(PCapAdapter); PCapAdapter = nullptr; } - Platform::DynamicLibrary_Unload(PCapLib); - PCapLib = nullptr; + PCapAdapter = other.PCapAdapter; + PCapLib = std::move(other.PCapLib); + close = other.close; + sendpacket = other.sendpacket; + dispatch = other.dispatch; + Callback = std::move(other.Callback); + + other.PCapAdapter = nullptr; + other.close = nullptr; + other.PCapLib = nullptr; + other.sendpacket = nullptr; + other.dispatch = nullptr; + other.Callback = nullptr; } + + return *this; } - -void RXCallback(u_char* userdata, const struct pcap_pkthdr* header, const u_char* data) +Net_PCap::~Net_PCap() noexcept { - Net::RXEnqueue(data, header->len); + if (close && PCapAdapter) + { + close(PCapAdapter); + PCapAdapter = nullptr; + } + // PCapLib will be freed at this point (shared_ptr + custom deleter) } -int SendPacket(u8* data, int len) +void Net_PCap::RXCallback(u_char* userdata, const struct pcap_pkthdr* header, const u_char* data) noexcept { - if (PCapAdapter == nullptr) + Net_PCap& self = *reinterpret_cast(userdata); + if (self.Callback) + self.Callback(data, header->len); +} + +int Net_PCap::SendPacket(u8* data, int len) noexcept +{ + if (PCapAdapter == nullptr || data == nullptr) return 0; if (len > 2048) @@ -363,17 +445,17 @@ int SendPacket(u8* data, int len) return 0; } - pcap_sendpacket(PCapAdapter, data, len); + sendpacket(PCapAdapter, data, len); // TODO: check success return len; } -void RecvCheck() +void Net_PCap::RecvCheck() noexcept { - if (PCapAdapter == nullptr) + if (PCapAdapter == nullptr || dispatch == nullptr) return; - pcap_dispatch(PCapAdapter, 1, RXCallback, nullptr); + dispatch(PCapAdapter, 1, RXCallback, reinterpret_cast(this)); } } diff --git a/src/net/Net_PCap.h b/src/net/Net_PCap.h index bf2ad882..cd2ab90b 100644 --- a/src/net/Net_PCap.h +++ b/src/net/Net_PCap.h @@ -19,13 +19,20 @@ #ifndef NET_PCAP_H #define NET_PCAP_H +#include +#include +#include #include +#include +#include + #include "types.h" +#include "Platform.h" +#include "NetDriver.h" -namespace Net_PCap + +namespace melonDS { - -using namespace melonDS; struct AdapterData { char DeviceName[128]; @@ -34,19 +41,95 @@ struct AdapterData u8 MAC[6]; u8 IP_v4[4]; + + /// The flags on the pcap_if_t that was used to populate this struct + u32 Flags; }; +typedef int (*pcap_findalldevs_t)(pcap_if_t** alldevs, char* errbuf); +typedef void (*pcap_freealldevs_t)(pcap_if_t* alldevs); +typedef pcap_t* (*pcap_open_live_t)(const char* src, int snaplen, int flags, int readtimeout, char* errbuf); +typedef void (*pcap_close_t)(pcap_t* dev); +typedef int (*pcap_setnonblock_t)(pcap_t* dev, int nonblock, char* errbuf); +typedef int (*pcap_sendpacket_t)(pcap_t* dev, const u_char* data, int len); +typedef int (*pcap_dispatch_t)(pcap_t* dev, int num, pcap_handler callback, u_char* data); +typedef const u_char* (*pcap_next_t)(pcap_t* dev, struct pcap_pkthdr* hdr); -extern AdapterData* Adapters; -extern int NumAdapters; +class Net_PCap; +class LibPCap +{ +public: + static std::optional New() noexcept; + LibPCap(const LibPCap&) = delete; + LibPCap& operator=(const LibPCap&) = delete; + LibPCap(LibPCap&&) noexcept; + LibPCap& operator=(LibPCap&&) noexcept; + ~LibPCap() noexcept = default; -bool InitAdapterList(); -bool Init(std::string_view devicename); -void DeInit(); + [[nodiscard]] std::unique_ptr Open(std::string_view devicename, const Platform::SendPacketCallback& handler) const noexcept; + [[nodiscard]] std::unique_ptr Open(const AdapterData& device, const Platform::SendPacketCallback& handler) const noexcept; -int SendPacket(u8* data, int len); -void RecvCheck(); + // so that Net_PCap objects can safely outlive LibPCap + // (because the actual DLL will be kept loaded until no shared_ptrs remain) + std::shared_ptr PCapLib = nullptr; + pcap_findalldevs_t findalldevs = nullptr; + pcap_freealldevs_t freealldevs = nullptr; + pcap_open_live_t open_live = nullptr; + pcap_close_t close = nullptr; + pcap_setnonblock_t setnonblock = nullptr; + pcap_sendpacket_t sendpacket = nullptr; + pcap_dispatch_t dispatch = nullptr; + pcap_next_t next = nullptr; + + [[nodiscard]] bool IsValid() const noexcept + { + return + PCapLib != nullptr && + findalldevs != nullptr && + freealldevs != nullptr && + open_live != nullptr && + close != nullptr && + setnonblock != nullptr && + sendpacket != nullptr && + dispatch != nullptr && + next != nullptr + ; + } + + /// @return List of all network interfaces available at the time of the call + [[nodiscard]] std::vector GetAdapters() const noexcept; +private: + static bool TryLoadPCap(LibPCap& pcap, Platform::DynamicLibrary *lib) noexcept; + LibPCap() noexcept = default; +}; + +class Net_PCap : public NetDriver +{ +public: + Net_PCap() noexcept = default; + ~Net_PCap() noexcept override; + Net_PCap(const Net_PCap&) = delete; + Net_PCap& operator=(const Net_PCap&) = delete; + Net_PCap(Net_PCap&& other) noexcept; + Net_PCap& operator=(Net_PCap&& other) noexcept; + + int SendPacket(u8* data, int len) noexcept override; + void RecvCheck() noexcept override; +private: + friend class LibPCap; + static void RXCallback(u_char* userdata, const pcap_pkthdr* header, const u_char* data) noexcept; + + pcap_t* PCapAdapter = nullptr; + Platform::SendPacketCallback Callback; + + // To avoid undefined behavior in case the original LibPCap object is destroyed + // before this interface is cleaned up + std::shared_ptr PCapLib = nullptr; + pcap_close_t close = nullptr; + pcap_sendpacket_t sendpacket = nullptr; + pcap_dispatch_t dispatch = nullptr; +}; } diff --git a/src/net/Net_Slirp.cpp b/src/net/Net_Slirp.cpp index 7ec7a046..fef4d19c 100644 --- a/src/net/Net_Slirp.cpp +++ b/src/net/Net_Slirp.cpp @@ -19,11 +19,17 @@ #include #include #include "Net.h" +#include "Net_Slirp.h" #include "FIFO.h" #include "Platform.h" #include +// "register" is indirectly used by slirp.h but isn't allowed in C++17, this is a workaround +#define register +// Needed for Slirp's definition so we can adjust the opaque pointer in the move constructor +#include + #ifdef __WIN32__ #include #else @@ -33,9 +39,7 @@ #include #endif -using namespace melonDS; - -namespace Net_Slirp +namespace melonDS { using Platform::Log; @@ -48,13 +52,6 @@ const u32 kClientIP = kSubnet | 0x10; const u8 kServerMAC[6] = {0x00, 0xAB, 0x33, 0x28, 0x99, 0x44}; -FIFO> 2)> RXBuffer; - -u32 IPv4ID; - -Slirp* Ctx = nullptr; - - #ifdef __WIN32__ #define poll WSAPoll @@ -77,7 +74,7 @@ int clock_gettime(int, struct timespec *spec) #endif // __WIN32__ -ssize_t SlirpCbSendPacket(const void* buf, size_t len, void* opaque) +ssize_t Net_Slirp::SlirpCbSendPacket(const void* buf, size_t len, void* opaque) noexcept { if (len > 2048) { @@ -87,7 +84,11 @@ ssize_t SlirpCbSendPacket(const void* buf, size_t len, void* opaque) Log(LogLevel::Debug, "slirp: response packet of %zu bytes, type %04X\n", len, ntohs(((u16*)buf)[6])); - Net::RXEnqueue(buf, len); + Net_Slirp& self = *static_cast(opaque); + if (self.Callback) + { + self.Callback((const u8*)buf, len); + } return len; } @@ -132,7 +133,7 @@ void SlirpCbNotify(void* opaque) Log(LogLevel::Debug, "Slirp: notify???\n"); } -SlirpCb cb = +const SlirpCb Net_Slirp::cb = { .send_packet = SlirpCbSendPacket, .guest_error = SlirpCbGuestError, @@ -145,11 +146,9 @@ SlirpCb cb = .notify = SlirpCbNotify }; -bool Init() +Net_Slirp::Net_Slirp(const Platform::SendPacketCallback& callback) noexcept : Callback(callback) { - IPv4ID = 0; - - SlirpConfig cfg; + SlirpConfig cfg {}; memset(&cfg, 0, sizeof(cfg)); cfg.version = 1; @@ -161,12 +160,67 @@ bool Init() *(u32*)&cfg.vdhcp_start = htonl(kClientIP); *(u32*)&cfg.vnameserver = htonl(kDNSIP); - Ctx = slirp_new(&cfg, &cb, nullptr); - - return true; + Ctx = slirp_new(&cfg, &cb, this); } -void DeInit() + +Net_Slirp::Net_Slirp(Net_Slirp&& other) noexcept +{ + RXBuffer = other.RXBuffer; + IPv4ID = other.IPv4ID; + Ctx = other.Ctx; + PollListSize = other.PollListSize; + Callback = std::move(other.Callback); + memcpy(PollList, other.PollList, sizeof(PollList)); + + other.RXBuffer = {}; + other.IPv4ID = 0; + other.Ctx = nullptr; + other.PollListSize = 0; + other.Callback = nullptr; + memset(other.PollList, 0, sizeof(other.PollList)); + + if (Ctx) + { + Ctx->opaque = this; + // Gotta ensure that the context doesn't try to pass around a dead object + } +} + +Net_Slirp& Net_Slirp::operator=(Net_Slirp&& other) noexcept +{ + if (this != &other) + { + if (Ctx) + { + slirp_cleanup(Ctx); + } + + RXBuffer = other.RXBuffer; + IPv4ID = other.IPv4ID; + Ctx = other.Ctx; + PollListSize = other.PollListSize; + Callback = std::move(other.Callback); + memcpy(PollList, other.PollList, sizeof(PollList)); + + other.RXBuffer = {}; + other.IPv4ID = 0; + other.Ctx = nullptr; + other.PollListSize = 0; + other.Callback = nullptr; + memset(other.PollList, 0, sizeof(other.PollList)); + + if (Ctx) + { + Ctx->opaque = this; + // Gotta ensure that the context doesn't try to pass around a dead object + } + } + + return *this; +} + +Net_Slirp::~Net_Slirp() noexcept { if (Ctx) { @@ -215,7 +269,7 @@ void FinishUDPFrame(u8* data, int len) *(u16*)&udpheader[6] = htons(tmp); } -void HandleDNSFrame(u8* data, int len) +void Net_Slirp::HandleDNSFrame(u8* data, int len) noexcept { u8* ipheader = &data[0xE]; u8* udpheader = &data[0x22]; @@ -368,10 +422,11 @@ void HandleDNSFrame(u8* data, int len) if (framelen & 1) { *out++ = 0; framelen++; } FinishUDPFrame(resp, framelen); - Net::RXEnqueue(resp, framelen); + if (Callback) + Callback(resp, framelen); } -int SendPacket(u8* data, int len) +int Net_Slirp::SendPacket(u8* data, int len) noexcept { if (!Ctx) return 0; @@ -401,19 +456,17 @@ int SendPacket(u8* data, int len) return len; } -const int PollListMax = 64; -struct pollfd PollList[PollListMax]; -int PollListSize; - -int SlirpCbAddPoll(int fd, int events, void* opaque) +int Net_Slirp::SlirpCbAddPoll(int fd, int events, void* opaque) noexcept { - if (PollListSize >= PollListMax) + Net_Slirp& self = *static_cast(opaque); + + if (self.PollListSize >= PollListMax) { Log(LogLevel::Error, "slirp: POLL LIST FULL\n"); return -1; } - int idx = PollListSize++; + int idx = self.PollListSize++; u16 evt = 0; @@ -427,18 +480,20 @@ int SlirpCbAddPoll(int fd, int events, void* opaque) if (events & SLIRP_POLL_HUP) evt |= POLLHUP; #endif // !__WIN32__ - PollList[idx].fd = fd; - PollList[idx].events = evt; + self.PollList[idx].fd = fd; + self.PollList[idx].events = evt; return idx; } -int SlirpCbGetREvents(int idx, void* opaque) +int Net_Slirp::SlirpCbGetREvents(int idx, void* opaque) noexcept { - if (idx < 0 || idx >= PollListSize) + Net_Slirp& self = *static_cast(opaque); + + if (idx < 0 || idx >= self.PollListSize) return 0; - u16 evt = PollList[idx].revents; + u16 evt = self.PollList[idx].revents; int ret = 0; if (evt & POLLIN) ret |= SLIRP_POLL_IN; @@ -450,7 +505,7 @@ int SlirpCbGetREvents(int idx, void* opaque) return ret; } -void RecvCheck() +void Net_Slirp::RecvCheck() noexcept { if (!Ctx) return; @@ -458,9 +513,9 @@ void RecvCheck() { u32 timeout = 0; PollListSize = 0; - slirp_pollfds_fill(Ctx, &timeout, SlirpCbAddPoll, nullptr); + slirp_pollfds_fill(Ctx, &timeout, SlirpCbAddPoll, this); int res = poll(PollList, PollListSize, timeout); - slirp_pollfds_poll(Ctx, res<0, SlirpCbGetREvents, nullptr); + slirp_pollfds_poll(Ctx, res<0, SlirpCbGetREvents, this); } } diff --git a/src/net/Net_Slirp.h b/src/net/Net_Slirp.h index 7446adf4..5f9b6587 100644 --- a/src/net/Net_Slirp.h +++ b/src/net/Net_Slirp.h @@ -20,17 +20,48 @@ #define NET_SLIRP_H #include "types.h" +#include "FIFO.h" +#include "Platform.h" +#include "NetDriver.h" -namespace Net_Slirp +#include + +#ifdef __WIN32__ + #include +#else + #include +#endif + +struct Slirp; + +namespace melonDS { -using namespace melonDS; +class Net_Slirp : public NetDriver +{ +public: + explicit Net_Slirp(const Platform::SendPacketCallback& callback) noexcept; + Net_Slirp(const Net_Slirp&) = delete; + Net_Slirp& operator=(const Net_Slirp&) = delete; + Net_Slirp(Net_Slirp&& other) noexcept; + Net_Slirp& operator=(Net_Slirp&& other) noexcept; + ~Net_Slirp() noexcept override; -bool Init(); -void DeInit(); - -int SendPacket(u8* data, int len); -void RecvCheck(); + int SendPacket(u8* data, int len) noexcept override; + void RecvCheck() noexcept override; +private: + static constexpr int PollListMax = 64; + static const SlirpCb cb; + static int SlirpCbGetREvents(int idx, void* opaque) noexcept; + static int SlirpCbAddPoll(int fd, int events, void* opaque) noexcept; + static ssize_t SlirpCbSendPacket(const void* buf, size_t len, void* opaque) noexcept; + void HandleDNSFrame(u8* data, int len) noexcept; + Platform::SendPacketCallback Callback; + pollfd PollList[PollListMax] {}; + int PollListSize = 0; + FIFO> 2)> RXBuffer {}; + u32 IPv4ID = 0; + Slirp* Ctx = nullptr; +}; } - #endif // NET_SLIRP_H diff --git a/src/net/PacketDispatcher.cpp b/src/net/PacketDispatcher.cpp index 61deed81..c9e0d274 100644 --- a/src/net/PacketDispatcher.cpp +++ b/src/net/PacketDispatcher.cpp @@ -34,7 +34,6 @@ const u32 kPacketMagic = 0x4B504C4D; PacketDispatcher::PacketDispatcher() : mutex(Platform::Mutex_Create()) { instanceMask = 0; - memset(packetQueues, 0, sizeof(packetQueues)); } PacketDispatcher::~PacketDispatcher() @@ -48,7 +47,7 @@ void PacketDispatcher::registerInstance(int inst) Mutex_Lock(mutex); instanceMask |= (1 << inst); - packetQueues[inst] = new PacketQueue(); + packetQueues[inst] = std::make_unique(); Mutex_Unlock(mutex); } @@ -58,7 +57,7 @@ void PacketDispatcher::unregisterInstance(int inst) Mutex_Lock(mutex); instanceMask &= ~(1 << inst); - delete packetQueues[inst]; + packetQueues[inst] = nullptr; Mutex_Unlock(mutex); } @@ -72,8 +71,7 @@ void PacketDispatcher::clear() if (!(instanceMask & (1 << i))) continue; - PacketQueue* queue = packetQueues[i]; - queue->Clear(); + packetQueues[i]->Clear(); } Mutex_Unlock(mutex); } @@ -105,7 +103,7 @@ void PacketDispatcher::sendPacket(const void* header, int headerlen, const void* if (!(recv_mask & (1 << i))) continue; - PacketQueue* queue = packetQueues[i]; + PacketQueue* queue = packetQueues[i].get(); // if we run out of space: discard old packets while (!queue->CanFit(totallen)) @@ -128,7 +126,7 @@ bool PacketDispatcher::recvPacket(void *header, int *headerlen, void *data, int if (receiver < 0 || receiver > 15) return false; Mutex_Lock(mutex); - PacketQueue* queue = packetQueues[receiver]; + PacketQueue* queue = packetQueues[receiver].get(); PacketHeader phdr; if (!queue->Read(&phdr, sizeof(phdr))) diff --git a/src/net/PacketDispatcher.h b/src/net/PacketDispatcher.h index 92df9941..d7f37c9a 100644 --- a/src/net/PacketDispatcher.h +++ b/src/net/PacketDispatcher.h @@ -19,6 +19,8 @@ #ifndef PACKETDISPATCHER_H #define PACKETDISPATCHER_H +#include +#include #include "Platform.h" #include "types.h" #include "FIFO.h" @@ -42,7 +44,7 @@ public: private: melonDS::Platform::Mutex* mutex; melonDS::u16 instanceMask; - PacketQueue* packetQueues[16]; + std::array, 16> packetQueues {}; }; #endif // PACKETDISPATCHER_H