diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp index bdc08f21a4..a2483b00e9 100644 --- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp +++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.cpp @@ -68,6 +68,8 @@ bool CEXIETHERNET::BuiltInBBAInterface::Activate() buf.reserve(2048); // Workaround to get the host IP (might not be accurate) + // TODO: Fix the JNI crash and use GetSystemDefaultInterface() + // - https://pastebin.com/BFpmnxby (see https://dolp.in/pr10920) const u32 ip = m_local_ip.empty() ? sf::IpAddress::getLocalAddress().toInteger() : sf::IpAddress(m_local_ip).toInteger(); m_current_ip = htonl(ip); @@ -334,7 +336,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket& ref->seq_num++; target = sf::IpAddress(ntohl(destination_ip)); - ref->tcp_socket.connect(target, ntohs(tcp_header.destination_port)); + ref->tcp_socket.Connect(target, ntohs(tcp_header.destination_port), m_current_ip); ref->ready = false; ref->ip = Common::BitCast(ip_header.destination_addr); @@ -420,7 +422,7 @@ void CEXIETHERNET::BuiltInBBAInterface::InitUDPPort(u16 port) ref->to.sin_addr.s_addr = m_current_ip; ref->to.sin_port = htons(port); ref->udp_socket.setBlocking(false); - if (ref->udp_socket.bind(port) != sf::Socket::Done) + if (ref->udp_socket.Bind(port, m_current_ip) != 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); @@ -450,12 +452,12 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket& 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(ntohs(udp_header.source_port)) != sf::Socket::Done) + if (ref->udp_socket.Bind(ntohs(udp_header.source_port), m_current_ip) != sf::Socket::Done) { PanicAlertFmt( "Port {:x} is already in use, this game might not work as intented in LAN Mode.", htons(udp_header.source_port)); - if (ref->udp_socket.bind(sf::Socket::AnyPort) != sf::Socket::Done) + if (ref->udp_socket.Bind(sf::Socket::AnyPort, m_current_ip) != sf::Socket::Done) { ERROR_LOG_FMT(SP1, "Couldn't open UDP socket"); return; @@ -710,3 +712,22 @@ void CEXIETHERNET::BuiltInBBAInterface::RecvStop() m_queue_write = 0; } } // namespace ExpansionInterface + +BbaTcpSocket::BbaTcpSocket() = default; + +sf::Socket::Status BbaTcpSocket::Connect(const sf::IpAddress& dest, u16 port, u32 net_ip) +{ + sockaddr_in addr; + addr.sin_addr.s_addr = net_ip; + addr.sin_family = AF_INET; + addr.sin_port = 0; + ::bind(getHandle(), reinterpret_cast(&addr), sizeof(addr)); + return this->connect(dest, port); +} + +BbaUdpSocket::BbaUdpSocket() = default; + +sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip) +{ + return this->bind(port, sf::IpAddress(ntohl(net_ip))); +} diff --git a/Source/Core/Core/HW/EXI/BBA/BuiltIn.h b/Source/Core/Core/HW/EXI/BBA/BuiltIn.h index e1117c8b7e..35e74eae43 100644 --- a/Source/Core/Core/HW/EXI/BBA/BuiltIn.h +++ b/Source/Core/Core/HW/EXI/BBA/BuiltIn.h @@ -34,6 +34,27 @@ struct TcpBuffer std::vector data; }; +// Socket helper classes to ensure network interface consistency. +// +// If the socket isn't bound, the system will pick the interface to use automatically. +// This might result in the source IP address changing when talking to the same peer +// and multiple interfaces/IP addresses can reach the socket's peer. +class BbaTcpSocket : public sf::TcpSocket +{ +public: + BbaTcpSocket(); + + sf::Socket::Status Connect(const sf::IpAddress& dest, u16 port, u32 net_ip); +}; + +class BbaUdpSocket : public sf::UdpSocket +{ +public: + BbaUdpSocket(); + + sf::Socket::Status Bind(u16 port, u32 net_ip); +}; + struct StackRef { u32 ip; @@ -52,7 +73,7 @@ struct StackRef sockaddr_in to; Common::MACAddress bba_mac{}; Common::MACAddress my_mac{}; - sf::UdpSocket udp_socket; - sf::TcpSocket tcp_socket; + BbaUdpSocket udp_socket; + BbaTcpSocket tcp_socket; u64 poke_time; };