mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-10 14:39:01 +01:00
Merge pull request #10690 from schthack/BBA-tapless
BBA: Added BuiltIn device that allow BBA emulation without the need o…
This commit is contained in:
commit
5a7759e359
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@ -16,6 +17,7 @@
|
|||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "Common/BitUtils.h"
|
||||||
#include "Common/Random.h"
|
#include "Common/Random.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
|
||||||
@ -113,6 +115,11 @@ u16 IPv4Header::Size() const
|
|||||||
return static_cast<u16>(SIZE);
|
return static_cast<u16>(SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8 IPv4Header::DefinedSize() const
|
||||||
|
{
|
||||||
|
return (version_ihl & 0xf) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
TCPHeader::TCPHeader() = default;
|
TCPHeader::TCPHeader() = default;
|
||||||
|
|
||||||
TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data,
|
TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data,
|
||||||
@ -141,6 +148,23 @@ TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, co
|
|||||||
checksum = htons(static_cast<u16>(tcp_checksum));
|
checksum = htons(static_cast<u16>(tcp_checksum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, u32 ack, u16 flags)
|
||||||
|
{
|
||||||
|
source_port = from.sin_port;
|
||||||
|
destination_port = to.sin_port;
|
||||||
|
sequence_number = htonl(seq);
|
||||||
|
acknowledgement_number = htonl(ack);
|
||||||
|
properties = htons(flags);
|
||||||
|
|
||||||
|
window_size = 0x7c;
|
||||||
|
checksum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 TCPHeader::GetHeaderSize() const
|
||||||
|
{
|
||||||
|
return (ntohs(properties) & 0xf000) >> 10;
|
||||||
|
}
|
||||||
|
|
||||||
u16 TCPHeader::Size() const
|
u16 TCPHeader::Size() const
|
||||||
{
|
{
|
||||||
return static_cast<u16>(SIZE);
|
return static_cast<u16>(SIZE);
|
||||||
@ -170,6 +194,92 @@ u8 UDPHeader::IPProto() const
|
|||||||
return static_cast<u8>(IPPROTO_UDP);
|
return static_cast<u8>(IPPROTO_UDP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARPHeader::ARPHeader() = default;
|
||||||
|
|
||||||
|
ARPHeader::ARPHeader(u32 from_ip, const MACAddress& from_mac, u32 to_ip, const MACAddress& to_mac)
|
||||||
|
{
|
||||||
|
hardware_type = htons(BBA_HARDWARE_TYPE);
|
||||||
|
protocol_type = IPV4_HEADER_TYPE;
|
||||||
|
hardware_size = MAC_ADDRESS_SIZE;
|
||||||
|
protocol_size = IPV4_ADDR_LEN;
|
||||||
|
opcode = 0x200;
|
||||||
|
sender_ip = from_ip;
|
||||||
|
target_ip = to_ip;
|
||||||
|
targer_address = to_mac;
|
||||||
|
sender_address = from_mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 ARPHeader::Size() const
|
||||||
|
{
|
||||||
|
return static_cast<u16>(SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
DHCPBody::DHCPBody() = default;
|
||||||
|
|
||||||
|
DHCPBody::DHCPBody(u32 transaction, const MACAddress& client_address, u32 new_ip, u32 serv_ip)
|
||||||
|
{
|
||||||
|
transaction_id = transaction;
|
||||||
|
message_type = DHCPConst::MESSAGE_REPLY;
|
||||||
|
hardware_type = BBA_HARDWARE_TYPE;
|
||||||
|
hardware_addr = MAC_ADDRESS_SIZE;
|
||||||
|
client_mac = client_address;
|
||||||
|
your_ip = new_ip;
|
||||||
|
server_ip = serv_ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
DHCPPacket::DHCPPacket() = default;
|
||||||
|
|
||||||
|
DHCPPacket::DHCPPacket(const std::vector<u8>& data)
|
||||||
|
{
|
||||||
|
if (data.size() < DHCPBody::SIZE)
|
||||||
|
return;
|
||||||
|
body = Common::BitCastPtr<DHCPBody>(data.data());
|
||||||
|
std::size_t offset = DHCPBody::SIZE;
|
||||||
|
|
||||||
|
while (offset < data.size() - 1)
|
||||||
|
{
|
||||||
|
const u8 fnc = data[offset];
|
||||||
|
if (fnc == 0)
|
||||||
|
{
|
||||||
|
++offset;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fnc == 255)
|
||||||
|
break;
|
||||||
|
const u8 len = data[offset + 1];
|
||||||
|
const auto opt_begin = data.begin() + offset;
|
||||||
|
offset += 2 + len;
|
||||||
|
if (offset > data.size())
|
||||||
|
break;
|
||||||
|
const auto opt_end = data.begin() + offset;
|
||||||
|
options.emplace_back(opt_begin, opt_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DHCPPacket::AddOption(u8 fnc, const std::vector<u8>& params)
|
||||||
|
{
|
||||||
|
if (params.size() > 255)
|
||||||
|
return;
|
||||||
|
std::vector<u8> opt = {fnc, u8(params.size())};
|
||||||
|
opt.insert(opt.end(), params.begin(), params.end());
|
||||||
|
options.emplace_back(std::move(opt));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> DHCPPacket::Build() const
|
||||||
|
{
|
||||||
|
const u8* body_ptr = reinterpret_cast<const u8*>(&body);
|
||||||
|
std::vector<u8> result(body_ptr, body_ptr + DHCPBody::SIZE);
|
||||||
|
|
||||||
|
for (auto& opt : options)
|
||||||
|
{
|
||||||
|
result.insert(result.end(), opt.begin(), opt.end());
|
||||||
|
}
|
||||||
|
const std::vector<u8> no_option = {255, 0, 0, 0};
|
||||||
|
result.insert(result.end(), no_option.begin(), no_option.end());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the network checksum with a 32-bit accumulator using the
|
// Compute the network checksum with a 32-bit accumulator using the
|
||||||
// "Normal" order, see RFC 1071 for more details.
|
// "Normal" order, see RFC 1071 for more details.
|
||||||
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
|
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
|
||||||
@ -187,6 +297,257 @@ u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
|
|||||||
return ~static_cast<u16>(checksum);
|
return ~static_cast<u16>(checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compute the TCP checksum with its pseudo header
|
||||||
|
u16 ComputeTCPNetworkChecksum(const IPAddress& from, const IPAddress& to, const void* data,
|
||||||
|
u16 length, u8 protocol)
|
||||||
|
{
|
||||||
|
const u32 source_addr = ntohl(Common::BitCast<u32>(from));
|
||||||
|
const u32 destination_addr = ntohl(Common::BitCast<u32>(to));
|
||||||
|
const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) +
|
||||||
|
(destination_addr >> 16) + (destination_addr & 0xFFFF) + protocol +
|
||||||
|
length;
|
||||||
|
const u32 tcp_checksum = ComputeNetworkChecksum(data, length, initial_value);
|
||||||
|
return htons(static_cast<u16>(tcp_checksum));
|
||||||
|
}
|
||||||
|
|
||||||
|
ARPPacket::ARPPacket() = default;
|
||||||
|
|
||||||
|
u16 ARPPacket::Size() const
|
||||||
|
{
|
||||||
|
return static_cast<u16>(SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARPPacket::ARPPacket(const MACAddress& destination, const MACAddress& source)
|
||||||
|
{
|
||||||
|
eth_header.destination = destination;
|
||||||
|
eth_header.source = source;
|
||||||
|
eth_header.ethertype = htons(ARP_ETHERTYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> ARPPacket::Build() const
|
||||||
|
{
|
||||||
|
std::vector<u8> result;
|
||||||
|
result.reserve(EthernetHeader::SIZE + ARPHeader::SIZE);
|
||||||
|
const u8* eth_ptr = reinterpret_cast<const u8*>(ð_header);
|
||||||
|
result.insert(result.end(), eth_ptr, eth_ptr + EthernetHeader::SIZE);
|
||||||
|
const u8* arp_ptr = reinterpret_cast<const u8*>(&arp_header);
|
||||||
|
result.insert(result.end(), arp_ptr, arp_ptr + ARPHeader::SIZE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPPacket::TCPPacket() = default;
|
||||||
|
|
||||||
|
TCPPacket::TCPPacket(const MACAddress& destination, const MACAddress& source)
|
||||||
|
{
|
||||||
|
eth_header.destination = destination;
|
||||||
|
eth_header.source = source;
|
||||||
|
eth_header.ethertype = htons(IPV4_ETHERTYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPPacket::TCPPacket(const MACAddress& destination, const MACAddress& source,
|
||||||
|
const sockaddr_in& from, const sockaddr_in& to, u32 seq, u32 ack, u16 flags)
|
||||||
|
{
|
||||||
|
eth_header.destination = destination;
|
||||||
|
eth_header.source = source;
|
||||||
|
eth_header.ethertype = htons(IPV4_ETHERTYPE);
|
||||||
|
|
||||||
|
ip_header = Common::IPv4Header(Common::TCPHeader::SIZE, IPPROTO_TCP, from, to);
|
||||||
|
tcp_header = Common::TCPHeader(from, to, seq, ack, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> TCPPacket::Build()
|
||||||
|
{
|
||||||
|
std::vector<u8> result;
|
||||||
|
result.reserve(Size());
|
||||||
|
|
||||||
|
// recalc size
|
||||||
|
ip_header.total_len = htons(static_cast<u16>(IPv4Header::SIZE + ipv4_options.size() +
|
||||||
|
TCPHeader::SIZE + tcp_options.size() + data.size()));
|
||||||
|
|
||||||
|
// copy data
|
||||||
|
const u8* eth_ptr = reinterpret_cast<const u8*>(ð_header);
|
||||||
|
result.insert(result.end(), eth_ptr, eth_ptr + EthernetHeader::SIZE);
|
||||||
|
const u8* ip_ptr = reinterpret_cast<const u8*>(&ip_header);
|
||||||
|
result.insert(result.end(), ip_ptr, ip_ptr + IPv4Header::SIZE);
|
||||||
|
std::size_t offset = EthernetHeader::SIZE + IPv4Header::SIZE;
|
||||||
|
if (ipv4_options.size() > 0)
|
||||||
|
{
|
||||||
|
result.insert(result.end(), ipv4_options.begin(), ipv4_options.end());
|
||||||
|
offset += ipv4_options.size();
|
||||||
|
}
|
||||||
|
tcp_header.checksum = 0;
|
||||||
|
const u16 props = (ntohs(tcp_header.properties) & 0xfff) |
|
||||||
|
(static_cast<u16>((tcp_options.size() + TCPHeader::SIZE) & 0x3c) << 10);
|
||||||
|
tcp_header.properties = htons(props);
|
||||||
|
const u8* tcp_ptr = reinterpret_cast<const u8*>(&tcp_header);
|
||||||
|
result.insert(result.end(), tcp_ptr, tcp_ptr + TCPHeader::SIZE);
|
||||||
|
const std::size_t tcp_offset = offset;
|
||||||
|
offset += TCPHeader::SIZE;
|
||||||
|
if (tcp_options.size() > 0)
|
||||||
|
{
|
||||||
|
result.insert(result.end(), tcp_options.begin(), tcp_options.end());
|
||||||
|
offset += tcp_options.size();
|
||||||
|
}
|
||||||
|
if (data.size() > 0)
|
||||||
|
{
|
||||||
|
result.insert(result.end(), data.begin(), data.end());
|
||||||
|
}
|
||||||
|
tcp_header.checksum = ComputeTCPNetworkChecksum(
|
||||||
|
ip_header.source_addr, ip_header.destination_addr, &result[tcp_offset],
|
||||||
|
static_cast<u16>(result.size() - tcp_offset), IPPROTO_TCP);
|
||||||
|
std::copy(tcp_ptr, tcp_ptr + TCPHeader::SIZE, result.begin() + tcp_offset);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 TCPPacket::Size() const
|
||||||
|
{
|
||||||
|
return static_cast<u16>(MIN_SIZE + data.size() + ipv4_options.size() + tcp_options.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
UDPPacket::UDPPacket() = default;
|
||||||
|
|
||||||
|
UDPPacket::UDPPacket(const MACAddress& destination, const MACAddress& source)
|
||||||
|
{
|
||||||
|
eth_header.destination = destination;
|
||||||
|
eth_header.source = source;
|
||||||
|
eth_header.ethertype = htons(IPV4_ETHERTYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
UDPPacket::UDPPacket(const MACAddress& destination, const MACAddress& source,
|
||||||
|
const sockaddr_in& from, const sockaddr_in& to, const std::vector<u8>& payload)
|
||||||
|
{
|
||||||
|
eth_header.destination = destination;
|
||||||
|
eth_header.source = source;
|
||||||
|
eth_header.ethertype = htons(IPV4_ETHERTYPE);
|
||||||
|
|
||||||
|
ip_header = Common::IPv4Header(static_cast<u16>(payload.size() + Common::UDPHeader::SIZE),
|
||||||
|
IPPROTO_UDP, from, to);
|
||||||
|
udp_header = Common::UDPHeader(from, to, static_cast<u16>(payload.size()));
|
||||||
|
data = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> UDPPacket::Build()
|
||||||
|
{
|
||||||
|
std::vector<u8> result;
|
||||||
|
result.reserve(Size());
|
||||||
|
|
||||||
|
// recalc size
|
||||||
|
ip_header.total_len = htons(
|
||||||
|
static_cast<u16>(IPv4Header::SIZE + ipv4_options.size() + UDPHeader::SIZE + data.size()));
|
||||||
|
udp_header.length = htons(static_cast<u16>(UDPHeader::SIZE + data.size()));
|
||||||
|
|
||||||
|
// copy data
|
||||||
|
const u8* eth_ptr = reinterpret_cast<const u8*>(ð_header);
|
||||||
|
result.insert(result.end(), eth_ptr, eth_ptr + EthernetHeader::SIZE);
|
||||||
|
const u8* ip_ptr = reinterpret_cast<const u8*>(&ip_header);
|
||||||
|
result.insert(result.end(), ip_ptr, ip_ptr + IPv4Header::SIZE);
|
||||||
|
std::size_t offset = EthernetHeader::SIZE + IPv4Header::SIZE;
|
||||||
|
if (ipv4_options.size() > 0)
|
||||||
|
{
|
||||||
|
result.insert(result.end(), ipv4_options.begin(), ipv4_options.end());
|
||||||
|
offset += ipv4_options.size();
|
||||||
|
}
|
||||||
|
udp_header.checksum = 0;
|
||||||
|
const u8* udp_ptr = reinterpret_cast<const u8*>(&udp_header);
|
||||||
|
result.insert(result.end(), udp_ptr, udp_ptr + UDPHeader::SIZE);
|
||||||
|
const std::size_t udp_offset = offset;
|
||||||
|
offset += UDPHeader::SIZE;
|
||||||
|
if (data.size() > 0)
|
||||||
|
{
|
||||||
|
result.insert(result.end(), data.begin(), data.end());
|
||||||
|
}
|
||||||
|
udp_header.checksum = ComputeTCPNetworkChecksum(
|
||||||
|
ip_header.source_addr, ip_header.destination_addr, &result[udp_offset],
|
||||||
|
static_cast<u16>(result.size() - udp_offset), IPPROTO_UDP);
|
||||||
|
std::copy(udp_ptr, udp_ptr + UDPHeader::SIZE, result.begin() + udp_offset);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 UDPPacket::Size() const
|
||||||
|
{
|
||||||
|
return static_cast<u16>(MIN_SIZE + data.size() + ipv4_options.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketView::PacketView(const u8* ptr, std::size_t size) : m_ptr(ptr), m_size(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u16> PacketView::GetEtherType() const
|
||||||
|
{
|
||||||
|
if (m_size < EthernetHeader::SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
const std::size_t offset = offsetof(EthernetHeader, ethertype);
|
||||||
|
return ntohs(Common::BitCastPtr<u16>(m_ptr + offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ARPPacket> PacketView::GetARPPacket() const
|
||||||
|
{
|
||||||
|
if (m_size < ARPPacket::SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
return Common::BitCastPtr<ARPPacket>(m_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> PacketView::GetIPProto() const
|
||||||
|
{
|
||||||
|
if (m_size < EthernetHeader::SIZE + IPv4Header::SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
return m_ptr[EthernetHeader::SIZE + offsetof(IPv4Header, protocol)];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TCPPacket> PacketView::GetTCPPacket() const
|
||||||
|
{
|
||||||
|
if (m_size < TCPPacket::MIN_SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
TCPPacket result;
|
||||||
|
result.eth_header = Common::BitCastPtr<EthernetHeader>(m_ptr);
|
||||||
|
result.ip_header = Common::BitCastPtr<IPv4Header>(m_ptr + EthernetHeader::SIZE);
|
||||||
|
const u16 offset = result.ip_header.DefinedSize() + EthernetHeader::SIZE;
|
||||||
|
if (m_size < offset + TCPHeader::SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
result.ipv4_options =
|
||||||
|
std::vector<u8>(m_ptr + EthernetHeader::SIZE + IPv4Header::SIZE, m_ptr + offset);
|
||||||
|
result.tcp_header = Common::BitCastPtr<TCPHeader>(m_ptr + offset);
|
||||||
|
const u16 data_offset = result.tcp_header.GetHeaderSize() + offset;
|
||||||
|
|
||||||
|
const u16 total_len = ntohs(result.ip_header.total_len);
|
||||||
|
const std::size_t end = EthernetHeader::SIZE + total_len;
|
||||||
|
|
||||||
|
if (m_size < end || end < data_offset)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
result.tcp_options = std::vector<u8>(m_ptr + offset + TCPHeader::SIZE, m_ptr + data_offset);
|
||||||
|
result.data = std::vector<u8>(m_ptr + data_offset, m_ptr + end);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<UDPPacket> PacketView::GetUDPPacket() const
|
||||||
|
{
|
||||||
|
if (m_size < UDPPacket::MIN_SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
UDPPacket result;
|
||||||
|
result.eth_header = Common::BitCastPtr<EthernetHeader>(m_ptr);
|
||||||
|
result.ip_header = Common::BitCastPtr<IPv4Header>(m_ptr + EthernetHeader::SIZE);
|
||||||
|
const u16 offset = result.ip_header.DefinedSize() + EthernetHeader::SIZE;
|
||||||
|
if (m_size < offset + UDPHeader::SIZE)
|
||||||
|
return std::nullopt;
|
||||||
|
result.ipv4_options =
|
||||||
|
std::vector<u8>(m_ptr + EthernetHeader::SIZE + IPv4Header::SIZE, m_ptr + offset);
|
||||||
|
result.udp_header = Common::BitCastPtr<UDPHeader>(m_ptr + offset);
|
||||||
|
const u16 data_offset = UDPHeader::SIZE + offset;
|
||||||
|
|
||||||
|
const u16 total_len = ntohs(result.udp_header.length);
|
||||||
|
const std::size_t end = offset + total_len;
|
||||||
|
|
||||||
|
if (m_size < end || end < data_offset)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
result.data = std::vector<u8>(m_ptr + data_offset, m_ptr + end);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
NetworkErrorState SaveNetworkErrorState()
|
NetworkErrorState SaveNetworkErrorState()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
@ -22,11 +23,25 @@ enum class MACConsumer
|
|||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
MAC_ADDRESS_SIZE = 6
|
BBA_HARDWARE_TYPE = 1,
|
||||||
|
MAC_ADDRESS_SIZE = 6,
|
||||||
|
IPV4_HEADER_TYPE = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DHCPConst
|
||||||
|
{
|
||||||
|
MESSAGE_QUERY = 1,
|
||||||
|
MESSAGE_REPLY = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
using MACAddress = std::array<u8, MAC_ADDRESS_SIZE>;
|
using MACAddress = std::array<u8, MAC_ADDRESS_SIZE>;
|
||||||
constexpr std::size_t IPV4_ADDR_LEN = 4;
|
constexpr std::size_t IPV4_ADDR_LEN = 4;
|
||||||
|
using IPAddress = std::array<u8, IPV4_ADDR_LEN>;
|
||||||
|
constexpr IPAddress IP_ADDR_ANY = {0, 0, 0, 0};
|
||||||
|
constexpr IPAddress IP_ADDR_BROADCAST = {255, 255, 255, 255};
|
||||||
|
constexpr IPAddress IP_ADDR_SSDP = {239, 255, 255, 250};
|
||||||
|
constexpr u16 IPV4_ETHERTYPE = 0x800;
|
||||||
|
constexpr u16 ARP_ETHERTYPE = 0x806;
|
||||||
|
|
||||||
struct EthernetHeader
|
struct EthernetHeader
|
||||||
{
|
{
|
||||||
@ -47,6 +62,7 @@ struct IPv4Header
|
|||||||
IPv4Header();
|
IPv4Header();
|
||||||
IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to);
|
IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to);
|
||||||
u16 Size() const;
|
u16 Size() const;
|
||||||
|
u8 DefinedSize() const;
|
||||||
|
|
||||||
static constexpr std::size_t SIZE = 20;
|
static constexpr std::size_t SIZE = 20;
|
||||||
|
|
||||||
@ -58,8 +74,8 @@ struct IPv4Header
|
|||||||
u8 ttl = 0;
|
u8 ttl = 0;
|
||||||
u8 protocol = 0;
|
u8 protocol = 0;
|
||||||
u16 header_checksum = 0;
|
u16 header_checksum = 0;
|
||||||
std::array<u8, IPV4_ADDR_LEN> source_addr{};
|
IPAddress source_addr{};
|
||||||
std::array<u8, IPV4_ADDR_LEN> destination_addr{};
|
IPAddress destination_addr{};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(IPv4Header) == IPv4Header::SIZE);
|
static_assert(sizeof(IPv4Header) == IPv4Header::SIZE);
|
||||||
|
|
||||||
@ -67,6 +83,8 @@ struct TCPHeader
|
|||||||
{
|
{
|
||||||
TCPHeader();
|
TCPHeader();
|
||||||
TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, u16 length);
|
TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, u16 length);
|
||||||
|
TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, u32 ack, u16 flags);
|
||||||
|
u8 GetHeaderSize() const;
|
||||||
u16 Size() const;
|
u16 Size() const;
|
||||||
u8 IPProto() const;
|
u8 IPProto() const;
|
||||||
|
|
||||||
@ -99,6 +117,134 @@ struct UDPHeader
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(UDPHeader) == UDPHeader::SIZE);
|
static_assert(sizeof(UDPHeader) == UDPHeader::SIZE);
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct ARPHeader
|
||||||
|
{
|
||||||
|
ARPHeader();
|
||||||
|
ARPHeader(u32 from_ip, const MACAddress& from_mac, u32 to_ip, const MACAddress& to_mac);
|
||||||
|
u16 Size() const;
|
||||||
|
|
||||||
|
static constexpr std::size_t SIZE = 28;
|
||||||
|
|
||||||
|
u16 hardware_type = 0;
|
||||||
|
u16 protocol_type = 0;
|
||||||
|
u8 hardware_size = 0;
|
||||||
|
u8 protocol_size = 0;
|
||||||
|
u16 opcode = 0;
|
||||||
|
MACAddress sender_address{};
|
||||||
|
u32 sender_ip = 0;
|
||||||
|
MACAddress targer_address{};
|
||||||
|
u32 target_ip = 0;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ARPHeader) == ARPHeader::SIZE);
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct DHCPBody
|
||||||
|
{
|
||||||
|
DHCPBody();
|
||||||
|
DHCPBody(u32 transaction, const MACAddress& client_address, u32 new_ip, u32 serv_ip);
|
||||||
|
static constexpr std::size_t SIZE = 240;
|
||||||
|
u8 message_type = 0;
|
||||||
|
u8 hardware_type = 0;
|
||||||
|
u8 hardware_addr = 0;
|
||||||
|
u8 hops = 0;
|
||||||
|
u32 transaction_id = 0;
|
||||||
|
u16 seconds = 0;
|
||||||
|
u16 boot_flag = 0;
|
||||||
|
u32 client_ip = 0;
|
||||||
|
u32 your_ip = 0;
|
||||||
|
u32 server_ip = 0;
|
||||||
|
u32 relay_ip = 0;
|
||||||
|
MACAddress client_mac{};
|
||||||
|
unsigned char padding[10]{};
|
||||||
|
unsigned char hostname[0x40]{};
|
||||||
|
unsigned char boot_file[0x80]{};
|
||||||
|
u8 magic_cookie[4] = {0x63, 0x82, 0x53, 0x63};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DHCPBody) == DHCPBody::SIZE);
|
||||||
|
|
||||||
|
struct DHCPPacket
|
||||||
|
{
|
||||||
|
DHCPPacket();
|
||||||
|
DHCPPacket(const std::vector<u8>& data);
|
||||||
|
void AddOption(u8 fnc, const std::vector<u8>& params);
|
||||||
|
std::vector<u8> Build() const;
|
||||||
|
|
||||||
|
DHCPBody body;
|
||||||
|
std::vector<std::vector<u8>> options;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The compiler might add 2 bytes after EthernetHeader to enforce 16-bytes alignment
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct ARPPacket
|
||||||
|
{
|
||||||
|
ARPPacket();
|
||||||
|
ARPPacket(const MACAddress& destination, const MACAddress& source);
|
||||||
|
std::vector<u8> Build() const;
|
||||||
|
u16 Size() const;
|
||||||
|
|
||||||
|
EthernetHeader eth_header;
|
||||||
|
ARPHeader arp_header;
|
||||||
|
|
||||||
|
static constexpr std::size_t SIZE = EthernetHeader::SIZE + ARPHeader::SIZE;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ARPPacket) == ARPPacket::SIZE);
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct TCPPacket
|
||||||
|
{
|
||||||
|
TCPPacket();
|
||||||
|
TCPPacket(const MACAddress& destination, const MACAddress& source);
|
||||||
|
TCPPacket(const MACAddress& destination, const MACAddress& source, const sockaddr_in& from,
|
||||||
|
const sockaddr_in& to, u32 seq, u32 ack, u16 flags);
|
||||||
|
std::vector<u8> Build();
|
||||||
|
u16 Size() const;
|
||||||
|
|
||||||
|
EthernetHeader eth_header;
|
||||||
|
IPv4Header ip_header;
|
||||||
|
TCPHeader tcp_header;
|
||||||
|
std::vector<u8> ipv4_options;
|
||||||
|
std::vector<u8> tcp_options;
|
||||||
|
std::vector<u8> data;
|
||||||
|
|
||||||
|
static constexpr std::size_t MIN_SIZE = EthernetHeader::SIZE + IPv4Header::SIZE + TCPHeader::SIZE;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UDPPacket
|
||||||
|
{
|
||||||
|
UDPPacket();
|
||||||
|
UDPPacket(const MACAddress& destination, const MACAddress& source);
|
||||||
|
UDPPacket(const MACAddress& destination, const MACAddress& source, const sockaddr_in& from,
|
||||||
|
const sockaddr_in& to, const std::vector<u8>& payload);
|
||||||
|
std::vector<u8> Build();
|
||||||
|
u16 Size() const;
|
||||||
|
|
||||||
|
EthernetHeader eth_header;
|
||||||
|
IPv4Header ip_header;
|
||||||
|
UDPHeader udp_header;
|
||||||
|
|
||||||
|
std::vector<u8> ipv4_options;
|
||||||
|
std::vector<u8> data;
|
||||||
|
|
||||||
|
static constexpr std::size_t MIN_SIZE = EthernetHeader::SIZE + IPv4Header::SIZE + UDPHeader::SIZE;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PacketView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PacketView(const u8* ptr, std::size_t size);
|
||||||
|
|
||||||
|
std::optional<u16> GetEtherType() const;
|
||||||
|
std::optional<ARPPacket> GetARPPacket() const;
|
||||||
|
std::optional<u8> GetIPProto() const;
|
||||||
|
std::optional<TCPPacket> GetTCPPacket() const;
|
||||||
|
std::optional<UDPPacket> GetUDPPacket() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const u8* m_ptr;
|
||||||
|
std::size_t m_size;
|
||||||
|
};
|
||||||
|
|
||||||
struct NetworkErrorState
|
struct NetworkErrorState
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
@ -111,6 +257,8 @@ MACAddress GenerateMacAddress(MACConsumer type);
|
|||||||
std::string MacAddressToString(const MACAddress& mac);
|
std::string MacAddressToString(const MACAddress& mac);
|
||||||
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
|
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
|
||||||
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0);
|
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0);
|
||||||
|
u16 ComputeTCPNetworkChecksum(const IPAddress& from, const IPAddress& to, const void* data,
|
||||||
|
u16 length, u8 protocol);
|
||||||
NetworkErrorState SaveNetworkErrorState();
|
NetworkErrorState SaveNetworkErrorState();
|
||||||
void RestoreNetworkErrorState(const NetworkErrorState& state);
|
void RestoreNetworkErrorState(const NetworkErrorState& state);
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
@ -647,6 +647,8 @@ if(WIN32)
|
|||||||
HW/EXI/BBA/TAP_Win32.cpp
|
HW/EXI/BBA/TAP_Win32.cpp
|
||||||
HW/EXI/BBA/TAP_Win32.h
|
HW/EXI/BBA/TAP_Win32.h
|
||||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||||
|
HW/EXI/BBA/BuiltIn.cpp
|
||||||
|
HW/EXI/BBA/BuiltIn.h
|
||||||
HW/WiimoteReal/IOWin.cpp
|
HW/WiimoteReal/IOWin.cpp
|
||||||
HW/WiimoteReal/IOWin.h
|
HW/WiimoteReal/IOWin.h
|
||||||
)
|
)
|
||||||
@ -662,12 +664,16 @@ elseif(APPLE)
|
|||||||
HW/EXI/BBA/TAP_Apple.cpp
|
HW/EXI/BBA/TAP_Apple.cpp
|
||||||
HW/EXI/BBA/TAPServer_Apple.cpp
|
HW/EXI/BBA/TAPServer_Apple.cpp
|
||||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||||
|
HW/EXI/BBA/BuiltIn.cpp
|
||||||
|
HW/EXI/BBA/BuiltIn.h
|
||||||
)
|
)
|
||||||
target_link_libraries(core PUBLIC ${IOB_LIBRARY})
|
target_link_libraries(core PUBLIC ${IOB_LIBRARY})
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
target_sources(core PRIVATE
|
target_sources(core PRIVATE
|
||||||
HW/EXI/BBA/TAP_Unix.cpp
|
HW/EXI/BBA/TAP_Unix.cpp
|
||||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||||
|
HW/EXI/BBA/BuiltIn.cpp
|
||||||
|
HW/EXI/BBA/BuiltIn.h
|
||||||
)
|
)
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
target_sources(core PRIVATE
|
target_sources(core PRIVATE
|
||||||
|
@ -116,6 +116,11 @@ const Info<std::string> MAIN_BBA_MAC{{System::Main, "Core", "BBA_MAC"}, ""};
|
|||||||
const Info<std::string> MAIN_BBA_XLINK_IP{{System::Main, "Core", "BBA_XLINK_IP"}, "127.0.0.1"};
|
const Info<std::string> MAIN_BBA_XLINK_IP{{System::Main, "Core", "BBA_XLINK_IP"}, "127.0.0.1"};
|
||||||
const Info<bool> MAIN_BBA_XLINK_CHAT_OSD{{System::Main, "Core", "BBA_XLINK_CHAT_OSD"}, true};
|
const Info<bool> MAIN_BBA_XLINK_CHAT_OSD{{System::Main, "Core", "BBA_XLINK_CHAT_OSD"}, true};
|
||||||
|
|
||||||
|
// Schthack PSO Server - https://schtserv.com/
|
||||||
|
const Info<std::string> MAIN_BBA_BUILTIN_DNS{{System::Main, "Core", "BBA_BUILTIN_DNS"},
|
||||||
|
"149.56.167.128"};
|
||||||
|
const Info<std::string> MAIN_BBA_BUILTIN_IP{{System::Main, "Core", "BBA_BUILTIN_IP"}, ""};
|
||||||
|
|
||||||
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel)
|
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel)
|
||||||
{
|
{
|
||||||
static const std::array<const Info<SerialInterface::SIDevices>, 4> infos{
|
static const std::array<const Info<SerialInterface::SIDevices>, 4> infos{
|
||||||
|
@ -86,6 +86,8 @@ const Info<ExpansionInterface::EXIDeviceType>& GetInfoForEXIDevice(ExpansionInte
|
|||||||
extern const Info<std::string> MAIN_BBA_MAC;
|
extern const Info<std::string> MAIN_BBA_MAC;
|
||||||
extern const Info<std::string> MAIN_BBA_XLINK_IP;
|
extern const Info<std::string> MAIN_BBA_XLINK_IP;
|
||||||
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
|
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
|
||||||
|
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
|
||||||
|
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
|
||||||
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
|
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
|
||||||
const Info<bool>& GetInfoForAdapterRumble(int channel);
|
const Info<bool>& GetInfoForAdapterRumble(int channel);
|
||||||
const Info<bool>& GetInfoForSimulateKonga(int channel);
|
const Info<bool>& GetInfoForSimulateKonga(int channel);
|
||||||
|
@ -81,6 +81,8 @@ bool IsSettingSaveable(const Config::Location& config_location)
|
|||||||
&Config::MAIN_AGP_CART_B_PATH.GetLocation(),
|
&Config::MAIN_AGP_CART_B_PATH.GetLocation(),
|
||||||
&Config::MAIN_BBA_MAC.GetLocation(),
|
&Config::MAIN_BBA_MAC.GetLocation(),
|
||||||
&Config::MAIN_BBA_XLINK_IP.GetLocation(),
|
&Config::MAIN_BBA_XLINK_IP.GetLocation(),
|
||||||
|
&Config::MAIN_BBA_BUILTIN_DNS.GetLocation(),
|
||||||
|
&Config::MAIN_BBA_BUILTIN_IP.GetLocation(),
|
||||||
&Config::MAIN_BBA_XLINK_CHAT_OSD.GetLocation(),
|
&Config::MAIN_BBA_XLINK_CHAT_OSD.GetLocation(),
|
||||||
&Config::MAIN_OVERRIDE_REGION_SETTINGS.GetLocation(),
|
&Config::MAIN_OVERRIDE_REGION_SETTINGS.GetLocation(),
|
||||||
&Config::MAIN_CUSTOM_RTC_ENABLE.GetLocation(),
|
&Config::MAIN_CUSTOM_RTC_ENABLE.GetLocation(),
|
||||||
|
687
Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp
Normal file
687
Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp
Normal file
@ -0,0 +1,687 @@
|
|||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <SFML/Network.hpp>
|
||||||
|
|
||||||
|
#include "Common/BitUtils.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Common/MsgHandler.h"
|
||||||
|
#include "Core/HW/EXI/BBA/BuiltIn.h"
|
||||||
|
#include "Core/HW/EXI/EXI_Device.h"
|
||||||
|
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||||
|
|
||||||
|
namespace ExpansionInterface
|
||||||
|
{
|
||||||
|
u64 GetTickCountStd()
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::BuiltInBBAInterface::Activate()
|
||||||
|
{
|
||||||
|
if (IsActivated())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_active = true;
|
||||||
|
for (auto& buf : m_queue_data)
|
||||||
|
buf.reserve(2048);
|
||||||
|
m_fake_mac = Common::GenerateMacAddress(Common::MACConsumer::BBA);
|
||||||
|
|
||||||
|
// Workaround to get the host IP (might not be accurate)
|
||||||
|
const u32 ip = m_local_ip.empty() ? sf::IpAddress::getLocalAddress().toInteger() :
|
||||||
|
sf::IpAddress(m_local_ip).toInteger();
|
||||||
|
m_current_ip = htonl(ip);
|
||||||
|
m_router_ip = (m_current_ip & 0xFFFFFF) | 0x01000000;
|
||||||
|
|
||||||
|
// clear all ref
|
||||||
|
for (auto& ref : network_ref)
|
||||||
|
{
|
||||||
|
ref.ip = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecvInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
|
||||||
|
{
|
||||||
|
// Is the BBA Active? If not skip shutdown
|
||||||
|
if (!IsActivated())
|
||||||
|
return;
|
||||||
|
// Signal read thread to exit.
|
||||||
|
m_read_enabled.Clear();
|
||||||
|
m_read_thread_shutdown.Set();
|
||||||
|
m_active = false;
|
||||||
|
|
||||||
|
// kill all active socket
|
||||||
|
for (auto& ref : network_ref)
|
||||||
|
{
|
||||||
|
if (ref.ip != 0)
|
||||||
|
{
|
||||||
|
ref.type == IPPROTO_TCP ? ref.tcp_socket.disconnect() : ref.udp_socket.unbind();
|
||||||
|
}
|
||||||
|
ref.ip = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for read thread to exit.
|
||||||
|
if (m_read_thread.joinable())
|
||||||
|
m_read_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::BuiltInBBAInterface::IsActivated()
|
||||||
|
{
|
||||||
|
return m_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::WriteToQueue(const std::vector<u8>& data)
|
||||||
|
{
|
||||||
|
m_queue_data[m_queue_write] = data;
|
||||||
|
const u8 next_write_index = (m_queue_write + 1) & 15;
|
||||||
|
if (next_write_index != m_queue_read)
|
||||||
|
m_queue_write = next_write_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::HandleARP(const Common::ARPPacket& packet)
|
||||||
|
{
|
||||||
|
const auto& [hwdata, arpdata] = packet;
|
||||||
|
const Common::MACAddress bba_mac =
|
||||||
|
Common::BitCastPtr<Common::MACAddress>(&m_eth_ref->mBbaMem[BBA_NAFR_PAR0]);
|
||||||
|
Common::ARPPacket response(bba_mac, m_fake_mac);
|
||||||
|
|
||||||
|
if (arpdata.target_ip == m_current_ip)
|
||||||
|
{
|
||||||
|
// game asked for himself, reply with his mac address
|
||||||
|
response.arp_header = Common::ARPHeader(arpdata.target_ip, bba_mac, m_current_ip, bba_mac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.arp_header = Common::ARPHeader(arpdata.target_ip, m_fake_mac, m_current_ip, bba_mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteToQueue(response.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(const Common::UDPPacket& packet)
|
||||||
|
{
|
||||||
|
const auto& [hwdata, ip, udp_header, ip_options, data] = packet;
|
||||||
|
const Common::DHCPPacket dhcp(packet.data);
|
||||||
|
const Common::DHCPBody& request = dhcp.body;
|
||||||
|
sockaddr_in from;
|
||||||
|
sockaddr_in to;
|
||||||
|
from.sin_addr.s_addr = m_router_ip;
|
||||||
|
from.sin_family = IPPROTO_UDP;
|
||||||
|
from.sin_port = htons(67);
|
||||||
|
to.sin_addr.s_addr = m_current_ip;
|
||||||
|
to.sin_family = IPPROTO_UDP;
|
||||||
|
to.sin_port = udp_header.source_port;
|
||||||
|
|
||||||
|
const u8* router_ip_ptr = reinterpret_cast<const u8*>(&m_router_ip);
|
||||||
|
const std::vector<u8> ip_part(router_ip_ptr, router_ip_ptr + sizeof(m_router_ip));
|
||||||
|
|
||||||
|
const std::vector<u8> timeout_24h = {0, 1, 0x51, 0x80};
|
||||||
|
|
||||||
|
const Common::MACAddress bba_mac =
|
||||||
|
Common::BitCastPtr<Common::MACAddress>(&m_eth_ref->mBbaMem[BBA_NAFR_PAR0]);
|
||||||
|
Common::DHCPPacket reply;
|
||||||
|
reply.body = Common::DHCPBody(request.transaction_id, bba_mac, m_current_ip, m_router_ip);
|
||||||
|
|
||||||
|
// options
|
||||||
|
// send our emulated lan settings
|
||||||
|
|
||||||
|
(dhcp.options.size() == 0 || dhcp.options[0].size() < 2 || dhcp.options[0].at(2) == 1) ?
|
||||||
|
reply.AddOption(53, {2}) : // default, send a suggestion
|
||||||
|
reply.AddOption(53, {5});
|
||||||
|
reply.AddOption(54, ip_part); // dhcp server ip
|
||||||
|
reply.AddOption(51, timeout_24h); // lease time 24h
|
||||||
|
reply.AddOption(58, timeout_24h); // renewal time
|
||||||
|
reply.AddOption(59, timeout_24h); // rebind time
|
||||||
|
reply.AddOption(1, {255, 255, 255, 0}); // submask
|
||||||
|
reply.AddOption(28, {ip_part[0], ip_part[1], ip_part[2], 255}); // broadcast ip
|
||||||
|
reply.AddOption(6, ip_part); // dns server
|
||||||
|
reply.AddOption(15, {0x6c, 0x61, 0x6e}); // domain name "lan"
|
||||||
|
reply.AddOption(3, ip_part); // router ip
|
||||||
|
reply.AddOption(255, {}); // end
|
||||||
|
|
||||||
|
Common::UDPPacket response(bba_mac, m_fake_mac, from, to, reply.Build());
|
||||||
|
|
||||||
|
WriteToQueue(response.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetAvailableSlot(u16 port)
|
||||||
|
{
|
||||||
|
if (port > 0) // existing connection?
|
||||||
|
{
|
||||||
|
for (auto& ref : network_ref)
|
||||||
|
{
|
||||||
|
if (ref.ip != 0 && ref.local == port)
|
||||||
|
return &ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& ref : network_ref)
|
||||||
|
{
|
||||||
|
if (ref.ip == 0)
|
||||||
|
return &ref;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetTCPSlot(u16 src_port, u16 dst_port, u32 ip)
|
||||||
|
{
|
||||||
|
for (auto& ref : network_ref)
|
||||||
|
{
|
||||||
|
if (ref.ip == ip && ref.remote == dst_port && ref.local == src_port)
|
||||||
|
{
|
||||||
|
return &ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> BuildFINFrame(StackRef* ref)
|
||||||
|
{
|
||||||
|
Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||||
|
ref->ack_num, TCP_FLAG_FIN | TCP_FLAG_ACK | TCP_FLAG_RST);
|
||||||
|
|
||||||
|
for (auto& tcp_buf : ref->tcp_buffers)
|
||||||
|
tcp_buf.used = false;
|
||||||
|
return result.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> BuildAckFrame(StackRef* ref)
|
||||||
|
{
|
||||||
|
Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||||
|
ref->ack_num, TCP_FLAG_ACK);
|
||||||
|
return result.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket& packet)
|
||||||
|
{
|
||||||
|
const auto& [hwdata, ip_header, tcp_header, ip_options, tcp_options, data] = packet;
|
||||||
|
sf::IpAddress target;
|
||||||
|
StackRef* ref = GetTCPSlot(tcp_header.source_port, tcp_header.destination_port,
|
||||||
|
Common::BitCast<u32>(ip_header.destination_addr));
|
||||||
|
const u16 properties = ntohs(tcp_header.properties);
|
||||||
|
if (properties & (TCP_FLAG_FIN | TCP_FLAG_RST))
|
||||||
|
{
|
||||||
|
if (ref == nullptr)
|
||||||
|
return; // not found
|
||||||
|
|
||||||
|
ref->ack_num++;
|
||||||
|
WriteToQueue(BuildFINFrame(ref));
|
||||||
|
ref->ip = 0;
|
||||||
|
ref->tcp_socket.disconnect();
|
||||||
|
}
|
||||||
|
else if (properties & TCP_FLAG_SIN)
|
||||||
|
{
|
||||||
|
// new connection
|
||||||
|
if (ref != nullptr)
|
||||||
|
return;
|
||||||
|
ref = GetAvailableSlot(0);
|
||||||
|
|
||||||
|
ref->delay = GetTickCountStd();
|
||||||
|
ref->local = tcp_header.source_port;
|
||||||
|
ref->remote = tcp_header.destination_port;
|
||||||
|
ref->ack_num = ntohl(tcp_header.sequence_number) + 1;
|
||||||
|
ref->ack_base = ref->ack_num;
|
||||||
|
ref->seq_num = 0x1000000;
|
||||||
|
ref->window_size = ntohl(tcp_header.window_size);
|
||||||
|
ref->type = IPPROTO_TCP;
|
||||||
|
for (auto& tcp_buf : ref->tcp_buffers)
|
||||||
|
tcp_buf.used = false;
|
||||||
|
ref->from.sin_addr.s_addr = Common::BitCast<u32>(ip_header.destination_addr);
|
||||||
|
ref->from.sin_port = tcp_header.destination_port;
|
||||||
|
ref->to.sin_addr.s_addr = Common::BitCast<u32>(ip_header.source_addr);
|
||||||
|
ref->to.sin_port = tcp_header.source_port;
|
||||||
|
ref->bba_mac = Common::BitCastPtr<Common::MACAddress>(&m_eth_ref->mBbaMem[BBA_NAFR_PAR0]);
|
||||||
|
ref->my_mac = m_fake_mac;
|
||||||
|
ref->tcp_socket.setBlocking(false);
|
||||||
|
|
||||||
|
// reply with a sin_ack
|
||||||
|
Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||||
|
ref->ack_num, TCP_FLAG_SIN | TCP_FLAG_ACK);
|
||||||
|
|
||||||
|
result.tcp_options = {0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x01, 0x01};
|
||||||
|
|
||||||
|
ref->seq_num++;
|
||||||
|
target = sf::IpAddress(ntohl(Common::BitCast<u32>(ip_header.destination_addr)));
|
||||||
|
ref->tcp_socket.connect(target, ntohs(tcp_header.destination_port));
|
||||||
|
ref->ready = false;
|
||||||
|
ref->ip = Common::BitCast<u32>(ip_header.destination_addr);
|
||||||
|
|
||||||
|
ref->tcp_buffers[0].data = result.Build();
|
||||||
|
ref->tcp_buffers[0].seq_id = ref->seq_num - 1;
|
||||||
|
ref->tcp_buffers[0].tick = GetTickCountStd() - 900; // delay
|
||||||
|
ref->tcp_buffers[0].used = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// data packet
|
||||||
|
if (ref == nullptr)
|
||||||
|
return; // not found
|
||||||
|
|
||||||
|
const int size =
|
||||||
|
ntohs(ip_header.total_len) - ip_header.DefinedSize() - tcp_header.GetHeaderSize();
|
||||||
|
const u32 this_seq = ntohl(tcp_header.sequence_number);
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
// only if contain data
|
||||||
|
if (static_cast<int>(this_seq - ref->ack_num) >= 0 && data.size() >= size)
|
||||||
|
{
|
||||||
|
ref->tcp_socket.send(data.data(), size);
|
||||||
|
ref->ack_num += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send ack
|
||||||
|
WriteToQueue(BuildAckFrame(ref));
|
||||||
|
}
|
||||||
|
// update windows size
|
||||||
|
ref->window_size = ntohs(tcp_header.window_size);
|
||||||
|
|
||||||
|
// clear any ack data
|
||||||
|
if (ntohs(tcp_header.properties) & TCP_FLAG_ACK)
|
||||||
|
{
|
||||||
|
const u32 ack_num = ntohl(tcp_header.acknowledgement_number);
|
||||||
|
for (auto& tcp_buf : ref->tcp_buffers)
|
||||||
|
{
|
||||||
|
if (!tcp_buf.used || tcp_buf.seq_id >= ack_num)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Common::PacketView view(tcp_buf.data.data(), tcp_buf.data.size());
|
||||||
|
auto tcp_packet = view.GetTCPPacket(); // This is always a tcp packet
|
||||||
|
if (!tcp_packet.has_value()) // should never happen but just in case
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const u32 seq_end = static_cast<u32>(tcp_buf.seq_id + tcp_packet->data.size());
|
||||||
|
if (seq_end <= ack_num)
|
||||||
|
{
|
||||||
|
tcp_buf.used = false; // confirmed data received
|
||||||
|
if (!ref->ready && !ref->tcp_buffers[0].used)
|
||||||
|
ref->ready = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// partial data, adjust the packet for next ack
|
||||||
|
const u16 ack_size = ack_num - tcp_buf.seq_id;
|
||||||
|
tcp_packet->data.erase(tcp_packet->data.begin(), tcp_packet->data.begin() + ack_size);
|
||||||
|
|
||||||
|
tcp_buf.seq_id += ack_size;
|
||||||
|
tcp_packet->tcp_header.sequence_number = htonl(tcp_buf.seq_id);
|
||||||
|
tcp_buf.data = tcp_packet->Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a little hack, some games open a UDP port
|
||||||
|
// and listen to it. We open it on our side manually.
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::InitUDPPort(u16 port)
|
||||||
|
{
|
||||||
|
StackRef* ref = GetAvailableSlot(htons(port));
|
||||||
|
if (ref == nullptr || ref->ip != 0)
|
||||||
|
return;
|
||||||
|
ref->ip = m_router_ip; // change for ip
|
||||||
|
ref->local = htons(port);
|
||||||
|
ref->remote = htons(port);
|
||||||
|
ref->type = IPPROTO_UDP;
|
||||||
|
ref->bba_mac = Common::BitCastPtr<Common::MACAddress>(&m_eth_ref->mBbaMem[BBA_NAFR_PAR0]);
|
||||||
|
ref->my_mac = m_fake_mac;
|
||||||
|
ref->from.sin_addr.s_addr = 0;
|
||||||
|
ref->from.sin_port = htons(port);
|
||||||
|
ref->to.sin_addr.s_addr = m_current_ip;
|
||||||
|
ref->to.sin_port = htons(port);
|
||||||
|
ref->udp_socket.setBlocking(false);
|
||||||
|
if (ref->udp_socket.bind(port) != sf::Socket::Done)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Couldn't open UDP socket");
|
||||||
|
PanicAlertFmt("Could't open port {:x}, this game might not work proprely in LAN mode.", port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket& packet)
|
||||||
|
{
|
||||||
|
const auto& [hwdata, ip_header, udp_header, ip_options, data] = packet;
|
||||||
|
sf::IpAddress target;
|
||||||
|
const u32 destination_addr = ip_header.destination_addr == Common::IP_ADDR_ANY ?
|
||||||
|
m_router_ip : // dns request
|
||||||
|
Common::BitCast<u32>(ip_header.destination_addr);
|
||||||
|
|
||||||
|
StackRef* ref = GetAvailableSlot(udp_header.source_port);
|
||||||
|
if (ref->ip == 0)
|
||||||
|
{
|
||||||
|
ref->ip = destination_addr; // change for ip
|
||||||
|
ref->local = udp_header.source_port;
|
||||||
|
ref->remote = udp_header.destination_port;
|
||||||
|
ref->type = IPPROTO_UDP;
|
||||||
|
ref->bba_mac = Common::BitCastPtr<Common::MACAddress>(&m_eth_ref->mBbaMem[BBA_NAFR_PAR0]);
|
||||||
|
ref->my_mac = m_fake_mac;
|
||||||
|
ref->from.sin_addr.s_addr = destination_addr;
|
||||||
|
ref->from.sin_port = udp_header.destination_port;
|
||||||
|
ref->to.sin_addr.s_addr = Common::BitCast<u32>(ip_header.source_addr);
|
||||||
|
ref->to.sin_port = udp_header.source_port;
|
||||||
|
ref->udp_socket.setBlocking(false);
|
||||||
|
if (ref->udp_socket.bind(htons(udp_header.source_port)) != sf::Socket::Done)
|
||||||
|
{
|
||||||
|
PanicAlertFmt(
|
||||||
|
"Port {:x} is already in use, this game might not work as intented in LAN Mode.",
|
||||||
|
htons(udp_header.source_port));
|
||||||
|
if (ref->udp_socket.bind(sf::Socket::AnyPort) != sf::Socket::Done)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Couldn't open UDP socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ntohs(udp_header.destination_port) == 1900)
|
||||||
|
{
|
||||||
|
InitUDPPort(26512); // MK DD and 1080
|
||||||
|
InitUDPPort(26502); // Air Ride
|
||||||
|
if (udp_header.length > 150)
|
||||||
|
{
|
||||||
|
// Quick hack to unlock the connection, throw it back at him
|
||||||
|
Common::UDPPacket reply = packet;
|
||||||
|
reply.eth_header.destination = hwdata.source;
|
||||||
|
reply.eth_header.source = hwdata.destination;
|
||||||
|
reply.ip_header.destination_addr = ip_header.source_addr;
|
||||||
|
if (ip_header.destination_addr == Common::IP_ADDR_SSDP)
|
||||||
|
reply.ip_header.source_addr = Common::IP_ADDR_BROADCAST;
|
||||||
|
else
|
||||||
|
reply.ip_header.source_addr = Common::BitCast<Common::IPAddress>(destination_addr);
|
||||||
|
WriteToQueue(reply.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ntohs(udp_header.destination_port) == 53)
|
||||||
|
target = sf::IpAddress(m_dns_ip.c_str()); // dns server ip
|
||||||
|
else if (ip_header.destination_addr == Common::IP_ADDR_SSDP)
|
||||||
|
target = sf::IpAddress(0xFFFFFFFF); // force real broadcast
|
||||||
|
else
|
||||||
|
target = sf::IpAddress(ntohl(Common::BitCast<u32>(ip_header.destination_addr)));
|
||||||
|
ref->udp_socket.send(data.data(), data.size(), target, ntohs(udp_header.destination_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mtx);
|
||||||
|
const Common::PacketView view(frame, size);
|
||||||
|
|
||||||
|
const std::optional<u16> ethertype = view.GetEtherType();
|
||||||
|
if (!ethertype.has_value())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Unable to send frame with invalid ethernet header");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*ethertype)
|
||||||
|
{
|
||||||
|
case Common::IPV4_ETHERTYPE:
|
||||||
|
{
|
||||||
|
const std::optional<u8> ip_proto = view.GetIPProto();
|
||||||
|
if (!ip_proto.has_value())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Unable to send frame with invalid IP header");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*ip_proto)
|
||||||
|
{
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
{
|
||||||
|
const auto udp_packet = view.GetUDPPacket();
|
||||||
|
if (!udp_packet.has_value())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Unable to send frame with invalid UDP header");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntohs(udp_packet->udp_header.destination_port) == 67)
|
||||||
|
{
|
||||||
|
HandleDHCP(*udp_packet);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleUDPFrame(*udp_packet);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
{
|
||||||
|
const auto tcp_packet = view.GetTCPPacket();
|
||||||
|
if (!tcp_packet.has_value())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Unable to send frame with invalid TCP header");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleTCPFrame(*tcp_packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Common::ARP_ETHERTYPE:
|
||||||
|
{
|
||||||
|
const auto arp_packet = view.GetARPPacket();
|
||||||
|
if (!arp_packet.has_value())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Unable to send frame with invalid ARP header");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleARP(*arp_packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
ERROR_LOG_FMT(SP1, "Unsupported EtherType {#06x}", *ethertype);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_eth_ref->SendComplete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<u8>> TryGetDataFromSocket(StackRef* ref)
|
||||||
|
{
|
||||||
|
size_t datasize = 0; // Set by socket.receive using a non-const reference
|
||||||
|
unsigned short remote_port;
|
||||||
|
|
||||||
|
switch (ref->type)
|
||||||
|
{
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
{
|
||||||
|
std::array<u8, MAX_UDP_LENGTH> buffer;
|
||||||
|
ref->udp_socket.receive(buffer.data(), MAX_UDP_LENGTH, datasize, ref->target, remote_port);
|
||||||
|
if (datasize > 0)
|
||||||
|
{
|
||||||
|
ref->from.sin_port = htons(remote_port);
|
||||||
|
ref->from.sin_addr.s_addr = htonl(ref->target.toInteger());
|
||||||
|
const std::vector<u8> udp_data(buffer.begin(), buffer.begin() + datasize);
|
||||||
|
Common::UDPPacket packet(ref->bba_mac, ref->my_mac, ref->from, ref->to, udp_data);
|
||||||
|
return packet.Build();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
sf::Socket::Status st = sf::Socket::Status::Done;
|
||||||
|
TcpBuffer* tcp_buffer = nullptr;
|
||||||
|
for (auto& tcp_buf : ref->tcp_buffers)
|
||||||
|
{
|
||||||
|
if (tcp_buf.used)
|
||||||
|
continue;
|
||||||
|
tcp_buffer = &tcp_buf;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default size to 0 to avoid issue
|
||||||
|
datasize = 0;
|
||||||
|
const bool can_go = (GetTickCountStd() - ref->poke_time > 100 || ref->window_size > 2000);
|
||||||
|
std::array<u8, MAX_TCP_LENGTH> buffer;
|
||||||
|
if (tcp_buffer != nullptr && ref->ready && can_go)
|
||||||
|
st = ref->tcp_socket.receive(buffer.data(), MAX_TCP_LENGTH, datasize);
|
||||||
|
|
||||||
|
if (datasize > 0)
|
||||||
|
{
|
||||||
|
Common::TCPPacket packet(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||||
|
ref->ack_num, TCP_FLAG_ACK);
|
||||||
|
packet.data = std::vector<u8>(buffer.begin(), buffer.begin() + datasize);
|
||||||
|
|
||||||
|
// build buffer
|
||||||
|
tcp_buffer->seq_id = ref->seq_num;
|
||||||
|
tcp_buffer->tick = GetTickCountStd();
|
||||||
|
tcp_buffer->data = packet.Build();
|
||||||
|
tcp_buffer->seq_id = ref->seq_num;
|
||||||
|
tcp_buffer->used = true;
|
||||||
|
ref->seq_num += static_cast<u32>(datasize);
|
||||||
|
ref->poke_time = GetTickCountStd();
|
||||||
|
return tcp_buffer->data;
|
||||||
|
}
|
||||||
|
if (GetTickCountStd() - ref->delay > 3000)
|
||||||
|
{
|
||||||
|
if (st == sf::Socket::Disconnected || st == sf::Socket::Error)
|
||||||
|
{
|
||||||
|
ref->ip = 0;
|
||||||
|
ref->tcp_socket.disconnect();
|
||||||
|
return BuildFINFrame(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the IP identification and recompute the checksum
|
||||||
|
static void SetIPIdentification(u8* ptr, std::size_t size, u16 value)
|
||||||
|
{
|
||||||
|
if (size < Common::EthernetHeader::SIZE + Common::IPv4Header::SIZE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
u8* const ip_ptr = ptr + Common::EthernetHeader::SIZE;
|
||||||
|
const u8 ip_header_size = (*ip_ptr & 0xf) * 4;
|
||||||
|
if (size < Common::EthernetHeader::SIZE + ip_header_size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
u8* const ip_id_ptr = ip_ptr + offsetof(Common::IPv4Header, identification);
|
||||||
|
Common::BitCastPtr<u16>(ip_id_ptr) = htons(value);
|
||||||
|
|
||||||
|
u8* const ip_checksum_ptr = ip_ptr + offsetof(Common::IPv4Header, header_checksum);
|
||||||
|
auto checksum_bitcast_ptr = Common::BitCastPtr<u16>(ip_checksum_ptr);
|
||||||
|
checksum_bitcast_ptr = u16(0);
|
||||||
|
checksum_bitcast_ptr = htons(Common::ComputeNetworkChecksum(ip_ptr, ip_header_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInBBAInterface* self)
|
||||||
|
{
|
||||||
|
while (!self->m_read_thread_shutdown.IsSet())
|
||||||
|
{
|
||||||
|
// make thread less cpu hungry
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
|
||||||
|
if (!self->m_read_enabled.IsSet())
|
||||||
|
continue;
|
||||||
|
size_t datasize = 0;
|
||||||
|
|
||||||
|
u8 wp = self->m_eth_ref->page_ptr(BBA_RWP);
|
||||||
|
const u8 rp = self->m_eth_ref->page_ptr(BBA_RRP);
|
||||||
|
if (rp > wp)
|
||||||
|
wp += 16;
|
||||||
|
|
||||||
|
if ((wp - rp) >= 8)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(self->m_mtx);
|
||||||
|
// process queue file first
|
||||||
|
if (self->m_queue_read != self->m_queue_write)
|
||||||
|
{
|
||||||
|
datasize = self->m_queue_data[self->m_queue_read].size();
|
||||||
|
if (datasize > BBA_RECV_SIZE)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Frame size is exceiding BBA capacity, frame stack might be corrupted"
|
||||||
|
"Killing Dolphin...");
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
std::memcpy(self->m_eth_ref->mRecvBuffer.get(), self->m_queue_data[self->m_queue_read].data(),
|
||||||
|
datasize);
|
||||||
|
self->m_queue_read++;
|
||||||
|
self->m_queue_read &= 15;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// test connections data
|
||||||
|
for (auto& net_ref : self->network_ref)
|
||||||
|
{
|
||||||
|
if (net_ref.ip == 0)
|
||||||
|
continue;
|
||||||
|
const auto socket_data = TryGetDataFromSocket(&net_ref);
|
||||||
|
if (socket_data.has_value())
|
||||||
|
{
|
||||||
|
datasize = socket_data->size();
|
||||||
|
std::memcpy(self->m_eth_ref->mRecvBuffer.get(), socket_data->data(), datasize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test and add any sleeping tcp data
|
||||||
|
for (auto& net_ref : self->network_ref)
|
||||||
|
{
|
||||||
|
if (net_ref.ip == 0 || net_ref.type != IPPROTO_TCP)
|
||||||
|
continue;
|
||||||
|
for (auto& tcp_buf : net_ref.tcp_buffers)
|
||||||
|
{
|
||||||
|
if (!tcp_buf.used || (GetTickCountStd() - tcp_buf.tick) <= 1000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tcp_buf.tick = GetTickCountStd();
|
||||||
|
// timmed out packet, resend
|
||||||
|
if (((self->m_queue_write + 1) & 15) != self->m_queue_read)
|
||||||
|
{
|
||||||
|
self->WriteToQueue(tcp_buf.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datasize > 0)
|
||||||
|
{
|
||||||
|
u8* buffer = reinterpret_cast<u8*>(self->m_eth_ref->mRecvBuffer.get());
|
||||||
|
Common::PacketView packet(buffer, datasize);
|
||||||
|
const auto packet_type = packet.GetEtherType();
|
||||||
|
if (packet_type.has_value() && packet_type == IP_PROTOCOL)
|
||||||
|
{
|
||||||
|
SetIPIdentification(buffer, datasize, ++self->m_ip_frame_id);
|
||||||
|
}
|
||||||
|
self->m_eth_ref->mRecvBufferLength = datasize > 64 ? static_cast<u32>(datasize) : 64;
|
||||||
|
self->m_eth_ref->RecvHandlePacket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::BuiltInBBAInterface::RecvInit()
|
||||||
|
{
|
||||||
|
m_read_thread = std::thread(ReadThreadHandler, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::RecvStart()
|
||||||
|
{
|
||||||
|
m_read_enabled.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::BuiltInBBAInterface::RecvStop()
|
||||||
|
{
|
||||||
|
m_read_enabled.Clear();
|
||||||
|
for (auto& net_ref : network_ref)
|
||||||
|
{
|
||||||
|
if (net_ref.ip != 0)
|
||||||
|
{
|
||||||
|
net_ref.type == IPPROTO_TCP ? net_ref.tcp_socket.disconnect() : net_ref.udp_socket.unbind();
|
||||||
|
}
|
||||||
|
net_ref.ip = 0;
|
||||||
|
}
|
||||||
|
m_queue_read = 0;
|
||||||
|
m_queue_write = 0;
|
||||||
|
}
|
||||||
|
} // namespace ExpansionInterface
|
58
Source/Core/Core/HW/EXI/BBA/BuiltIn.h
Normal file
58
Source/Core/Core/HW/EXI/BBA/BuiltIn.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2022 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#else
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <SFML/Network.hpp>
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Network.h"
|
||||||
|
|
||||||
|
constexpr u16 TCP_FLAG_SIN = 0x2;
|
||||||
|
constexpr u16 TCP_FLAG_ACK = 0x10;
|
||||||
|
constexpr u16 TCP_FLAG_PSH = 0x8;
|
||||||
|
constexpr u16 TCP_FLAG_FIN = 0x1;
|
||||||
|
constexpr u16 TCP_FLAG_RST = 0x4;
|
||||||
|
|
||||||
|
constexpr u16 IP_PROTOCOL = 0x800;
|
||||||
|
constexpr u16 ARP_PROTOCOL = 0x806;
|
||||||
|
|
||||||
|
constexpr u8 MAX_TCP_BUFFER = 4;
|
||||||
|
constexpr u16 MAX_UDP_LENGTH = 1500;
|
||||||
|
constexpr u16 MAX_TCP_LENGTH = 440;
|
||||||
|
|
||||||
|
struct TcpBuffer
|
||||||
|
{
|
||||||
|
bool used;
|
||||||
|
u64 tick;
|
||||||
|
u32 seq_id;
|
||||||
|
std::vector<u8> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StackRef
|
||||||
|
{
|
||||||
|
u32 ip;
|
||||||
|
u16 local;
|
||||||
|
u16 remote;
|
||||||
|
u16 type;
|
||||||
|
sf::IpAddress target;
|
||||||
|
u32 seq_num;
|
||||||
|
u32 ack_num;
|
||||||
|
u32 ack_base;
|
||||||
|
u16 window_size;
|
||||||
|
u64 delay;
|
||||||
|
std::array<TcpBuffer, MAX_TCP_BUFFER> tcp_buffers;
|
||||||
|
bool ready;
|
||||||
|
sockaddr_in from;
|
||||||
|
sockaddr_in to;
|
||||||
|
Common::MACAddress bba_mac{};
|
||||||
|
Common::MACAddress my_mac{};
|
||||||
|
sf::UdpSocket udp_socket;
|
||||||
|
sf::TcpSocket tcp_socket;
|
||||||
|
u64 poke_time;
|
||||||
|
};
|
@ -143,6 +143,10 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(const EXIDeviceType device_type, co
|
|||||||
result = std::make_unique<CEXIETHERNET>(BBADeviceType::XLINK);
|
result = std::make_unique<CEXIETHERNET>(BBADeviceType::XLINK);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EXIDeviceType::EthernetBuiltIn:
|
||||||
|
result = std::make_unique<CEXIETHERNET>(BBADeviceType::BuiltIn);
|
||||||
|
break;
|
||||||
|
|
||||||
case EXIDeviceType::Gecko:
|
case EXIDeviceType::Gecko:
|
||||||
result = std::make_unique<CEXIGecko>();
|
result = std::make_unique<CEXIGecko>();
|
||||||
break;
|
break;
|
||||||
|
@ -37,6 +37,7 @@ enum class EXIDeviceType : int
|
|||||||
EthernetXLink,
|
EthernetXLink,
|
||||||
// Only used on Apple devices.
|
// Only used on Apple devices.
|
||||||
EthernetTapServer,
|
EthernetTapServer,
|
||||||
|
EthernetBuiltIn,
|
||||||
None = 0xFF
|
None = 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(EXIDeviceType device_type, int chan
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
||||||
: EnumFormatter<ExpansionInterface::EXIDeviceType::EthernetTapServer>
|
: EnumFormatter<ExpansionInterface::EXIDeviceType::EthernetBuiltIn>
|
||||||
{
|
{
|
||||||
static constexpr array_type names = {
|
static constexpr array_type names = {
|
||||||
_trans("Dummy"),
|
_trans("Dummy"),
|
||||||
@ -95,6 +96,7 @@ struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
|||||||
_trans("Advance Game Port"),
|
_trans("Advance Game Port"),
|
||||||
_trans("Broadband Adapter (XLink Kai)"),
|
_trans("Broadband Adapter (XLink Kai)"),
|
||||||
_trans("Broadband Adapter (tapserver)"),
|
_trans("Broadband Adapter (tapserver)"),
|
||||||
|
_trans("Broadband Adapter (Built In)"),
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr formatter() : EnumFormatter(names) {}
|
constexpr formatter() : EnumFormatter(names) {}
|
||||||
|
@ -53,6 +53,11 @@ CEXIETHERNET::CEXIETHERNET(BBADeviceType type)
|
|||||||
INFO_LOG_FMT(SP1, "Created tapserver physical network interface.");
|
INFO_LOG_FMT(SP1, "Created tapserver physical network interface.");
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case BBADeviceType::BuiltIn:
|
||||||
|
m_network_interface = std::make_unique<BuiltInBBAInterface>(
|
||||||
|
this, Config::Get(Config::MAIN_BBA_BUILTIN_DNS), Config::Get(Config::MAIN_BBA_BUILTIN_IP));
|
||||||
|
INFO_LOG_FMT(SP1, "Created Built in network interface.");
|
||||||
|
break;
|
||||||
case BBADeviceType::XLINK:
|
case BBADeviceType::XLINK:
|
||||||
// TODO start BBA with network link down, bring it up after "connected" response from XLink
|
// TODO start BBA with network link down, bring it up after "connected" response from XLink
|
||||||
|
|
||||||
@ -155,6 +160,17 @@ void CEXIETHERNET::ImmWrite(u32 data, u32 size)
|
|||||||
{
|
{
|
||||||
case INTERRUPT:
|
case INTERRUPT:
|
||||||
exi_status.interrupt &= data ^ 0xff;
|
exi_status.interrupt &= data ^ 0xff;
|
||||||
|
// raise back if there is still data
|
||||||
|
if (page_ptr(BBA_RRP) != page_ptr(BBA_RWP))
|
||||||
|
{
|
||||||
|
if (mBbaMem[BBA_IMR] & INT_R)
|
||||||
|
{
|
||||||
|
mBbaMem[BBA_IR] |= INT_R;
|
||||||
|
|
||||||
|
exi_status.interrupt |= exi_status.TRANSFER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case INTERRUPT_MASK:
|
case INTERRUPT_MASK:
|
||||||
exi_status.interrupt_mask = data;
|
exi_status.interrupt_mask = data;
|
||||||
@ -228,9 +244,7 @@ void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
|
|||||||
void CEXIETHERNET::DMARead(u32 addr, u32 size)
|
void CEXIETHERNET::DMARead(u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(SP1, "DMA read: {:08x} {:x}", addr, size);
|
DEBUG_LOG_FMT(SP1, "DMA read: {:08x} {:x}", addr, size);
|
||||||
|
|
||||||
Memory::CopyToEmu(addr, &mBbaMem[transfer.address], size);
|
Memory::CopyToEmu(addr, &mBbaMem[transfer.address], size);
|
||||||
|
|
||||||
transfer.address += size;
|
transfer.address += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,7 +362,6 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
|
|||||||
// MXSoftReset();
|
// MXSoftReset();
|
||||||
m_network_interface->Activate();
|
m_network_interface->Activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) != 0)
|
if (((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) != 0)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(SP1, "{} rx", (data & NCRA_SR) ? "start" : "stop");
|
DEBUG_LOG_FMT(SP1, "{} rx", (data & NCRA_SR) ? "start" : "stop");
|
||||||
@ -415,7 +428,6 @@ void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size)
|
|||||||
{
|
{
|
||||||
// In direct mode, the hardware handles creating the state required by the
|
// In direct mode, the hardware handles creating the state required by the
|
||||||
// GMAC instead of finagling with packet descriptors and such
|
// GMAC instead of finagling with packet descriptors and such
|
||||||
|
|
||||||
u16* tx_fifo_count = (u16*)&mBbaMem[BBA_TXFIFOCNT];
|
u16* tx_fifo_count = (u16*)&mBbaMem[BBA_TXFIFOCNT];
|
||||||
|
|
||||||
memcpy(tx_fifo.get() + *tx_fifo_count, data, size);
|
memcpy(tx_fifo.get() + *tx_fifo_count, data, size);
|
||||||
@ -510,76 +522,80 @@ inline void CEXIETHERNET::inc_rwp()
|
|||||||
{
|
{
|
||||||
u16* rwp = (u16*)&mBbaMem[BBA_RWP];
|
u16* rwp = (u16*)&mBbaMem[BBA_RWP];
|
||||||
|
|
||||||
if (*rwp + 1 == page_ptr(BBA_RHBP))
|
if (*rwp == page_ptr(BBA_RHBP))
|
||||||
*rwp = page_ptr(BBA_BP);
|
*rwp = page_ptr(BBA_BP);
|
||||||
else
|
else
|
||||||
(*rwp)++;
|
(*rwp)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void CEXIETHERNET::set_rwp(u16 value)
|
||||||
|
{
|
||||||
|
u16* rwp = (u16*)&mBbaMem[BBA_RWP];
|
||||||
|
*rwp = value;
|
||||||
|
}
|
||||||
|
|
||||||
// This function is on the critical path for receiving data.
|
// This function is on the critical path for receiving data.
|
||||||
// Be very careful about calling into the logger and other slow things
|
// Be very careful about calling into the logger and other slow things
|
||||||
bool CEXIETHERNET::RecvHandlePacket()
|
bool CEXIETHERNET::RecvHandlePacket()
|
||||||
{
|
{
|
||||||
u8* write_ptr;
|
u8* write_ptr;
|
||||||
u8* end_ptr;
|
|
||||||
u8* read_ptr;
|
|
||||||
Descriptor* descriptor;
|
Descriptor* descriptor;
|
||||||
u32 status = 0;
|
u32 status = 0;
|
||||||
u16 rwp_initial = page_ptr(BBA_RWP);
|
u16 rwp_initial = page_ptr(BBA_RWP);
|
||||||
|
u16 current_rwp = 0;
|
||||||
|
u32 off = 4;
|
||||||
if (!RecvMACFilter())
|
if (!RecvMACFilter())
|
||||||
goto wait_for_next;
|
goto wait_for_next;
|
||||||
|
|
||||||
#ifdef BBA_TRACK_PAGE_PTRS
|
#ifdef BBA_TRACK_PAGE_PTRS
|
||||||
INFO_LOG_FMT(SP1, "RecvHandlePacket {:x}\n{}", mRecvBufferLength,
|
INFO_LOG_FMT(SP1, "RecvHandlePacket {:x}\n{}", mRecvBufferLength,
|
||||||
ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100));
|
ArrayToString(mRecvBuffer.get(), mRecvBufferLength, 16));
|
||||||
|
|
||||||
INFO_LOG_FMT(SP1, "{:x} {:x} {:x} {:x}", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
|
INFO_LOG_FMT(SP1, "{:x} {:x} {:x} {:x}", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
|
||||||
page_ptr(BBA_RHBP));
|
page_ptr(BBA_RHBP));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
write_ptr = ptr_from_page_ptr(BBA_RWP);
|
write_ptr = &mBbaMem[page_ptr(BBA_RWP) << 8];
|
||||||
end_ptr = ptr_from_page_ptr(BBA_RHBP);
|
|
||||||
read_ptr = ptr_from_page_ptr(BBA_RRP);
|
|
||||||
|
|
||||||
descriptor = (Descriptor*)write_ptr;
|
descriptor = (Descriptor*)write_ptr;
|
||||||
write_ptr += 4;
|
current_rwp = page_ptr(BBA_RWP);
|
||||||
|
DEBUG_LOG_FMT(SP1, "Frame recv: {:x}", mRecvBufferLength);
|
||||||
for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off)
|
for (u32 i = 0; i < mRecvBufferLength; i++)
|
||||||
{
|
{
|
||||||
*write_ptr++ = mRecvBuffer[i];
|
write_ptr[off] = mRecvBuffer[i];
|
||||||
|
off++;
|
||||||
if (off == 0xff)
|
if (off == 0x100)
|
||||||
{
|
{
|
||||||
off = 0;
|
off = 0;
|
||||||
inc_rwp();
|
// avoid increasing the BBA register while copying
|
||||||
}
|
// sometime the OS can try to process when it's not completed
|
||||||
|
current_rwp = current_rwp == page_ptr(BBA_RHBP) ? page_ptr(BBA_BP) : ++current_rwp;
|
||||||
|
|
||||||
if (write_ptr == end_ptr)
|
write_ptr = &mBbaMem[current_rwp << 8];
|
||||||
write_ptr = ptr_from_page_ptr(BBA_BP);
|
|
||||||
|
|
||||||
if (write_ptr == read_ptr)
|
if (page_ptr(BBA_RRP) == current_rwp)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
halt copy
|
halt copy
|
||||||
if (cur_packet_size >= PAGE_SIZE)
|
if (cur_packet_size >= PAGE_SIZE)
|
||||||
desc.status |= FO | BF
|
desc.status |= FO | BF
|
||||||
if (RBFIM)
|
if (RBFIM)
|
||||||
raise RBFI
|
raise RBFI
|
||||||
if (AUTORCVR)
|
if (AUTORCVR)
|
||||||
discard bad packet
|
discard bad packet
|
||||||
else
|
else
|
||||||
inc MPC instead of receiving packets
|
inc MPC instead of receiving packets
|
||||||
*/
|
*/
|
||||||
status |= DESC_FO | DESC_BF;
|
status |= DESC_FO | DESC_BF;
|
||||||
mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
|
mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Align up to next page
|
// Align up to next page
|
||||||
if ((mRecvBufferLength + 4) % 256)
|
if ((mRecvBufferLength + 4) % 256)
|
||||||
inc_rwp();
|
current_rwp = current_rwp == page_ptr(BBA_RHBP) ? page_ptr(BBA_BP) : ++current_rwp;
|
||||||
|
|
||||||
#ifdef BBA_TRACK_PAGE_PTRS
|
#ifdef BBA_TRACK_PAGE_PTRS
|
||||||
INFO_LOG_FMT(SP1, "{:x} {:x} {:x} {:x}", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
|
INFO_LOG_FMT(SP1, "{:x} {:x} {:x} {:x}", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
|
||||||
@ -602,7 +618,9 @@ bool CEXIETHERNET::RecvHandlePacket()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor->set(*(u16*)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, status);
|
descriptor->set(current_rwp, 4 + mRecvBufferLength, status);
|
||||||
|
|
||||||
|
set_rwp(current_rwp);
|
||||||
|
|
||||||
mBbaMem[BBA_LRPS] = status;
|
mBbaMem[BBA_LRPS] = status;
|
||||||
|
|
||||||
|
@ -13,7 +13,10 @@
|
|||||||
|
|
||||||
#include <SFML/Network.hpp>
|
#include <SFML/Network.hpp>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include "Common/Flag.h"
|
#include "Common/Flag.h"
|
||||||
|
#include "Common/Network.h"
|
||||||
|
#include "Core/HW/EXI/BBA/BuiltIn.h"
|
||||||
#include "Core/HW/EXI/EXI_Device.h"
|
#include "Core/HW/EXI/EXI_Device.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
@ -204,6 +207,7 @@ enum class BBADeviceType
|
|||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
TAPSERVER,
|
TAPSERVER,
|
||||||
#endif
|
#endif
|
||||||
|
BuiltIn,
|
||||||
};
|
};
|
||||||
|
|
||||||
class CEXIETHERNET : public IEXIDevice
|
class CEXIETHERNET : public IEXIDevice
|
||||||
@ -289,7 +293,6 @@ private:
|
|||||||
return ((u16)mBbaMem[index + 1] << 8) | mBbaMem[index];
|
return ((u16)mBbaMem[index + 1] << 8) | mBbaMem[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u8* ptr_from_page_ptr(int const index) const { return &mBbaMem[page_ptr(index) << 8]; }
|
|
||||||
bool IsMXCommand(u32 const data);
|
bool IsMXCommand(u32 const data);
|
||||||
bool IsWriteCommand(u32 const data);
|
bool IsWriteCommand(u32 const data);
|
||||||
const char* GetRegisterName() const;
|
const char* GetRegisterName() const;
|
||||||
@ -299,9 +302,11 @@ private:
|
|||||||
void SendFromDirectFIFO();
|
void SendFromDirectFIFO();
|
||||||
void SendFromPacketBuffer();
|
void SendFromPacketBuffer();
|
||||||
void SendComplete();
|
void SendComplete();
|
||||||
|
void SendCompleteBack();
|
||||||
u8 HashIndex(const u8* dest_eth_addr);
|
u8 HashIndex(const u8* dest_eth_addr);
|
||||||
bool RecvMACFilter();
|
bool RecvMACFilter();
|
||||||
void inc_rwp();
|
void inc_rwp();
|
||||||
|
void set_rwp(u16 value);
|
||||||
bool RecvHandlePacket();
|
bool RecvHandlePacket();
|
||||||
|
|
||||||
std::unique_ptr<u8[]> mBbaMem;
|
std::unique_ptr<u8[]> mBbaMem;
|
||||||
@ -413,6 +418,54 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BuiltInBBAInterface : public NetworkInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BuiltInBBAInterface(CEXIETHERNET* eth_ref, std::string dns_ip, std::string local_ip)
|
||||||
|
: NetworkInterface(eth_ref), m_dns_ip(std::move(dns_ip)), m_local_ip(std::move(local_ip))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
bool Activate() override;
|
||||||
|
void Deactivate() override;
|
||||||
|
bool IsActivated() override;
|
||||||
|
bool SendFrame(const u8* frame, u32 size) override;
|
||||||
|
bool RecvInit() override;
|
||||||
|
void RecvStart() override;
|
||||||
|
void RecvStop() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_mac_id;
|
||||||
|
std::string m_dns_ip;
|
||||||
|
bool m_active = false;
|
||||||
|
u16 m_ip_frame_id = 0;
|
||||||
|
u8 m_queue_read = 0;
|
||||||
|
u8 m_queue_write = 0;
|
||||||
|
std::array<std::vector<u8>, 16> m_queue_data;
|
||||||
|
std::mutex m_mtx;
|
||||||
|
std::string m_local_ip;
|
||||||
|
u32 m_current_ip = 0;
|
||||||
|
u32 m_router_ip = 0;
|
||||||
|
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||||
|
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
|
||||||
|
std::array<StackRef, 10> network_ref{}; // max 10 at same time, i think most gc game had a
|
||||||
|
// limit of 8 in the gc framework
|
||||||
|
std::thread m_read_thread;
|
||||||
|
Common::Flag m_read_enabled;
|
||||||
|
Common::Flag m_read_thread_shutdown;
|
||||||
|
Common::MACAddress m_fake_mac{};
|
||||||
|
static void ReadThreadHandler(BuiltInBBAInterface* self);
|
||||||
|
#endif
|
||||||
|
void WriteToQueue(const std::vector<u8>& data);
|
||||||
|
StackRef* GetAvailableSlot(u16 port);
|
||||||
|
StackRef* GetTCPSlot(u16 src_port, u16 dst_port, u32 ip);
|
||||||
|
|
||||||
|
void HandleARP(const Common::ARPPacket& packet);
|
||||||
|
void HandleDHCP(const Common::UDPPacket& packet);
|
||||||
|
void HandleTCPFrame(const Common::TCPPacket& packet);
|
||||||
|
void InitUDPPort(u16 port);
|
||||||
|
void HandleUDPFrame(const Common::UDPPacket& packet);
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<NetworkInterface> m_network_interface;
|
std::unique_ptr<NetworkInterface> m_network_interface;
|
||||||
|
|
||||||
std::unique_ptr<u8[]> mRecvBuffer;
|
std::unique_ptr<u8[]> mRecvBuffer;
|
||||||
|
@ -254,6 +254,7 @@
|
|||||||
<ClInclude Include="Core\HW\DVD\DVDMath.h" />
|
<ClInclude Include="Core\HW\DVD\DVDMath.h" />
|
||||||
<ClInclude Include="Core\HW\DVD\DVDThread.h" />
|
<ClInclude Include="Core\HW\DVD\DVDThread.h" />
|
||||||
<ClInclude Include="Core\HW\DVD\FileMonitor.h" />
|
<ClInclude Include="Core\HW\DVD\FileMonitor.h" />
|
||||||
|
<ClInclude Include="Core\HW\EXI\BBA\BuiltIn.h" />
|
||||||
<ClInclude Include="Core\HW\EXI\BBA\TAP_Win32.h" />
|
<ClInclude Include="Core\HW\EXI\BBA\TAP_Win32.h" />
|
||||||
<ClInclude Include="Core\HW\EXI\EXI_Channel.h" />
|
<ClInclude Include="Core\HW\EXI\EXI_Channel.h" />
|
||||||
<ClInclude Include="Core\HW\EXI\EXI_Device.h" />
|
<ClInclude Include="Core\HW\EXI\EXI_Device.h" />
|
||||||
@ -869,6 +870,7 @@
|
|||||||
<ClCompile Include="Core\HW\DVD\DVDMath.cpp" />
|
<ClCompile Include="Core\HW\DVD\DVDMath.cpp" />
|
||||||
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
|
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
|
||||||
<ClCompile Include="Core\HW\DVD\FileMonitor.cpp" />
|
<ClCompile Include="Core\HW\DVD\FileMonitor.cpp" />
|
||||||
|
<ClCompile Include="Core\HW\EXI\BBA\BuiltIn.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\BBA\TAP_Win32.cpp" />
|
<ClCompile Include="Core\HW\EXI\BBA\TAP_Win32.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\BBA\XLINK_KAI_BBA.cpp" />
|
<ClCompile Include="Core\HW\EXI\BBA\XLINK_KAI_BBA.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\EXI_Channel.cpp" />
|
<ClCompile Include="Core\HW\EXI\EXI_Channel.cpp" />
|
||||||
|
@ -48,6 +48,15 @@ void BroadbandAdapterSettingsDialog::InitControls()
|
|||||||
window_title = tr("Broadband Adapter MAC Address");
|
window_title = tr("Broadband Adapter MAC Address");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Type::BuiltIn:
|
||||||
|
address_label = new QLabel(tr("Enter the DNS server to use:"));
|
||||||
|
address_placeholder = QStringLiteral("8.8.8.8");
|
||||||
|
current_address = QString::fromStdString(Config::Get(Config::MAIN_BBA_BUILTIN_DNS));
|
||||||
|
description = new QLabel(tr("Use 8.8.8.8 for normal DNS, else enter your custom one"));
|
||||||
|
|
||||||
|
window_title = tr("Broadband Adapter DNS setting");
|
||||||
|
break;
|
||||||
|
|
||||||
case Type::XLinkKai:
|
case Type::XLinkKai:
|
||||||
address_label = new QLabel(tr("Enter IP address of device running the XLink Kai Client:"));
|
address_label = new QLabel(tr("Enter IP address of device running the XLink Kai Client:"));
|
||||||
address_placeholder = QString::fromStdString("127.0.0.1");
|
address_placeholder = QString::fromStdString("127.0.0.1");
|
||||||
@ -103,6 +112,9 @@ void BroadbandAdapterSettingsDialog::SaveAddress()
|
|||||||
Config::SetBaseOrCurrent(Config::MAIN_BBA_MAC, bba_new_address);
|
Config::SetBaseOrCurrent(Config::MAIN_BBA_MAC, bba_new_address);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Type::BuiltIn:
|
||||||
|
Config::SetBaseOrCurrent(Config::MAIN_BBA_BUILTIN_DNS, bba_new_address);
|
||||||
|
break;
|
||||||
case Type::XLinkKai:
|
case Type::XLinkKai:
|
||||||
Config::SetBaseOrCurrent(Config::MAIN_BBA_XLINK_IP, bba_new_address);
|
Config::SetBaseOrCurrent(Config::MAIN_BBA_XLINK_IP, bba_new_address);
|
||||||
break;
|
break;
|
||||||
|
@ -15,6 +15,7 @@ public:
|
|||||||
{
|
{
|
||||||
Ethernet,
|
Ethernet,
|
||||||
XLinkKai,
|
XLinkKai,
|
||||||
|
BuiltIn
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit BroadbandAdapterSettingsDialog(QWidget* target, Type bba_type);
|
explicit BroadbandAdapterSettingsDialog(QWidget* target, Type bba_type);
|
||||||
|
@ -120,6 +120,7 @@ void GameCubePane::CreateWidgets()
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
EXIDeviceType::EthernetTapServer,
|
EXIDeviceType::EthernetTapServer,
|
||||||
#endif
|
#endif
|
||||||
|
EXIDeviceType::EthernetBuiltIn,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
|
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
|
||||||
@ -259,7 +260,8 @@ void GameCubePane::UpdateButton(ExpansionInterface::Slot slot)
|
|||||||
break;
|
break;
|
||||||
case ExpansionInterface::Slot::SP1:
|
case ExpansionInterface::Slot::SP1:
|
||||||
has_config = (device == ExpansionInterface::EXIDeviceType::Ethernet ||
|
has_config = (device == ExpansionInterface::EXIDeviceType::Ethernet ||
|
||||||
device == ExpansionInterface::EXIDeviceType::EthernetXLink);
|
device == ExpansionInterface::EXIDeviceType::EthernetXLink ||
|
||||||
|
device == ExpansionInterface::EXIDeviceType::EthernetBuiltIn);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +295,11 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot)
|
|||||||
BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::XLinkKai).exec();
|
BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::XLinkKai).exec();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case ExpansionInterface::EXIDeviceType::EthernetBuiltIn:
|
||||||
|
{
|
||||||
|
BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::BuiltIn).exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown settings pressed for {}", device);
|
PanicAlertFmt("Unknown settings pressed for {}", device);
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user