Refactor network implementations to be more reusable and less buggy (#2107)
encapsulate network interfaces
This commit is contained in:
parent
c6bf5d5181
commit
327ce45124
|
@ -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<void(const u8* data, int len)>;
|
||||
|
||||
|
||||
// interface for camera emulation
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -37,11 +37,14 @@
|
|||
#define PCAP_NAME "libpcap"
|
||||
#endif
|
||||
|
||||
extern std::optional<melonDS::LibPCap> 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",
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#define WIFISETTINGSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <vector>
|
||||
#include "Net_PCap.h"
|
||||
|
||||
namespace Ui { class WifiSettingsDialog; }
|
||||
class WifiSettingsDialog;
|
||||
|
@ -68,6 +70,7 @@ private:
|
|||
bool haspcap;
|
||||
|
||||
void updateAdapterControls();
|
||||
std::vector<melonDS::AdapterData> adapters;
|
||||
};
|
||||
|
||||
#endif // WIFISETTINGSDIALOG_H
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<LibPCap> 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<Net_PCap> 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<Net_Slirp>([](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];
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<<i))
|
||||
SemPost(i);
|
||||
Semaphore_Post(SemPool[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int RecvPacketGeneric(int inst, u8* packet, bool block, u64* timestamp)
|
||||
int LocalMP::RecvPacketGeneric(int inst, u8* packet, bool block, u64* timestamp) noexcept
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (!SemWait(inst, block ? RecvTimeout : 0))
|
||||
if (!Semaphore_TryWait(SemPool[inst], block ? RecvTimeout : 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mutex_Lock(MPQueueLock);
|
||||
|
||||
MPPacketHeader pktheader;
|
||||
MPPacketHeader pktheader = {};
|
||||
FIFORead(inst, 0, &pktheader, sizeof(pktheader));
|
||||
|
||||
if (pktheader.Magic != 0x4946494E)
|
||||
{
|
||||
Log(LogLevel::Warn, "PACKET FIFO OVERFLOW\n");
|
||||
PacketReadOffset[inst] = MPStatus.PacketWriteOffset;
|
||||
SemReset(inst);
|
||||
Semaphore_Reset(SemPool[inst]);
|
||||
Mutex_Unlock(MPQueueLock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -331,33 +258,32 @@ int RecvPacketGeneric(int inst, u8* packet, bool block, u64* timestamp)
|
|||
}
|
||||
}
|
||||
|
||||
int SendPacket(int inst, u8* packet, int len, u64 timestamp)
|
||||
int LocalMP::SendPacket(int inst, u8* packet, int len, u64 timestamp)
|
||||
{
|
||||
return SendPacketGeneric(inst, 0, packet, len, timestamp);
|
||||
}
|
||||
|
||||
int RecvPacket(int inst, u8* packet, u64* timestamp)
|
||||
int LocalMP::RecvPacket(int inst, u8* packet, u64* timestamp)
|
||||
{
|
||||
return RecvPacketGeneric(inst, packet, false, timestamp);
|
||||
}
|
||||
|
||||
|
||||
int SendCmd(int inst, u8* packet, int len, u64 timestamp)
|
||||
int LocalMP::SendCmd(int inst, u8* packet, int len, u64 timestamp)
|
||||
{
|
||||
return SendPacketGeneric(inst, 1, packet, len, timestamp);
|
||||
}
|
||||
|
||||
int SendReply(int inst, u8* packet, int len, u64 timestamp, u16 aid)
|
||||
int LocalMP::SendReply(int inst, u8* packet, int len, u64 timestamp, u16 aid)
|
||||
{
|
||||
return SendPacketGeneric(inst, 2 | (aid<<16), packet, len, timestamp);
|
||||
}
|
||||
|
||||
int SendAck(int inst, u8* packet, int len, u64 timestamp)
|
||||
int LocalMP::SendAck(int inst, u8* packet, int len, u64 timestamp)
|
||||
{
|
||||
return SendPacketGeneric(inst, 3, packet, len, timestamp);
|
||||
}
|
||||
|
||||
int RecvHostPacket(int inst, u8* packet, u64* timestamp)
|
||||
int LocalMP::RecvHostPacket(int inst, u8* packet, u64* timestamp)
|
||||
{
|
||||
if (LastHostID != -1)
|
||||
{
|
||||
|
@ -372,7 +298,7 @@ int RecvHostPacket(int inst, u8* packet, u64* timestamp)
|
|||
return RecvPacketGeneric(inst, packet, true, timestamp);
|
||||
}
|
||||
|
||||
u16 RecvReplies(int inst, u8* packets, u64 timestamp, u16 aidmask)
|
||||
u16 LocalMP::RecvReplies(int inst, u8* packets, u64 timestamp, u16 aidmask)
|
||||
{
|
||||
u16 ret = 0;
|
||||
u16 myinstmask = (1 << inst);
|
||||
|
@ -386,7 +312,7 @@ u16 RecvReplies(int inst, u8* packets, u64 timestamp, u16 aidmask)
|
|||
|
||||
for (;;)
|
||||
{
|
||||
if (!SemWait(16+inst, RecvTimeout))
|
||||
if (!Semaphore_TryWait(SemPool[16+inst], RecvTimeout))
|
||||
{
|
||||
// no more replies available
|
||||
return ret;
|
||||
|
@ -394,14 +320,14 @@ u16 RecvReplies(int inst, u8* packets, u64 timestamp, u16 aidmask)
|
|||
|
||||
Mutex_Lock(MPQueueLock);
|
||||
|
||||
MPPacketHeader pktheader;
|
||||
MPPacketHeader pktheader = {};
|
||||
FIFORead(inst, 1, &pktheader, sizeof(pktheader));
|
||||
|
||||
if (pktheader.Magic != 0x4946494E)
|
||||
{
|
||||
Log(LogLevel::Warn, "REPLY FIFO OVERFLOW\n");
|
||||
ReplyReadOffset[inst] = MPStatus.ReplyWriteOffset;
|
||||
SemReset(16+inst);
|
||||
Semaphore_Reset(SemPool[16 + inst]);
|
||||
Mutex_Unlock(MPQueueLock);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -20,27 +20,72 @@
|
|||
#define LOCALMP_H
|
||||
|
||||
#include "types.h"
|
||||
#include "Platform.h"
|
||||
|
||||
namespace LocalMP
|
||||
namespace melonDS
|
||||
{
|
||||
struct MPStatusData
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
using namespace melonDS;
|
||||
bool Init();
|
||||
void DeInit();
|
||||
struct MPPacketHeader
|
||||
{
|
||||
u32 Magic;
|
||||
u32 SenderID;
|
||||
u32 Type; // 0=regular 1=CMD 2=reply 3=ack
|
||||
u32 Length;
|
||||
u64 Timestamp;
|
||||
};
|
||||
|
||||
void SetRecvTimeout(int timeout);
|
||||
constexpr u32 kPacketQueueSize = 0x10000;
|
||||
constexpr u32 kReplyQueueSize = 0x10000;
|
||||
constexpr u32 kMaxFrameSize = 0x948;
|
||||
|
||||
void Begin(int inst);
|
||||
void End(int inst);
|
||||
class LocalMP
|
||||
{
|
||||
public:
|
||||
LocalMP() noexcept;
|
||||
LocalMP(const LocalMP&) = delete;
|
||||
LocalMP& operator=(const LocalMP&) = delete;
|
||||
LocalMP(LocalMP&& other) = delete;
|
||||
LocalMP& operator=(LocalMP&& other) = delete;
|
||||
~LocalMP() noexcept;
|
||||
|
||||
int SendPacket(int inst, u8* data, int len, u64 timestamp);
|
||||
int RecvPacket(int inst, u8* data, u64* timestamp);
|
||||
int SendCmd(int inst, u8* data, int len, u64 timestamp);
|
||||
int SendReply(int inst, u8* data, int len, u64 timestamp, u16 aid);
|
||||
int SendAck(int inst, u8* data, int len, u64 timestamp);
|
||||
int RecvHostPacket(int inst, u8* data, u64* timestamp);
|
||||
u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask);
|
||||
[[nodiscard]] int GetRecvTimeout() const noexcept { return RecvTimeout; }
|
||||
void SetRecvTimeout(int timeout) noexcept { RecvTimeout = timeout; }
|
||||
|
||||
void Begin(int inst);
|
||||
void End(int inst);
|
||||
|
||||
int SendPacket(int inst, u8* data, int len, u64 timestamp);
|
||||
int RecvPacket(int inst, u8* data, u64* timestamp);
|
||||
int SendCmd(int inst, u8* data, int len, u64 timestamp);
|
||||
int SendReply(int inst, u8* data, int len, u64 timestamp, u16 aid);
|
||||
int SendAck(int inst, u8* data, int len, u64 timestamp);
|
||||
int RecvHostPacket(int inst, u8* data, u64* timestamp);
|
||||
u16 RecvReplies(int inst, u8* data, u64 timestamp, u16 aidmask);
|
||||
private:
|
||||
void FIFORead(int inst, int fifo, void* buf, int len) noexcept;
|
||||
void FIFOWrite(int inst, int fifo, void* buf, int len) noexcept;
|
||||
int SendPacketGeneric(int inst, u32 type, u8* packet, int len, u64 timestamp) noexcept;
|
||||
int RecvPacketGeneric(int inst, u8* packet, bool block, u64* timestamp) noexcept;
|
||||
Platform::Mutex* MPQueueLock;
|
||||
MPStatusData MPStatus {};
|
||||
u8 MPPacketQueue[kPacketQueueSize] {};
|
||||
u8 MPReplyQueue[kReplyQueueSize] {};
|
||||
u32 PacketReadOffset[16] {};
|
||||
u32 ReplyReadOffset[16] {};
|
||||
|
||||
int RecvTimeout = 25;
|
||||
|
||||
int LastHostID = -1;
|
||||
Platform::Semaphore* SemPool[32] {};
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LOCALMP_H
|
||||
|
|
|
@ -19,87 +19,46 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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))
|
||||
|
|
|
@ -19,26 +19,42 @@
|
|||
#ifndef NET_H
|
||||
#define NET_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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<NetDriver>&& driver) noexcept { Driver = std::move(driver); }
|
||||
[[nodiscard]] std::unique_ptr<NetDriver>& GetDriver() noexcept { return Driver; }
|
||||
[[nodiscard]] const std::unique_ptr<NetDriver>& GetDriver() const noexcept { return Driver; }
|
||||
|
||||
private:
|
||||
PacketDispatcher Dispatcher {};
|
||||
std::unique_ptr<NetDriver> Driver = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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> 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<Platform::DynamicLibrary>(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<AdapterData> 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<AdapterData> 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<Net_PCap> 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<Net_PCap> 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<Net_PCap> pcap = std::make_unique<Net_PCap>();
|
||||
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<Net_PCap*>(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<u_char*>(this));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,13 +19,20 @@
|
|||
#ifndef NET_PCAP_H
|
||||
#define NET_PCAP_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <pcap/pcap.h>
|
||||
|
||||
#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<LibPCap> 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<Net_PCap> Open(std::string_view devicename, const Platform::SendPacketCallback& handler) const noexcept;
|
||||
[[nodiscard]] std::unique_ptr<Net_PCap> 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<Platform::DynamicLibrary> 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<AdapterData> 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<Platform::DynamicLibrary> PCapLib = nullptr;
|
||||
pcap_close_t close = nullptr;
|
||||
pcap_sendpacket_t sendpacket = nullptr;
|
||||
pcap_dispatch_t dispatch = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,11 +19,17 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "Net.h"
|
||||
#include "Net_Slirp.h"
|
||||
#include "FIFO.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#include <libslirp.h>
|
||||
|
||||
// "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 <slirp.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
|
@ -33,9 +39,7 @@
|
|||
#include <time.h>
|
||||
#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<u32, (0x8000 >> 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<Net_Slirp*>(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<Net_Slirp*>(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<Net_Slirp*>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,17 +20,48 @@
|
|||
#define NET_SLIRP_H
|
||||
|
||||
#include "types.h"
|
||||
#include "FIFO.h"
|
||||
#include "Platform.h"
|
||||
#include "NetDriver.h"
|
||||
|
||||
namespace Net_Slirp
|
||||
#include <libslirp.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <poll.h>
|
||||
#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<u32, (0x8000 >> 2)> RXBuffer {};
|
||||
u32 IPv4ID = 0;
|
||||
Slirp* Ctx = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NET_SLIRP_H
|
||||
|
|
|
@ -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<PacketQueue>();
|
||||
|
||||
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)))
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef PACKETDISPATCHER_H
|
||||
#define PACKETDISPATCHER_H
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#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<std::unique_ptr<PacketQueue>, 16> packetQueues {};
|
||||
};
|
||||
|
||||
#endif // PACKETDISPATCHER_H
|
||||
|
|
Loading…
Reference in New Issue