Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS/Net: implement ioctlv that retrieves the network routing table that libogc now uses on network init #13066

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 187 additions & 28 deletions Source/Core/Core/IOS/Network/IP/Top.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
#include "jni/AndroidCommon/AndroidCommon.h"
#endif

#ifdef __linux__
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#endif

namespace IOS::HLE
{
enum SOResultCode : s32
Expand Down Expand Up @@ -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<InterfaceRouting> routing_table; // IPv4 routing table
};

static std::optional<DefaultInterface> GetSystemDefaultInterface()
static std::vector<InterfaceRouting> GetSystemInterfaceRouting()
{
std::vector<InterfaceRouting> routing_table;

#ifdef _WIN32
std::unique_ptr<MIB_IPFORWARDTABLE> forward_table;
DWORD forward_table_size = 0;
std::unique_ptr<MIB_IPFORWARDTABLE> forward_table;
if (GetIpForwardTable(nullptr, &forward_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
{
forward_table =
std::unique_ptr<MIB_IPFORWARDTABLE>((PMIB_IPFORWARDTABLE) operator new(forward_table_size));
}

std::unique_ptr<MIB_IPADDRTABLE> ip_table;
DWORD ip_table_size = 0;
if (GetIpAddrTable(nullptr, &ip_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
{
ip_table = std::unique_ptr<MIB_IPADDRTABLE>((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<DefaultInterface> GetSystemDefaultInterface()
{
auto routing_table = GetSystemInterfaceRouting();
#ifdef _WIN32
std::unique_ptr<MIB_IPADDRTABLE> ip_table;
DWORD ip_table_size = 0;
if (GetIpAddrTable(nullptr, &ip_table_size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
{
ip_table = std::unique_ptr<MIB_IPADDRTABLE>((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 &&
Expand All @@ -216,16 +341,20 @@ static std::optional<DefaultInterface> 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__)
const u32 addr = GetNetworkIpAddress();
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.
Expand Down Expand Up @@ -265,8 +394,13 @@ static std::optional<DefaultInterface> 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
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down