diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index 756dd0381e..d03b537e25 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -17,6 +17,7 @@ #include +#include "Common/BitUtils.h" #include "Common/Random.h" #include "Common/StringUtil.h" @@ -114,6 +115,11 @@ u16 IPv4Header::Size() const return static_cast(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(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& 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& data) +{ + if (data.size() < DHCPBody::SIZE) + return; + body = Common::BitCastPtr(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& params) +{ + if (params.size() > 255) + return; + std::vector opt = {fnc, u8(params.size())}; + opt.insert(opt.end(), params.begin(), params.end()); + options.emplace_back(std::move(opt)); +} + +std::vector DHCPPacket::Build() const +{ + const u8* body_ptr = reinterpret_cast(&body); + std::vector result(body_ptr, body_ptr + DHCPBody::SIZE); + + for (auto& opt : options) + { + result.insert(result.end(), opt.begin(), opt.end()); + } + const std::vector 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(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(from)); + const u32 destination_addr = ntohl(Common::BitCast(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(tcp_checksum)); } +ARPPacket::ARPPacket() = default; + +u16 ARPPacket::Size() const +{ + return static_cast(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 ARPPacket::Build() const +{ + std::vector result; + result.reserve(EthernetHeader::SIZE + ARPHeader::SIZE); + const u8* eth_ptr = reinterpret_cast(ð_header); + result.insert(result.end(), eth_ptr, eth_ptr + EthernetHeader::SIZE); + const u8* arp_ptr = reinterpret_cast(&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 TCPPacket::Build() +{ + std::vector result; + result.reserve(Size()); + + // recalc size + ip_header.total_len = htons(static_cast(IPv4Header::SIZE + ipv4_options.size() + + TCPHeader::SIZE + tcp_options.size() + data.size())); + + // copy data + const u8* eth_ptr = reinterpret_cast(ð_header); + result.insert(result.end(), eth_ptr, eth_ptr + EthernetHeader::SIZE); + const u8* ip_ptr = reinterpret_cast(&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((tcp_options.size() + TCPHeader::SIZE) & 0x3c) << 10); + tcp_header.properties = htons(props); + const u8* tcp_ptr = reinterpret_cast(&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(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(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& payload) +{ + eth_header.destination = destination; + eth_header.source = source; + eth_header.ethertype = htons(IPV4_ETHERTYPE); + + ip_header = Common::IPv4Header(static_cast(payload.size() + Common::UDPHeader::SIZE), + IPPROTO_UDP, from, to); + udp_header = Common::UDPHeader(from, to, static_cast(payload.size())); + data = payload; +} + +std::vector UDPPacket::Build() +{ + std::vector result; + result.reserve(Size()); + + // recalc size + ip_header.total_len = htons( + static_cast(IPv4Header::SIZE + ipv4_options.size() + UDPHeader::SIZE + data.size())); + udp_header.length = htons(static_cast(UDPHeader::SIZE + data.size())); + + // copy data + const u8* eth_ptr = reinterpret_cast(ð_header); + result.insert(result.end(), eth_ptr, eth_ptr + EthernetHeader::SIZE); + const u8* ip_ptr = reinterpret_cast(&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(&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(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(MIN_SIZE + data.size() + ipv4_options.size()); +} + +PacketView::PacketView(const u8* ptr, std::size_t size) : m_ptr(ptr), m_size(size) +{ +} + +std::optional PacketView::GetEtherType() const +{ + if (m_size < EthernetHeader::SIZE) + return std::nullopt; + const std::size_t offset = offsetof(EthernetHeader, ethertype); + return ntohs(Common::BitCastPtr(m_ptr + offset)); +} + +std::optional PacketView::GetARPPacket() const +{ + if (m_size < ARPPacket::SIZE) + return std::nullopt; + return Common::BitCastPtr(m_ptr); +} + +std::optional PacketView::GetIPProto() const +{ + if (m_size < EthernetHeader::SIZE + IPv4Header::SIZE) + return std::nullopt; + return m_ptr[EthernetHeader::SIZE + offsetof(IPv4Header, protocol)]; +} + +std::optional PacketView::GetTCPPacket() const +{ + if (m_size < TCPPacket::MIN_SIZE) + return std::nullopt; + TCPPacket result; + result.eth_header = Common::BitCastPtr(m_ptr); + result.ip_header = Common::BitCastPtr(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(m_ptr + EthernetHeader::SIZE + IPv4Header::SIZE, m_ptr + offset); + result.tcp_header = Common::BitCastPtr(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(m_ptr + offset + TCPHeader::SIZE, m_ptr + data_offset); + result.data = std::vector(m_ptr + data_offset, m_ptr + end); + + return result; +} + +std::optional PacketView::GetUDPPacket() const +{ + if (m_size < UDPPacket::MIN_SIZE) + return std::nullopt; + UDPPacket result; + result.eth_header = Common::BitCastPtr(m_ptr); + result.ip_header = Common::BitCastPtr(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(m_ptr + EthernetHeader::SIZE + IPv4Header::SIZE, m_ptr + offset); + result.udp_header = Common::BitCastPtr(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(m_ptr + data_offset, m_ptr + end); + + return result; +} + NetworkErrorState SaveNetworkErrorState() { return { diff --git a/Source/Core/Common/Network.h b/Source/Core/Common/Network.h index eec09234a7..f9706c0328 100644 --- a/Source/Core/Common/Network.h +++ b/Source/Core/Common/Network.h @@ -36,6 +36,12 @@ enum DHCPConst using MACAddress = std::array; constexpr std::size_t IPV4_ADDR_LEN = 4; +using IPAddress = std::array; +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 source_addr{}; - std::array 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& 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& data); + void AddOption(u8 fnc, const std::vector& params); + std::vector Build() const; + + DHCPBody body; + std::vector> 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 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 Build(); + u16 Size() const; + + EthernetHeader eth_header; + IPv4Header ip_header; + TCPHeader tcp_header; + std::vector ipv4_options; + std::vector tcp_options; + std::vector 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& payload); + std::vector Build(); + u16 Size() const; + + EthernetHeader eth_header; + IPv4Header ip_header; + UDPHeader udp_header; + + std::vector ipv4_options; + std::vector 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 GetEtherType() const; + std::optional GetARPPacket() const; + std::optional GetIPProto() const; + std::optional GetTCPPacket() const; + std::optional 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 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); diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 0dba56339b..99a143f2bd 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -723,4 +723,4 @@ endif() if(MSVC) # Add precompiled header target_link_libraries(core PRIVATE use_pch) -endif() \ No newline at end of file +endif() diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp index bba928e660..8dfbf59764 100644 --- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp +++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp @@ -3,13 +3,13 @@ #include +#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(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 -getTcpHeaders(u8* data, Common::MACAddress dest, Common::MACAddress src) -{ - SetHardwareInfo(data, dest, src); - return std::tuple( - (Common::EthernetHeader*)data, (Common::IPv4Header*)&data[14], - (Common::TCPHeader*)&data[0x22]); -} - -std::tuple -getUdpHeaders(u8* data, Common::MACAddress dest, Common::MACAddress src) -{ - SetHardwareInfo(data, dest, src); - return std::tuple( - (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(9004); - m_out_frame = std::make_unique(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& 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(&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 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(&m_router_ip); + const std::vector ip_part(router_ip_ptr, router_ip_ptr + sizeof(m_router_ip)); - *udppart = Common::UDPHeader(from, to, 300); + const std::vector 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(&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{2}) : - reply->AddDHCPOption(1, 53, std::vector{5}); - reply->AddDHCPOption(4, 54, ip_part); // dhcp server ip - reply->AddDHCPOption(4, 51, std::vector{0, 1, 0x51, 0x80}); // lease time 24h - reply->AddDHCPOption(4, 58, std::vector{0, 1, 0x51, 0x80}); // renewal - reply->AddDHCPOption(4, 59, std::vector{0, 1, 0x51, 0x80}); // rebind - reply->AddDHCPOption(4, 1, std::vector{255, 255, 255, 0}); // submask - reply->AddDHCPOption(4, 28, - std::vector{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{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 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 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(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(ip_header.destination_addr); + ref->from.sin_port = tcp_header.destination_port; + ref->to.sin_addr.s_addr = Common::BitCast(ip_header.source_addr); + ref->to.sin_port = tcp_header.source_port; + ref->bba_mac = Common::BitCastPtr(&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(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(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(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(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(); } } } } -/// -/// This is a litle hack, Mario Kart open some UDP port -/// and listen to it. We open it on our side manualy. -/// +// 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(&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(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(&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(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(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(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 lock(m_mtx); + const Common::PacketView view(frame, size); - std::memcpy(m_out_frame.get(), frame, size); - std::lock_guard lock(mtx); - - // handle the packet data - Common::EthernetHeader* hwdata = (Common::EthernetHeader*)m_out_frame.get(); - if (hwdata->ethertype == 0x08) // IPV4 + const std::optional 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 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> 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 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 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 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(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(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(ip_id_ptr) = htons(value); + + u8* const ip_checksum_ptr = ip_ptr + offsetof(Common::IPv4Header, header_checksum); + auto checksum_bitcast_ptr = Common::BitCastPtr(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 lock(self->mtx); + std::lock_guard 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(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(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 diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.h b/Source/Core/Core/HW/EXI/BBA/BuiltIn.h index 7b3b5415c5..e1117c8b7e 100644 --- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.h +++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.h @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once + #ifdef _WIN32 #include #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 data; + std::vector data; }; struct StackRef diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp index 311d7ccda2..416b2c362d 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp @@ -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. diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h index ea29383cbf..3ca1f26b01 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h @@ -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, 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, 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 network_ref{}; // max 10 at same time, i think most gc game had a // limit of 8 in the gc framework - std::unique_ptr m_in_frame; - std::unique_ptr 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& 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 m_network_interface; diff --git a/Source/Core/DolphinQt/Settings/BroadbandAdapterSettingsDialog.cpp b/Source/Core/DolphinQt/Settings/BroadbandAdapterSettingsDialog.cpp index 8b6d96df02..8a8d0c752c 100644 --- a/Source/Core/DolphinQt/Settings/BroadbandAdapterSettingsDialog.cpp +++ b/Source/Core/DolphinQt/Settings/BroadbandAdapterSettingsDialog.cpp @@ -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"));