diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index 2c078779b9dd..073758fec972 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -57,6 +57,11 @@ #include "jni/AndroidCommon/AndroidCommon.h" #endif +#ifdef __linux__ +#include +#include +#endif + namespace IOS::HLE { enum SOResultCode : s32 @@ -163,50 +168,170 @@ static s32 MapWiiSockOptNameToNative(u32 optname) } // u32 values are in little endian (i.e. 0x0100007f means 127.0.0.1) +struct InterfaceRouting +{ + u32 index; + u32 destination; + u32 netmask; + u32 gateway; +}; + struct DefaultInterface { - u32 inet; // IPv4 address - u32 netmask; // IPv4 subnet mask - u32 broadcast; // IPv4 broadcast address + u32 inet; // IPv4 address + u32 netmask; // IPv4 subnet mask + u32 broadcast; // IPv4 broadcast address + std::vector routing_table; // IPv4 routing table }; -static std::optional GetSystemDefaultInterface() +static std::vector GetSystemInterfaceRouting() { + std::vector routing_table; + #ifdef _WIN32 - std::unique_ptr forward_table; DWORD forward_table_size = 0; + std::unique_ptr forward_table; if (GetIpForwardTable(nullptr, &forward_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER) { forward_table = std::unique_ptr((PMIB_IPFORWARDTABLE) operator new(forward_table_size)); } - std::unique_ptr ip_table; - DWORD ip_table_size = 0; - if (GetIpAddrTable(nullptr, &ip_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER) - { - ip_table = std::unique_ptr((PMIB_IPADDRTABLE) operator new(ip_table_size)); - } - - // find the interface IP used for the default route and use that - NET_IFINDEX ifIndex = NET_IFINDEX_UNSPECIFIED; DWORD result = GetIpForwardTable(forward_table.get(), &forward_table_size, FALSE); // can return ERROR_MORE_DATA on XP even after the first call while (result == NO_ERROR || result == ERROR_MORE_DATA) { for (DWORD i = 0; i < forward_table->dwNumEntries; ++i) + routing_table.push_back( + {forward_table->table[i].dwForwardIfIndex, forward_table->table[i].dwForwardDest, + forward_table->table[i].dwForwardMask, forward_table->table[i].dwForwardNextHop}); + + if (result == NO_ERROR) + break; + + result = GetIpForwardTable(forward_table.get(), &forward_table_size, FALSE); + } + +#elif defined(__linux__) + const int buff_size = 8192; + unsigned int msgSeq = 0; + int sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (sock < 0) + return {}; + + Common::ScopeGuard socket_guard{[sock] { close(sock); }}; + char msgbuf[buff_size]; + memset(msgbuf, 0, buff_size); + auto nlMsg = (struct nlmsghdr*)msgbuf; + auto rtMsg = (struct rtmsg*)NLMSG_DATA(nlMsg); + unsigned int pid = getpid(); + + // prepare command/netlink packet + nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message. + nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . + + nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. + nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet. + nlMsg->nlmsg_pid = pid; // PID of process sending the request. + + // ship it + if (send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0) + { + return {}; + } + + // read response + int readlen, msglen = 0; + memset(msgbuf, 0, buff_size); + + do + { + auto bufPtr = msgbuf + msglen; + readlen = recv(sock, bufPtr, buff_size - msglen, 0); + if (readlen < 0) + { + return {}; + } + + nlMsg = (struct nlmsghdr*)bufPtr; + if (NLMSG_OK(nlMsg, readlen) == 0 || nlMsg->nlmsg_type == NLMSG_ERROR) + { + return {}; + } + + // if its the last msg, we are done + if (nlMsg->nlmsg_type == NLMSG_DONE) { - if (forward_table->table[i].dwForwardDest == 0) + break; + } + + msglen += readlen; + if ((nlMsg->nlmsg_flags & NLM_F_MULTI) == 0) + { + break; + } + + } while (nlMsg->nlmsg_seq != msgSeq || nlMsg->nlmsg_pid != pid); + + // parse the response + nlMsg = (struct nlmsghdr*)msgbuf; + for (; NLMSG_OK(nlMsg, msglen); nlMsg = NLMSG_NEXT(nlMsg, msglen)) + { + // only parse AF_INET, as the wii is ipv4 only + if ((rtMsg->rtm_family != AF_INET)) + continue; + + auto rtAttr = (struct rtattr*)RTM_RTA(rtMsg); + auto rtLen = RTM_PAYLOAD(nlMsg); + InterfaceRouting route = {}; + + // get netmask from the destination ip length and the rest from the response + for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) + { + switch (rtAttr->rta_type) { - ifIndex = forward_table->table[i].dwForwardIfIndex; + case RTA_GATEWAY: + route.gateway = *(unsigned int*)RTA_DATA(rtAttr); + break; + case RTA_DST: + route.destination = *(unsigned int*)RTA_DATA(rtAttr); break; + case RTA_OIF: + default: + continue; } } + route.netmask = route.destination == 0 && rtMsg->rtm_dst_len == 0 ? + 0 : + 0xFFFFFFFF >> (32 - rtMsg->rtm_dst_len); + routing_table.push_back(route); + return routing_table; + } +#endif - if (result == NO_ERROR || ifIndex != NET_IFINDEX_UNSPECIFIED) - break; + return routing_table; +} - result = GetIpForwardTable(forward_table.get(), &forward_table_size, FALSE); +static std::optional GetSystemDefaultInterface() +{ + auto routing_table = GetSystemInterfaceRouting(); +#ifdef _WIN32 + std::unique_ptr ip_table; + DWORD ip_table_size = 0; + if (GetIpAddrTable(nullptr, &ip_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER) + { + ip_table = std::unique_ptr((PMIB_IPADDRTABLE) operator new(ip_table_size)); + } + + // find the interface IP used for the default route and use that + NET_IFINDEX ifIndex = NET_IFINDEX_UNSPECIFIED; + for (InterfaceRouting route : routing_table) + { + if (route.destination != 0) + continue; + + ifIndex = route.index; + break; } if (ifIndex != NET_IFINDEX_UNSPECIFIED && @@ -216,7 +341,7 @@ static std::optional GetSystemDefaultInterface() { const auto& entry = ip_table->table[i]; if (entry.dwIndex == ifIndex) - return DefaultInterface{entry.dwAddr, entry.dwMask, entry.dwBCastAddr}; + return DefaultInterface{entry.dwAddr, entry.dwMask, entry.dwBCastAddr, routing_table}; } } #elif defined(__ANDROID__) @@ -224,8 +349,12 @@ static std::optional GetSystemDefaultInterface() const u32 prefix_length = GetNetworkPrefixLength(); const u32 netmask = (1 << prefix_length) - 1; const u32 gateway = GetNetworkGateway(); + // this isnt fully correct, but this will make calls to get the routing table at least return the + // gateway + if (routing_table.empty()) + routing_table = {{0, 0, 0, gateway}}; if (addr || netmask || gateway) - return DefaultInterface{addr, netmask, gateway}; + return DefaultInterface{addr, netmask, gateway, routing_table}; #else // Assume that the address that is used to access the Internet corresponds // to the default interface. @@ -265,8 +394,13 @@ static std::optional GetSystemDefaultInterface() if (iface->ifa_addr && iface->ifa_addr->sa_family == AF_INET && get_addr(iface->ifa_addr) == default_interface_address->s_addr) { + // this isnt fully correct, but this will make calls to get the routing table at least return + // the gateway + if (routing_table.empty()) + routing_table = {{0, 0, 0, get_addr(iface->ifa_dstaddr)}}; + return DefaultInterface{get_addr(iface->ifa_addr), get_addr(iface->ifa_netmask), - get_addr(iface->ifa_broadaddr)}; + get_addr(iface->ifa_broadaddr), routing_table}; } } #endif @@ -277,8 +411,11 @@ static DefaultInterface GetSystemDefaultInterfaceOrFallback() { static const u32 FALLBACK_IP = inet_addr("10.0.1.30"); static const u32 FALLBACK_NETMASK = inet_addr("255.255.255.0"); - static const u32 FALLBACK_GATEWAY = inet_addr("10.0.255.255"); - static const DefaultInterface FALLBACK_VALUES{FALLBACK_IP, FALLBACK_NETMASK, FALLBACK_GATEWAY}; + static const u32 FALLBACK_BROADCAST = inet_addr("10.0.255.255"); + static const u32 FALLBACK_GATEWAY = inet_addr("10.0.255.1"); + static const InterfaceRouting FALLBACK_ROUTING = {0, 0, 0, FALLBACK_GATEWAY}; + static const DefaultInterface FALLBACK_VALUES = { + FALLBACK_IP, FALLBACK_NETMASK, FALLBACK_BROADCAST, {FALLBACK_ROUTING}}; return GetSystemDefaultInterface().value_or(FALLBACK_VALUES); } @@ -830,6 +967,7 @@ IPCReply NetIPTopDevice::HandleGetInterfaceOptRequest(const IOCtlVRequest& reque auto& system = GetSystem(); auto& memory = system.GetMemory(); + const DefaultInterface interface = GetSystemDefaultInterfaceOrFallback(); const u32 param = memory.Read_U32(request.in_vectors[0].address); const u32 param2 = memory.Read_U32(request.in_vectors[0].address + 4); const u32 param3 = memory.Read_U32(request.io_vectors[0].address); @@ -980,20 +1118,41 @@ IPCReply NetIPTopDevice::HandleGetInterfaceOptRequest(const IOCtlVRequest& reque case 0x4003: // ip addr table { - // XXX: this isn't exactly right; the buffer can be larger than 12 bytes, in which case - // SO can write 12 more bytes. + // XXX: this isn't exactly right; the buffer can be larger than 12 bytes, + // in which case, depending on some interface settings, SO can write 12 more bytes memory.Write_U32(0xC, request.io_vectors[1].address); - const DefaultInterface interface = GetSystemDefaultInterfaceOrFallback(); memory.Write_U32(Common::swap32(interface.inet), request.io_vectors[0].address); memory.Write_U32(Common::swap32(interface.netmask), request.io_vectors[0].address + 4); memory.Write_U32(Common::swap32(interface.broadcast), request.io_vectors[0].address + 8); break; } - case 0x4005: // hardcoded value + case 0x4005: + // get routing table size, which is almost always hardcoded to be 0x20 in IOS + // on pc its often around 0x20 too so... meh memory.Write_U32(0x20, request.io_vectors[0].address); break; + case 0x4006: // get routing table + for (InterfaceRouting route : interface.routing_table) + { + memory.Write_U32(Common::swap32(route.destination), request.io_vectors[0].address + param5); + memory.Write_U32(Common::swap32(route.netmask), request.io_vectors[0].address + param5 + 4); + memory.Write_U32(Common::swap32(route.gateway), request.io_vectors[0].address + param5 + 8); + + // write flags. unknown what they do. + memory.Write_U32(0x01, request.io_vectors[0].address + param5 + 12); + + // some unknown + memory.Write_U64(0x00, request.io_vectors[0].address + param5 + 16); + param5 += 24; + if (param5 >= param4) + break; + } + + memory.Write_U32(param5, request.io_vectors[1].address); + break; + case 0x6003: // hardcoded value memory.Write_U32(0x80, request.io_vectors[0].address); break;