Refactoring of the code to match dolphin style

Changed access for offsetof + some clean up
This commit is contained in:
schthack 2022-07-08 10:20:18 -04:00
parent 01ada3850f
commit 087020b23a
8 changed files with 733 additions and 383 deletions

View file

@ -17,6 +17,7 @@
#include <fmt/format.h>
#include "Common/BitUtils.h"
#include "Common/Random.h"
#include "Common/StringUtil.h"
@ -114,6 +115,11 @@ u16 IPv4Header::Size() const
return static_cast<u16>(SIZE);
}
u8 IPv4Header::DefinedSize() const
{
return (version_ihl & 0xf) * 4;
}
TCPHeader::TCPHeader() = default;
TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data,
@ -148,12 +154,17 @@ TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, u3
destination_port = to.sin_port;
sequence_number = htonl(seq);
acknowledgement_number = htonl(ack);
properties = 0x50 | flags;
properties = htons(flags);
window_size = 0x7c;
checksum = 0;
}
u8 TCPHeader::GetHeaderSize() const
{
return (ntohs(properties) & 0xf000) >> 10;
}
u16 TCPHeader::Size() const
{
return static_cast<u16>(SIZE);
@ -185,7 +196,7 @@ u8 UDPHeader::IPProto() const
ARPHeader::ARPHeader() = default;
ARPHeader::ARPHeader(u32 from_ip, MACAddress from_mac, u32 to_ip, MACAddress to_mac)
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;
@ -205,7 +216,7 @@ u16 ARPHeader::Size() const
DHCPBody::DHCPBody() = default;
DHCPBody::DHCPBody(u32 transaction, MACAddress client_address, u32 new_ip, u32 serv_ip)
DHCPBody::DHCPBody(u32 transaction, const MACAddress& client_address, u32 new_ip, u32 serv_ip)
{
transaction_id = transaction;
message_type = DHCPConst::MESSAGE_REPLY;
@ -216,24 +227,57 @@ DHCPBody::DHCPBody(u32 transaction, MACAddress client_address, u32 new_ip, u32 s
server_ip = serv_ip;
}
// Add an option to the DHCP Body
bool DHCPBody::AddDHCPOption(u8 size, u8 fnc, const std::vector<u8>& params)
{
int i = 0;
while (options[i] != 0)
{
i += options[i + 1] + 2;
if (i >= std::size(options))
{
return false;
}
}
DHCPPacket::DHCPPacket() = default;
options[i++] = fnc;
options[i++] = size;
for (auto val : params)
options[i++] = val;
return true;
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
@ -253,13 +297,12 @@ u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
return ~static_cast<u16>(checksum);
}
// Compute the TCP network checksum with a fake header
u16 ComputeTCPNetworkChecksum(const sockaddr_in& from, const sockaddr_in& to, const void* data,
// Compute the TCP checksum with its pseudo header
u16 ComputeTCPNetworkChecksum(const IPAddress& from, const IPAddress& to, const void* data,
u16 length, u8 protocol)
{
// Compute the TCP checksum with its pseudo header
const u32 source_addr = ntohl(from.sin_addr.s_addr);
const u32 destination_addr = ntohl(to.sin_addr.s_addr);
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;
@ -267,6 +310,244 @@ u16 ComputeTCPNetworkChecksum(const sockaddr_in& from, const sockaddr_in& to, co
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*>(&eth_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*>(&eth_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*>(&eth_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()
{
return {

View file

@ -36,6 +36,12 @@ enum DHCPConst
using MACAddress = std::array<u8, MAC_ADDRESS_SIZE>;
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
{
@ -56,6 +62,7 @@ struct IPv4Header
IPv4Header();
IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to);
u16 Size() const;
u8 DefinedSize() const;
static constexpr std::size_t SIZE = 20;
@ -67,8 +74,8 @@ struct IPv4Header
u8 ttl = 0;
u8 protocol = 0;
u16 header_checksum = 0;
std::array<u8, IPV4_ADDR_LEN> source_addr{};
std::array<u8, IPV4_ADDR_LEN> destination_addr{};
IPAddress source_addr{};
IPAddress destination_addr{};
};
static_assert(sizeof(IPv4Header) == IPv4Header::SIZE);
@ -77,6 +84,7 @@ struct 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, u32 ack, u16 flags);
u8 GetHeaderSize() const;
u16 Size() const;
u8 IPProto() const;
@ -113,7 +121,7 @@ static_assert(sizeof(UDPHeader) == UDPHeader::SIZE);
struct ARPHeader
{
ARPHeader();
ARPHeader(u32 from_ip, MACAddress from_mac, u32 to_ip, MACAddress to_mac);
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;
@ -134,15 +142,14 @@ static_assert(sizeof(ARPHeader) == ARPHeader::SIZE);
struct DHCPBody
{
DHCPBody();
DHCPBody(u32 transaction, MACAddress client_address, u32 new_ip, u32 serv_ip);
bool AddDHCPOption(u8 size, u8 fnc, const std::vector<u8>& params);
static constexpr std::size_t SIZE = 540;
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 secondes = 0;
u16 seconds = 0;
u16 boot_flag = 0;
u32 client_ip = 0;
u32 your_ip = 0;
@ -152,11 +159,92 @@ struct DHCPBody
unsigned char padding[10]{};
unsigned char hostname[0x40]{};
unsigned char boot_file[0x80]{};
u32 magic_cookie = 0x63538263;
u8 options[300]{};
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
{
int error;
@ -169,7 +257,7 @@ MACAddress GenerateMacAddress(MACConsumer type);
std::string MacAddressToString(const MACAddress& mac);
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0);
u16 ComputeTCPNetworkChecksum(const sockaddr_in& from, const sockaddr_in& to, const void* data,
u16 ComputeTCPNetworkChecksum(const IPAddress& from, const IPAddress& to, const void* data,
u16 length, u8 protocol);
NetworkErrorState SaveNetworkErrorState();
void RestoreNetworkErrorState(const NetworkErrorState& state);

View file

@ -723,4 +723,4 @@ endif()
if(MSVC)
# Add precompiled header
target_link_libraries(core PRIVATE use_pch)
endif()
endif()

View file

@ -3,13 +3,13 @@
#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"
// #define BBA_TRACK_PAGE_PTRS
namespace ExpansionInterface
{
u64 GetTickCountStd()
@ -18,51 +18,26 @@ u64 GetTickCountStd()
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
void SetHardwareInfo(u8* data, Common::MACAddress dest, Common::MACAddress src)
{
Common::EthernetHeader* hwpart = (Common::EthernetHeader*)data;
*hwpart = Common::EthernetHeader(IP_PROTOCOL);
hwpart->destination = dest;
hwpart->source = src;
}
std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::TCPHeader*>
getTcpHeaders(u8* data, Common::MACAddress dest, Common::MACAddress src)
{
SetHardwareInfo(data, dest, src);
return std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::TCPHeader*>(
(Common::EthernetHeader*)data, (Common::IPv4Header*)&data[14],
(Common::TCPHeader*)&data[0x22]);
}
std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::UDPHeader*>
getUdpHeaders(u8* data, Common::MACAddress dest, Common::MACAddress src)
{
SetHardwareInfo(data, dest, src);
return std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::UDPHeader*>(
(Common::EthernetHeader*)data, (Common::IPv4Header*)&data[14],
(Common::UDPHeader*)&data[0x22]);
}
bool CEXIETHERNET::BuiltInBBAInterface::Activate()
{
if (IsActivated())
return true;
active = true;
m_in_frame = std::make_unique<u8[]>(9004);
m_out_frame = std::make_unique<u8[]>(9004);
for (auto& buf : queue_data)
m_active = true;
for (auto& buf : m_queue_data)
buf.reserve(2048);
fake_mac = Common::GenerateMacAddress(Common::MACConsumer::BBA);
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 (int i = 0; i < std::size(network_ref); i++)
for (auto& ref : network_ref)
{
network_ref[i].ip = 0;
ref.ip = 0;
}
return RecvInit();
@ -76,17 +51,16 @@ void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
// Signal read thread to exit.
m_read_enabled.Clear();
m_read_thread_shutdown.Set();
active = false;
m_active = false;
// kill all active socket
for (int i = 0; i < std::size(network_ref); i++)
for (auto& ref : network_ref)
{
if (network_ref[i].ip != 0)
if (ref.ip != 0)
{
network_ref[i].type == IPPROTO_TCP ? network_ref[i].tcp_socket.disconnect() :
network_ref[i].udp_socket.unbind();
ref.type == IPPROTO_TCP ? ref.tcp_socket.disconnect() : ref.udp_socket.unbind();
}
network_ref[i].ip = 0;
ref.ip = 0;
}
// Wait for read thread to exit.
@ -96,222 +70,185 @@ void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
bool CEXIETHERNET::BuiltInBBAInterface::IsActivated()
{
return active;
return m_active;
}
void CEXIETHERNET::BuiltInBBAInterface::WriteToQueue(const u8* data, int length)
void CEXIETHERNET::BuiltInBBAInterface::WriteToQueue(const std::vector<u8>& data)
{
queue_data[queue_write].resize(length);
std::memcpy(&queue_data[queue_write][0], data, length);
if (((queue_write + 1) & 15) == queue_read)
{
return;
}
queue_write = (queue_write + 1) & 15;
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(Common::EthernetHeader* hwdata,
Common::ARPHeader* arpdata)
void CEXIETHERNET::BuiltInBBAInterface::HandleARP(const Common::ARPPacket& packet)
{
std::memset(m_in_frame.get(), 0, 0x30);
Common::EthernetHeader* hwpart = (Common::EthernetHeader*)m_in_frame.get();
*hwpart = Common::EthernetHeader(ARP_PROTOCOL);
hwpart->destination = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
hwpart->source = fake_mac;
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);
Common::ARPHeader* arppart = (Common::ARPHeader*)&m_in_frame[14];
Common::MACAddress bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
if (arpdata->target_ip == m_current_ip)
if (arpdata.target_ip == m_current_ip)
{
// game asked for himself, reply with his mac address
*arppart = Common::ARPHeader(arpdata->target_ip, bba_mac, m_current_ip, bba_mac);
response.arp_header = Common::ARPHeader(arpdata.target_ip, bba_mac, m_current_ip, bba_mac);
}
else
{
*arppart = Common::ARPHeader(arpdata->target_ip, fake_mac, m_current_ip, bba_mac);
response.arp_header = Common::ARPHeader(arpdata.target_ip, m_fake_mac, m_current_ip, bba_mac);
}
WriteToQueue(&m_in_frame[0], 0x2a);
WriteToQueue(response.Build());
}
void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(Common::EthernetHeader* hwdata,
Common::UDPHeader* udpdata,
Common::DHCPBody* request)
void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(const Common::UDPPacket& packet)
{
Common::DHCPBody* reply = (Common::DHCPBody*)&m_in_frame[0x2a];
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;
std::memset(m_in_frame.get(), 0, 0x156);
// build layer
auto [hwpart, ippart, udppart] = getUdpHeaders(
m_in_frame.get(), *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0], fake_mac);
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 = udpdata->source_port;
const std::vector<u8> ip_part = {((u8*)&m_router_ip)[0], ((u8*)&m_router_ip)[1],
((u8*)&m_router_ip)[2], ((u8*)&m_router_ip)[3]};
to.sin_port = udp_header.source_port;
*ippart = Common::IPv4Header(308, IPPROTO_UDP, from, to);
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));
*udppart = Common::UDPHeader(from, to, 300);
const std::vector<u8> timeout_24h = {0, 1, 0x51, 0x80};
*reply = Common::DHCPBody(request->transaction_id,
*(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0], m_current_ip,
m_router_ip);
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
request->options[2] == 1 ? reply->AddDHCPOption(1, 53, std::vector<u8>{2}) :
reply->AddDHCPOption(1, 53, std::vector<u8>{5});
reply->AddDHCPOption(4, 54, ip_part); // dhcp server ip
reply->AddDHCPOption(4, 51, std::vector<u8>{0, 1, 0x51, 0x80}); // lease time 24h
reply->AddDHCPOption(4, 58, std::vector<u8>{0, 1, 0x51, 0x80}); // renewal
reply->AddDHCPOption(4, 59, std::vector<u8>{0, 1, 0x51, 0x80}); // rebind
reply->AddDHCPOption(4, 1, std::vector<u8>{255, 255, 255, 0}); // submask
reply->AddDHCPOption(4, 28,
std::vector<u8>{ip_part[0], ip_part[1], ip_part[2], 255}); // broadcast ip
reply->AddDHCPOption(4, 6, ip_part); // dns server
reply->AddDHCPOption(3, 15, std::vector<u8>{0x6c, 0x61, 0x6e}); // domaine name "lan"
reply->AddDHCPOption(4, 3, ip_part); // router ip
reply->AddDHCPOption(0, 255, {}); // end
// send our emulated lan settings
udppart->checksum = Common::ComputeTCPNetworkChecksum(from, to, udppart, 308, IPPROTO_UDP);
(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
WriteToQueue(m_in_frame.get(), 0x156);
Common::UDPPacket response(bba_mac, m_fake_mac, from, to, reply.Build());
WriteToQueue(response.Build());
}
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetAvaibleSlot(u16 port)
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetAvailableSlot(u16 port)
{
if (port > 0) // existing connection?
{
for (int i = 0; i < std::size(network_ref); i++)
for (auto& ref : network_ref)
{
if (network_ref[i].ip != 0 && network_ref[i].local == port)
return &network_ref[i];
if (ref.ip != 0 && ref.local == port)
return &ref;
}
}
for (int i = 0; i < std::size(network_ref); i++)
for (auto& ref : network_ref)
{
if (network_ref[i].ip == 0)
return &network_ref[i];
if (ref.ip == 0)
return &ref;
}
return nullptr;
}
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetTCPSlot(u16 src_port, u16 dst_port, u32 ip)
{
for (int i = 0; i < std::size(network_ref); i++)
for (auto& ref : network_ref)
{
if (network_ref[i].ip == ip && network_ref[i].remote == dst_port &&
network_ref[i].local == src_port)
if (ref.ip == ip && ref.remote == dst_port && ref.local == src_port)
{
return &network_ref[i];
return &ref;
}
}
return nullptr;
}
int BuildFINFrame(StackRef* ref, u8* buf)
std::vector<u8> BuildFINFrame(StackRef* ref)
{
std::memset(buf, 0, 0x36);
auto [hwpart, ippart, tcppart] = getTcpHeaders(buf, ref->bba_mac, ref->my_mac);
*ippart = Common::IPv4Header(20, IPPROTO_TCP, ref->from, ref->to);
*tcppart = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num,
TCP_FLAG_FIN | TCP_FLAG_ACK | TCP_FLAG_RST);
tcppart->checksum =
Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart, 20, IPPROTO_TCP);
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 0x36;
return result.Build();
}
int BuildAckFrame(StackRef* ref, u8* buf)
std::vector<u8> BuildAckFrame(StackRef* ref)
{
std::memset(buf, 0, 0x36);
auto [hwpart, ippart, tcppart] = getTcpHeaders(buf, ref->bba_mac, ref->my_mac);
*ippart = Common::IPv4Header(20, IPPROTO_TCP, ref->from, ref->to);
*tcppart = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num, TCP_FLAG_ACK);
tcppart->checksum =
Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart, 20, IPPROTO_TCP);
return 0x36;
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(Common::EthernetHeader* hwdata,
Common::IPv4Header* ipdata,
Common::TCPHeader* tcpdata, u8* data)
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(tcpdata->source_port, tcpdata->destination_port, *(u32*)&ipdata->destination_addr);
if (tcpdata->properties & (TCP_FLAG_FIN | TCP_FLAG_RST))
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++;
const int size = BuildFINFrame(ref, m_in_frame.get());
WriteToQueue(m_in_frame.get(), size);
WriteToQueue(BuildFINFrame(ref));
ref->ip = 0;
ref->tcp_socket.disconnect();
}
else if (tcpdata->properties & TCP_FLAG_SIN)
else if (properties & TCP_FLAG_SIN)
{
// new connection
if (ref != nullptr)
return;
ref = GetAvaibleSlot(0);
ref = GetAvailableSlot(0);
ref->delay = GetTickCountStd();
ref->local = tcpdata->source_port;
ref->remote = tcpdata->destination_port;
ref->ack_num = htonl(tcpdata->sequence_number) + 1;
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 = htons(tcpdata->window_size);
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 = *(u32*)&ipdata->destination_addr;
ref->from.sin_port = tcpdata->destination_port;
ref->to.sin_addr.s_addr = *(u32*)&ipdata->source_addr;
ref->to.sin_port = tcpdata->source_port;
ref->bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
ref->my_mac = fake_mac;
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
std::memset(m_in_frame.get(), 0, 0x100);
auto [hwpart, ippart, tcppart] = getTcpHeaders(m_in_frame.get(), ref->bba_mac, ref->my_mac);
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);
*ippart = Common::IPv4Header(28, IPPROTO_TCP, ref->from, ref->to);
*tcppart = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num,
0x70 | TCP_FLAG_SIN | TCP_FLAG_ACK);
const u8 options[] = {0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x01, 0x01};
std::memcpy(&m_in_frame[0x36], options, std::size(options));
// do checksum
tcppart->checksum =
Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart, 28, IPPROTO_TCP);
result.tcp_options = {0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x01, 0x01};
ref->seq_num++;
target = sf::IpAddress(htonl(*(u32*)&ipdata->destination_addr));
ref->tcp_socket.connect(target, ntohs(tcpdata->destination_port));
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 = *(u32*)ipdata->destination_addr;
ref->ip = Common::BitCast<u32>(ip_header.destination_addr);
std::memcpy(&ref->tcp_buffers[0].data, m_in_frame.get(), 0x3e);
ref->tcp_buffers[0].data_size = 0x3e;
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;
@ -322,38 +259,40 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(Common::EthernetHeader* h
if (ref == nullptr)
return; // not found
const int c = (tcpdata->properties & 0xf0) >> 2; // header size
const int size = ntohs(ipdata->total_len) - 20 - c;
const u32 this_seq = ntohl(tcpdata->sequence_number);
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 data
if ((int)(this_seq - ref->ack_num) >= 0)
// only if contain data
if (static_cast<int>(this_seq - ref->ack_num) >= 0 && data.size() >= size)
{
ref->tcp_socket.send(data, size);
ref->tcp_socket.send(data.data(), size);
ref->ack_num += size;
}
// send ack
BuildAckFrame(ref, m_in_frame.get());
WriteToQueue(m_in_frame.get(), 0x36);
WriteToQueue(BuildAckFrame(ref));
}
// update windows size
ref->window_size = ntohs(tcpdata->window_size);
ref->window_size = ntohs(tcp_header.window_size);
// clear any ack data
if (tcpdata->properties & TCP_FLAG_ACK)
if (ntohs(tcp_header.properties) & TCP_FLAG_ACK)
{
const u32 ack_num = ntohl(tcpdata->acknowledgement_number);
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::TCPHeader* tcppart = (Common::TCPHeader*)&tcp_buf.data[0x22];
const u32 seq_end =
tcp_buf.seq_id + tcp_buf.data_size - ((tcppart->properties & 0xf0) >> 2) - 34;
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
@ -363,36 +302,29 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(Common::EthernetHeader* h
}
// partial data, adjust the packet for next ack
const u16 ack_size = ack_num - tcp_buf.seq_id;
const u16 new_data_size = tcp_buf.data_size - 0x36 - ack_size;
std::memmove(&tcp_buf.data[0x36], &tcp_buf.data[0x36 + ack_size], new_data_size);
tcp_buf.data_size -= ack_size;
tcp_packet->data.erase(tcp_packet->data.begin(), tcp_packet->data.begin() + ack_size);
tcp_buf.seq_id += ack_size;
tcppart->sequence_number = htonl(tcp_buf.seq_id);
Common::IPv4Header* ippart = (Common::IPv4Header*)&tcp_buf.data[14];
ippart->total_len = htons(tcp_buf.data_size - 14);
tcppart->checksum = 0;
tcppart->checksum = Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart,
new_data_size + 20, IPPROTO_TCP);
tcp_packet->tcp_header.sequence_number = htonl(tcp_buf.seq_id);
tcp_buf.data = tcp_packet->Build();
}
}
}
}
/// <summary>
/// This is a litle hack, Mario Kart open some UDP port
/// and listen to it. We open it on our side manualy.
/// </summary>
// 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 = GetAvaibleSlot(htons(port));
StackRef* ref = GetAvailableSlot(htons(port));
if (ref == nullptr || ref->ip != 0)
return;
ref->ip = 0x08080808; // change for ip
ref->ip = m_router_ip; // change for ip
ref->local = htons(port);
ref->remote = htons(port);
ref->type = 17;
ref->bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
ref->my_mac = fake_mac;
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;
@ -401,144 +333,176 @@ void CEXIETHERNET::BuiltInBBAInterface::InitUDPPort(u16 port)
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(Common::EthernetHeader* hwdata,
Common::IPv4Header* ipdata,
Common::UDPHeader* udpdata, u8* data)
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);
if (*(u32*)ipdata->destination_addr == 0)
*(u32*)ipdata->destination_addr = m_router_ip;
// dns request
StackRef* ref = GetAvaibleSlot(udpdata->source_port);
StackRef* ref = GetAvailableSlot(udp_header.source_port);
if (ref->ip == 0)
{
ref->ip = *(u32*)ipdata->destination_addr; // change for ip
ref->local = udpdata->source_port;
ref->remote = udpdata->destination_port;
ref->type = 17;
ref->bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
ref->my_mac = fake_mac;
ref->from.sin_addr.s_addr = *(u32*)&ipdata->destination_addr;
ref->from.sin_port = udpdata->destination_port;
ref->to.sin_addr.s_addr = *(u32*)&ipdata->source_addr;
ref->to.sin_port = udpdata->source_port;
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(udpdata->source_port)) != sf::Socket::Done &&
ref->udp_socket.bind(sf::Socket::AnyPort) != sf::Socket::Done)
if (ref->udp_socket.bind(htons(udp_header.source_port)) != sf::Socket::Done)
{
ERROR_LOG_FMT(SP1, "Couldn't open UDP socket");
return;
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(udpdata->destination_port) == 1900)
{
InitUDPPort(26512); // MK DD and 1080
InitUDPPort(26502); // Air Ride
if (*(u32*)ipdata->destination_addr == 0xFAFFFFEF) // force real broadcast
*(u32*)ipdata->destination_addr = 0xFFFFFFFF; // Multi cast cannot be read
if (udpdata->length > 150)
{
// Quick hack to unlock the connection, throw it back at him
Common::EthernetHeader* hwpart = (Common::EthernetHeader*)m_in_frame.get();
Common::IPv4Header* ippart = (Common::IPv4Header*)&m_in_frame[14];
std::memcpy(m_in_frame.get(), hwdata, ntohs(ipdata->total_len) + 14);
hwpart->destination = hwdata->source;
hwpart->source = hwdata->destination;
*(u32*)ippart->destination_addr = *(u32*)ipdata->source_addr;
*(u32*)ippart->source_addr = *(u32*)ipdata->destination_addr;
WriteToQueue(m_in_frame.get(), ntohs(ipdata->total_len) + 14);
}
}
if (ntohs(udpdata->destination_port) == 53)
{
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(*(u32*)ipdata->destination_addr));
}
ref->udp_socket.send(data, ntohs(udpdata->length) - 8, target, ntohs(udpdata->destination_port));
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)
{
int offset = 0;
std::lock_guard<std::mutex> lock(m_mtx);
const Common::PacketView view(frame, size);
std::memcpy(m_out_frame.get(), frame, size);
std::lock_guard<std::mutex> lock(mtx);
// handle the packet data
Common::EthernetHeader* hwdata = (Common::EthernetHeader*)m_out_frame.get();
if (hwdata->ethertype == 0x08) // IPV4
const std::optional<u16> ethertype = view.GetEtherType();
if (!ethertype.has_value())
{
// IP sub
Common::IPv4Header* ipdata = (Common::IPv4Header*)&m_out_frame[14];
offset = Common::EthernetHeader::SIZE + (ipdata->version_ihl & 0xf) * 4;
switch (ipdata->protocol)
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:
{
Common::UDPHeader* udpdata = (Common::UDPHeader*)&m_out_frame[offset];
offset += Common::UDPHeader::SIZE;
if (ntohs(udpdata->destination_port) == 67)
const auto udp_packet = view.GetUDPPacket();
if (!udp_packet.has_value())
{
Common::DHCPBody* request = (Common::DHCPBody*)&m_out_frame[offset];
HandleDHCP(hwdata, udpdata, request);
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(hwdata, ipdata, udpdata, &m_out_frame[offset]);
HandleUDPFrame(*udp_packet);
}
break;
}
case IPPROTO_TCP:
{
Common::TCPHeader* tcpdata = (Common::TCPHeader*)&m_out_frame[offset];
offset += (tcpdata->properties & 0xf0) >> 2;
HandleTCPFrame(hwdata, ipdata, tcpdata, &m_out_frame[offset]);
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;
}
if (hwdata->ethertype == 0x608) // arp
case Common::ARP_ETHERTYPE:
{
Common::ARPHeader* arpdata = (Common::ARPHeader*)&m_out_frame[14];
HandleARP(hwdata, arpdata);
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;
}
size_t TryGetDataFromSocket(StackRef* ref, u8* buffer)
std::optional<std::vector<u8>> TryGetDataFromSocket(StackRef* ref)
{
size_t datasize = 0; // this will be filled by the socket read later
size_t datasize = 0; // Set by socket.receive using a non-const reference
unsigned short remote_port;
switch (ref->type)
{
case IPPROTO_UDP:
ref->udp_socket.receive(&buffer[0x2a], 1500, datasize, ref->target, remote_port);
{
std::array<u8, MAX_UDP_LENGTH> buffer;
ref->udp_socket.receive(buffer.data(), MAX_UDP_LENGTH, datasize, ref->target, remote_port);
if (datasize > 0)
{
std::memset(buffer, 0, 0x2a);
auto [hwpart, ipdata, udpdata] = getUdpHeaders(buffer, ref->bba_mac, ref->my_mac);
ref->from.sin_port = htons(remote_port);
ref->from.sin_addr.s_addr = htonl(ref->target.toInteger());
*ipdata = Common::IPv4Header((u16)(datasize + 8), IPPROTO_UDP, ref->from, ref->to);
*udpdata = Common::UDPHeader(ref->from, ref->to, (u16)datasize);
udpdata->checksum = Common::ComputeTCPNetworkChecksum(ref->from, ref->to, udpdata,
(u16)(datasize + 8), IPPROTO_UDP);
datasize += 0x2a;
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;
@ -554,30 +518,25 @@ size_t TryGetDataFromSocket(StackRef* ref, u8* buffer)
// 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[0x36], 440, datasize);
st = ref->tcp_socket.receive(buffer.data(), MAX_TCP_LENGTH, datasize);
if (datasize > 0)
{
std::memset(buffer, 0, 0x36);
auto [hwpart, ipdata, tcpdata] = getTcpHeaders(buffer, ref->bba_mac, ref->my_mac);
*ipdata = Common::IPv4Header((u16)(datasize + 20), IPPROTO_TCP, ref->from, ref->to);
*tcpdata = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num, TCP_FLAG_ACK);
tcpdata->checksum = Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcpdata,
(u16)(datasize + 20), IPPROTO_TCP);
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->data_size = (u16)datasize + 0x36;
tcp_buffer->tick = GetTickCountStd();
std::memcpy(&tcp_buffer->data[0], buffer, datasize + 0x36);
tcp_buffer->data = packet.Build();
tcp_buffer->seq_id = ref->seq_num;
tcp_buffer->used = true;
ref->seq_num += (u32)datasize;
ref->seq_num += static_cast<u32>(datasize);
ref->poke_time = GetTickCountStd();
datasize += 0x36;
return tcp_buffer->data;
}
if (GetTickCountStd() - ref->delay > 3000)
{
@ -585,13 +544,33 @@ size_t TryGetDataFromSocket(StackRef* ref, u8* buffer)
{
ref->ip = 0;
ref->tcp_socket.disconnect();
datasize = BuildFINFrame(ref, buffer);
return BuildFINFrame(ref);
}
}
break;
}
return datasize;
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)
@ -613,15 +592,21 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
if ((wp - rp) >= 8)
continue;
std::lock_guard<std::mutex> lock(self->mtx);
std::lock_guard<std::mutex> lock(self->m_mtx);
// process queue file first
if (self->queue_read != self->queue_write)
if (self->m_queue_read != self->m_queue_write)
{
datasize = self->queue_data[self->queue_read].size();
std::memcpy(self->m_eth_ref->mRecvBuffer.get(), &self->queue_data[self->queue_read][0],
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->queue_read++;
self->queue_read &= 15;
self->m_queue_read++;
self->m_queue_read &= 15;
}
else
{
@ -630,9 +615,13 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
{
if (net_ref.ip == 0)
continue;
datasize = TryGetDataFromSocket(&net_ref, self->m_eth_ref->mRecvBuffer.get());
if (datasize > 0)
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;
}
}
}
@ -647,26 +636,24 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
continue;
tcp_buf.tick = GetTickCountStd();
// late data, resend
if (((self->queue_write + 1) & 15) != self->queue_read)
// timmed out packet, resend
if (((self->m_queue_write + 1) & 15) != self->m_queue_read)
{
self->WriteToQueue(&tcp_buf.data[0], tcp_buf.data_size);
self->WriteToQueue(tcp_buf.data);
}
}
}
if (datasize > 0)
{
u8* b = &self->m_eth_ref->mRecvBuffer[0];
Common::EthernetHeader* hwdata = (Common::EthernetHeader*)b;
if (hwdata->ethertype == 0x8) // IP_PROTOCOL
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)
{
Common::IPv4Header* ipdata = (Common::IPv4Header*)&b[14];
ipdata->identification = ntohs(++self->ip_frame_id);
ipdata->header_checksum = 0;
ipdata->header_checksum = htons(Common::ComputeNetworkChecksum(ipdata, 20));
SetIPIdentification(buffer, datasize, ++self->m_ip_frame_id);
}
self->m_eth_ref->mRecvBufferLength = datasize > 64 ? (u32)datasize : 64;
self->m_eth_ref->mRecvBufferLength = datasize > 64 ? static_cast<u32>(datasize) : 64;
self->m_eth_ref->RecvHandlePacket();
}
}
@ -694,7 +681,7 @@ void CEXIETHERNET::BuiltInBBAInterface::RecvStop()
}
net_ref.ip = 0;
}
queue_read = 0;
queue_write = 0;
m_queue_read = 0;
m_queue_write = 0;
}
} // namespace ExpansionInterface

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _WIN32
#include <WinSock2.h>
#else
@ -12,24 +13,25 @@
#include "Common/CommonTypes.h"
#include "Common/Network.h"
constexpr u16 TCP_FLAG_SIN = 0x200;
constexpr u16 TCP_FLAG_ACK = 0x1000;
constexpr u16 TCP_FLAG_PSH = 0x800;
constexpr u16 TCP_FLAG_FIN = 0x100;
constexpr u16 TCP_FLAG_RST = 0x400;
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;
u16 data_size;
std::array<u8, 2048> data;
std::vector<u8> data;
};
struct StackRef

View file

@ -18,8 +18,6 @@
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/Memmap.h"
//#define BBA_TRACK_PAGE_PTRS
namespace ExpansionInterface
{
// XXX: The BBA stores multi-byte elements as little endian.

View file

@ -425,8 +425,6 @@ private:
: NetworkInterface(eth_ref), m_dns_ip(std::move(dns_ip)), m_local_ip(std::move(local_ip))
{
}
public:
bool Activate() override;
void Deactivate() override;
bool IsActivated() override;
@ -438,12 +436,12 @@ private:
private:
std::string m_mac_id;
std::string m_dns_ip;
bool active = false;
u16 ip_frame_id = 0;
u8 queue_read = 0;
u8 queue_write = 0;
std::array<std::vector<u8>, 16> queue_data;
std::mutex mtx;
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;
@ -451,25 +449,21 @@ private:
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::unique_ptr<u8[]> m_in_frame;
std::unique_ptr<u8[]> m_out_frame;
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);
Common::MACAddress fake_mac{};
#endif
void WriteToQueue(const u8* data, int length);
void HandleARP(Common::EthernetHeader* hwdata, Common::ARPHeader* arpdata);
void HandleDHCP(Common::EthernetHeader* hwdata, Common::UDPHeader* udpdata,
Common::DHCPBody* request);
StackRef* GetAvaibleSlot(u16 port);
void WriteToQueue(const std::vector<u8>& data);
StackRef* GetAvailableSlot(u16 port);
StackRef* GetTCPSlot(u16 src_port, u16 dst_port, u32 ip);
void HandleTCPFrame(Common::EthernetHeader* hwdata, Common::IPv4Header* ipdata,
Common::TCPHeader* tcpdata, u8* data);
void HandleARP(const Common::ARPPacket& packet);
void HandleDHCP(const Common::UDPPacket& packet);
void HandleTCPFrame(const Common::TCPPacket& packet);
void InitUDPPort(u16 port);
void HandleUDPFrame(Common::EthernetHeader* hwdata, Common::IPv4Header* ipdata,
Common::UDPHeader* udpdata, u8* data);
void HandleUDPFrame(const Common::UDPPacket& packet);
};
std::unique_ptr<NetworkInterface> m_network_interface;

View file

@ -50,7 +50,7 @@ void BroadbandAdapterSettingsDialog::InitControls()
case Type::BuiltIn:
address_label = new QLabel(tr("Enter the DNS server to use:"));
address_placeholder = QString::fromStdString("8.8.8.8");
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"));