Add tap-like fake Ethernet network interface for macOS
TunTap has recently become unmaintained, and it seems Apple wants developers to move away from kexts in general. TunTap currently takes some finagling to work on Catalina, and it may not work at all on Big Sur, necessitating a non-kext-based solution. Fortunately, fake Ethernet devices were introduced in Sierra and can be used similarly to tap adapters. This commit adds a new type of BBA interface implementation which uses fake Ethernet devices via tapserver (https://github.com/fuzziqersoftware/tapserver) to communicate with the host. This implementation was tested with PSO Episodes I & II, which can successfully connect to a private server running locally. This implementation is only available on macOS, since that's the only place it's needed - Windows/Linux/Unix are unaffected by TunTap being deprecated.
This commit is contained in:
parent
dbf7c40886
commit
a9486d087f
|
@ -623,6 +623,7 @@ if(WIN32)
|
|||
elseif(APPLE)
|
||||
target_sources(core PRIVATE
|
||||
HW/EXI/BBA/TAP_Apple.cpp
|
||||
HW/EXI/BBA/TAPServer_Apple.cpp
|
||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||
HW/WiimoteReal/IOdarwin.h
|
||||
HW/WiimoteReal/IOdarwin_private.h
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2020 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
// This interface is only implemented on macOS, since macOS needs a replacement
|
||||
// for TunTap when the kernel extension is no longer supported. This interface
|
||||
// only appears in the menu on macOS, so on other platforms, it does nothing and
|
||||
// refuses to activate.
|
||||
|
||||
constexpr char socket_path[] = "/tmp/dolphin-tap";
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::Activate()
|
||||
{
|
||||
if (IsActivated())
|
||||
return true;
|
||||
|
||||
sockaddr_un sun = {};
|
||||
if (sizeof(socket_path) > sizeof(sun.sun_path))
|
||||
{
|
||||
ERROR_LOG(SP1, "Socket path is too long, unable to init BBA");
|
||||
return false;
|
||||
}
|
||||
sun.sun_family = AF_UNIX;
|
||||
strcpy(sun.sun_path, socket_path);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
ERROR_LOG(SP1, "Couldn't create socket, unable to init BBA");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connect(fd, reinterpret_cast<sockaddr*>(&sun), sizeof(sun)) == -1)
|
||||
{
|
||||
ERROR_LOG(SP1, "Couldn't connect socket (%d), unable to init BBA", errno);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG(SP1, "BBA initialized.\n");
|
||||
return RecvInit();
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::SendFrame(const u8* frame, u32 size)
|
||||
{
|
||||
{
|
||||
const std::string s = ArrayToString(frame, size, 0x10);
|
||||
INFO_LOG(SP1, "SendFrame %x\n%s\n", size, s.c_str());
|
||||
}
|
||||
|
||||
auto size16 = u16(size);
|
||||
if (write(fd, &size16, 2) != 2)
|
||||
{
|
||||
ERROR_LOG(SP1, "SendFrame(): could not write size field\n");
|
||||
return false;
|
||||
}
|
||||
int written_bytes = write(fd, frame, size);
|
||||
if (u32(written_bytes) != size)
|
||||
{
|
||||
ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d", size,
|
||||
written_bytes);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eth_ref->SendComplete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIETHERNET::TAPServerNetworkInterface::ReadThreadHandler()
|
||||
{
|
||||
while (!readThreadShutdown.IsSet())
|
||||
{
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 50000;
|
||||
if (select(fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
|
||||
continue;
|
||||
|
||||
u16 size;
|
||||
if (read(fd, &size, 2) != 2)
|
||||
{
|
||||
ERROR_LOG(SP1, "Failed to read size field from BBA, err=%d", errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
int read_bytes = read(fd, m_eth_ref->mRecvBuffer.get(), size);
|
||||
if (read_bytes < 0)
|
||||
{
|
||||
ERROR_LOG(SP1, "Failed to read packet data from BBA, err=%d", errno);
|
||||
}
|
||||
else if (readEnabled.IsSet())
|
||||
{
|
||||
std::string data_string = ArrayToString(m_eth_ref->mRecvBuffer.get(), read_bytes, 0x10);
|
||||
INFO_LOG(SP1, "Read data: %s", data_string.c_str());
|
||||
m_eth_ref->mRecvBufferLength = read_bytes;
|
||||
m_eth_ref->RecvHandlePacket();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::RecvInit()
|
||||
{
|
||||
readThread = std::thread(&CEXIETHERNET::TAPServerNetworkInterface::ReadThreadHandler, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ExpansionInterface
|
|
@ -136,6 +136,12 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(const TEXIDevices device_type, cons
|
|||
result = std::make_unique<CEXIETHERNET>(BBADeviceType::TAP);
|
||||
break;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
case EXIDEVICE_ETHTAPSERVER:
|
||||
result = std::make_unique<CEXIETHERNET>(BBADeviceType::TAPSERVER);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case EXIDEVICE_ETHXLINK:
|
||||
result = std::make_unique<CEXIETHERNET>(BBADeviceType::XLINK);
|
||||
break;
|
||||
|
|
|
@ -33,6 +33,9 @@ enum TEXIDevices : int
|
|||
EXIDEVICE_MEMORYCARDFOLDER,
|
||||
EXIDEVICE_AGP,
|
||||
EXIDEVICE_ETHXLINK,
|
||||
#if defined(__APPLE__)
|
||||
EXIDEVICE_ETHTAPSERVER,
|
||||
#endif
|
||||
EXIDEVICE_NONE = 0xFF
|
||||
};
|
||||
|
||||
|
|
|
@ -48,6 +48,12 @@ CEXIETHERNET::CEXIETHERNET(BBADeviceType type)
|
|||
m_network_interface = std::make_unique<TAPNetworkInterface>(this);
|
||||
INFO_LOG_FMT(SP1, "Created TAP physical network interface.");
|
||||
break;
|
||||
#if defined(__APPLE__)
|
||||
case BBADeviceType::TAPSERVER:
|
||||
m_network_interface = std::make_unique<TAPServerNetworkInterface>(this);
|
||||
INFO_LOG(SP1, "Created tapserver physical network interface.");
|
||||
break;
|
||||
#endif
|
||||
case BBADeviceType::XLINK:
|
||||
// TODO start BBA with network link down, bring it up after "connected" response from XLink
|
||||
|
||||
|
|
|
@ -202,6 +202,9 @@ enum class BBADeviceType
|
|||
{
|
||||
TAP,
|
||||
XLINK,
|
||||
#if defined(__APPLE__)
|
||||
TAPSERVER,
|
||||
#endif
|
||||
};
|
||||
|
||||
class CEXIETHERNET : public IEXIDevice
|
||||
|
@ -337,7 +340,7 @@ private:
|
|||
void RecvStart() override;
|
||||
void RecvStop() override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
std::thread readThread;
|
||||
|
@ -356,6 +359,22 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
#if defined(__APPLE__)
|
||||
class TAPServerNetworkInterface : public TAPNetworkInterface
|
||||
{
|
||||
public:
|
||||
explicit TAPServerNetworkInterface(CEXIETHERNET* eth_ref) : TAPNetworkInterface(eth_ref) {}
|
||||
|
||||
public:
|
||||
bool Activate() override;
|
||||
bool SendFrame(const u8* frame, u32 size) override;
|
||||
bool RecvInit() override;
|
||||
|
||||
private:
|
||||
void ReadThreadHandler();
|
||||
};
|
||||
#endif
|
||||
|
||||
class XLinkNetworkInterface : public NetworkInterface
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -100,12 +100,16 @@ void GameCubePane::CreateWidgets()
|
|||
|
||||
// Add SP1 devices
|
||||
|
||||
for (const auto& entry :
|
||||
{std::make_pair(tr("<Nothing>"), ExpansionInterface::EXIDEVICE_NONE),
|
||||
std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY),
|
||||
std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH),
|
||||
std::make_pair(tr("Broadband Adapter (XLink Kai)"),
|
||||
ExpansionInterface::EXIDEVICE_ETHXLINK)})
|
||||
std::vector<std::pair<QString, ExpansionInterface::TEXIDevices>> sp1Entries{
|
||||
std::make_pair(tr("<Nothing>"), ExpansionInterface::EXIDEVICE_NONE),
|
||||
std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY),
|
||||
std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH),
|
||||
std::make_pair(tr("Broadband Adapter (XLink Kai)"), ExpansionInterface::EXIDEVICE_ETHXLINK)};
|
||||
#if defined(__APPLE__)
|
||||
sp1Entries.emplace_back(std::make_pair(tr("Broadband Adapter (tapserver)"),
|
||||
ExpansionInterface::EXIDEVICE_ETHTAPSERVER));
|
||||
#endif
|
||||
for (const auto& entry : sp1Entries)
|
||||
{
|
||||
m_slot_combos[2]->addItem(entry.first, entry.second);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue