Skip to content

Commit

Permalink
IOS/Net: implement ioctlv that retrieves the network routing table th…
Browse files Browse the repository at this point in the history
…at libogc now uses on network init
  • Loading branch information
DacoTaco committed Sep 21, 2024
1 parent af92168 commit ff10312
Showing 1 changed file with 187 additions and 28 deletions.
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

0 comments on commit ff10312

Please sign in to comment.