mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
getifaddrs listed only the host's IPv4 and IPv6 addresses, so the per-interface AF_LINK entries FreeBSD's getifaddrs puts first were missing, and with them the interface index, hardware address, MTU and counters and every interface without an address. On Linux each interface now starts with an AF_LINK entry, followed by its addresses, as FreeBSD 11.4's getifaddrs orders them. Its address is a sockaddr_dl with the host index, type, name and hardware address (none for a loopback), sized as FreeBSD's if_attach sizes it. Its ifa_data is a 152-byte FreeBSD if_data filled as if_data_copy fills it: type, address and header length, link state, MTU, speed and the host's packet, byte, error, drop and multicast counters from sysfs, or the host list's 32-bit counters when sysfs cannot be read. Fields with no host equivalent are 0 and listed in the debt entry. Windows is unchanged.
309 lines
14 KiB
C++
309 lines
14 KiB
C++
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include <ifaddrs.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/in.h>
|
|
#include <netpacket/packet.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <fstream>
|
|
#include <string>
|
|
#endif
|
|
#include "prx/libc/include/ApplicationHeap.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
struct GuestInterfaceAddress {
|
|
GuestInterfaceAddress* next;
|
|
char* name;
|
|
std::uint32_t flags;
|
|
void* address;
|
|
void* netmask;
|
|
void* destination;
|
|
void* data;
|
|
};
|
|
static_assert(sizeof(GuestInterfaceAddress) == 56);
|
|
static_assert(offsetof(GuestInterfaceAddress, flags) == 16);
|
|
static_assert(offsetof(GuestInterfaceAddress, address) == 24);
|
|
static_assert(offsetof(GuestInterfaceAddress, netmask) == 32);
|
|
static_assert(offsetof(GuestInterfaceAddress, destination) == 40);
|
|
static_assert(offsetof(GuestInterfaceAddress, data) == 48);
|
|
|
|
extern "C" {
|
|
int APS5_VABI getifaddrs_nid_postfix(GuestInterfaceAddress** list);
|
|
void APS5_VABI freeifaddrs_nid_postfix(GuestInterfaceAddress* list);
|
|
void APS5_VABI free_nid_postfix(void* pointer);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
}
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
struct HostInterfaceAddress {
|
|
int family;
|
|
std::uint8_t address[16];
|
|
std::uint8_t netmask[16];
|
|
std::uint32_t scope;
|
|
char name[16];
|
|
unsigned int index;
|
|
std::uint8_t type;
|
|
std::uint8_t hardwareLength;
|
|
std::uint8_t hardware[8];
|
|
bool loopbackUp;
|
|
};
|
|
|
|
static HostInterfaceAddress HostAddress(const sockaddr* address) {
|
|
HostInterfaceAddress entry{};
|
|
entry.family = address->sa_family;
|
|
if (entry.family == AF_INET) {
|
|
std::memcpy(entry.address, &reinterpret_cast<const sockaddr_in*>(address)->sin_addr, 4);
|
|
} else {
|
|
const auto* v6 = reinterpret_cast<const sockaddr_in6*>(address);
|
|
std::memcpy(entry.address, &v6->sin6_addr, 16);
|
|
entry.scope = v6->sin6_scope_id;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
static std::uint64_t SysfsCounter(const char* name, const char* file) {
|
|
std::ifstream input(std::string("/sys/class/net/") + name + "/statistics/" + file);
|
|
unsigned long long value = 0;
|
|
Require(static_cast<bool>(input >> value));
|
|
return value;
|
|
}
|
|
|
|
static std::uint32_t HostMtu(const char* name) {
|
|
const int probe = ::socket(AF_INET, SOCK_DGRAM, 0);
|
|
Require(probe >= 0);
|
|
ifreq request{};
|
|
std::strncpy(request.ifr_name, name, sizeof(request.ifr_name) - 1);
|
|
Require(::ioctl(probe, SIOCGIFMTU, &request) == 0);
|
|
::close(probe);
|
|
return static_cast<std::uint32_t>(request.ifr_mtu);
|
|
}
|
|
|
|
static void SendLoopbackDatagram() {
|
|
const int receiver = ::socket(AF_INET, SOCK_DGRAM, 0);
|
|
const int sender = ::socket(AF_INET, SOCK_DGRAM, 0);
|
|
Require(receiver >= 0 && sender >= 0);
|
|
sockaddr_in address{};
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
Require(::bind(receiver, reinterpret_cast<const sockaddr*>(&address), sizeof(address)) == 0);
|
|
socklen_t length = sizeof(address);
|
|
Require(::getsockname(receiver, reinterpret_cast<sockaddr*>(&address), &length) == 0);
|
|
const char payload = 'x';
|
|
Require(::sendto(sender, &payload, 1, 0, reinterpret_cast<const sockaddr*>(&address), sizeof(address)) == 1);
|
|
char received = 0;
|
|
Require(::recv(receiver, &received, 1, 0) == 1);
|
|
::close(sender);
|
|
::close(receiver);
|
|
}
|
|
#endif
|
|
|
|
static std::vector<HostInterfaceAddress> HostSnapshot() {
|
|
std::vector<HostInterfaceAddress> entries;
|
|
#ifdef _WIN32
|
|
const ULONG options = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
|
ULONG size = 0;
|
|
std::vector<std::uint8_t> storage;
|
|
ULONG result = GetAdaptersAddresses(AF_UNSPEC, options, nullptr, nullptr, &size);
|
|
for (unsigned calls = 1; result == ERROR_BUFFER_OVERFLOW && calls < 4; ++calls) {
|
|
storage.resize(size);
|
|
result = GetAdaptersAddresses(AF_UNSPEC, options, nullptr, reinterpret_cast<IP_ADAPTER_ADDRESSES*>(storage.data()), &size);
|
|
}
|
|
if (result == ERROR_NO_DATA) return entries;
|
|
Require(result == NO_ERROR);
|
|
for (auto* adapter = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(storage.data()); adapter; adapter = adapter->Next)
|
|
for (auto* unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next) {
|
|
const sockaddr* address = unicast->Address.lpSockaddr;
|
|
if (!address || (address->sa_family != AF_INET && address->sa_family != AF_INET6)) continue;
|
|
HostInterfaceAddress entry = HostAddress(address);
|
|
const unsigned bits = entry.family == AF_INET ? 32 : 128;
|
|
unsigned prefix = unicast->OnLinkPrefixLength;
|
|
if (prefix > bits) prefix = bits;
|
|
for (unsigned bit = 0; bit < prefix; ++bit) entry.netmask[bit / 8] |= static_cast<std::uint8_t>(0x80 >> (bit % 8));
|
|
entries.push_back(entry);
|
|
}
|
|
#else
|
|
ifaddrs* host = nullptr;
|
|
Require(::getifaddrs(&host) == 0);
|
|
std::vector<std::string> names;
|
|
std::vector<std::vector<HostInterfaceAddress>> groups;
|
|
const auto group = [&](const char* name) -> std::vector<HostInterfaceAddress>& {
|
|
for (std::size_t index = 0; index < names.size(); ++index)
|
|
if (names[index] == name) return groups[index];
|
|
names.emplace_back(name);
|
|
return groups.emplace_back();
|
|
};
|
|
try {
|
|
for (auto* item = host; item; item = item->ifa_next) {
|
|
if (!item->ifa_addr) continue;
|
|
if (item->ifa_addr->sa_family == AF_PACKET) {
|
|
const auto& link = *reinterpret_cast<const sockaddr_ll*>(item->ifa_addr);
|
|
HostInterfaceAddress entry{};
|
|
entry.family = AF_PACKET;
|
|
std::strncpy(entry.name, item->ifa_name, sizeof(entry.name) - 1);
|
|
entry.index = static_cast<unsigned int>(link.sll_ifindex);
|
|
entry.type = link.sll_hatype == ARPHRD_ETHER ? 0x6 : link.sll_hatype == ARPHRD_LOOPBACK ? 0x18 : link.sll_hatype == ARPHRD_PPP ? 0x17 : 0x1;
|
|
entry.hardwareLength = entry.type == 0x18 ? 0 : link.sll_halen;
|
|
std::memcpy(entry.hardware, link.sll_addr, entry.hardwareLength);
|
|
entry.loopbackUp = (item->ifa_flags & IFF_UP) != 0;
|
|
auto& members = group(item->ifa_name);
|
|
members.insert(members.begin(), entry);
|
|
continue;
|
|
}
|
|
if (item->ifa_addr->sa_family != AF_INET && item->ifa_addr->sa_family != AF_INET6) continue;
|
|
Require(item->ifa_netmask != nullptr);
|
|
HostInterfaceAddress entry = HostAddress(item->ifa_addr);
|
|
std::strncpy(entry.name, item->ifa_name, sizeof(entry.name) - 1);
|
|
if (entry.family == AF_INET)
|
|
std::memcpy(entry.netmask, &reinterpret_cast<const sockaddr_in*>(item->ifa_netmask)->sin_addr, 4);
|
|
else
|
|
std::memcpy(entry.netmask, &reinterpret_cast<const sockaddr_in6*>(item->ifa_netmask)->sin6_addr, 16);
|
|
group(item->ifa_name).push_back(entry);
|
|
}
|
|
} catch (...) {
|
|
::freeifaddrs(host);
|
|
throw;
|
|
}
|
|
::freeifaddrs(host);
|
|
for (const auto& members : groups) entries.insert(entries.end(), members.begin(), members.end());
|
|
#endif
|
|
return entries;
|
|
}
|
|
|
|
int main() {
|
|
std::array<void*, 10> api{};
|
|
ApplicationHeapRegister_nid_no_patch(api.data());
|
|
constexpr std::uint32_t knownFlags = 0x1 | 0x2 | 0x8 | 0x10 | 0x40 | 0x80 | 0x100 | 0x200 | 0x8000;
|
|
const std::uint8_t loopbackAddress[] = {127, 0, 0, 1};
|
|
const std::uint8_t loopbackMask[] = {255, 0, 0, 0};
|
|
bool matched = false;
|
|
#ifndef _WIN32
|
|
bool linkChecked = false;
|
|
SendLoopbackDatagram();
|
|
#endif
|
|
for (unsigned attempt = 0; attempt < 3 && !matched; ++attempt) {
|
|
const std::vector<HostInterfaceAddress> host = HostSnapshot();
|
|
GuestInterfaceAddress* list = nullptr;
|
|
Require(getifaddrs_nid_postfix(&list) == 0);
|
|
std::size_t entries = 0;
|
|
bool equal = true;
|
|
bool loopback = false;
|
|
for (auto* item = list; item; item = item->next) {
|
|
Require(++entries < 100000);
|
|
Require(item->name != nullptr);
|
|
const std::size_t nameLength = std::strlen(item->name);
|
|
Require(nameLength >= 1 && nameLength <= 15);
|
|
Require((item->flags & ~knownFlags) == 0);
|
|
const auto* address = static_cast<const std::uint8_t*>(item->address);
|
|
Require(address != nullptr && reinterpret_cast<std::uintptr_t>(address) % 8 == 0);
|
|
#ifndef _WIN32
|
|
if (address[1] == 18) {
|
|
Require(item->netmask == nullptr && item->destination == nullptr);
|
|
const auto* data = static_cast<const std::uint8_t*>(item->data);
|
|
Require(data != nullptr && reinterpret_cast<std::uintptr_t>(data) % 8 == 0);
|
|
std::uint16_t index = 0;
|
|
std::uint16_t dataLength = 0;
|
|
std::uint32_t mtu = 0;
|
|
std::uint64_t counters[11] = {};
|
|
std::memcpy(&index, address + 2, 2);
|
|
std::memcpy(&dataLength, data + 6, 2);
|
|
std::memcpy(&mtu, data + 8, 4);
|
|
std::memcpy(counters, data + 24, sizeof(counters));
|
|
Require(address[0] >= 54 && address[0] % 8 == 0 && address[0] >= 24 + address[6]);
|
|
Require(address[5] == nameLength && std::memcmp(address + 8, item->name, nameLength) == 0 && address[7] == 0);
|
|
Require(dataLength == 152 && data[0] == address[4] && data[2] == address[6] && data[1] == 0 && data[5] == 0);
|
|
Require(data[4] == 1 || data[4] == 2);
|
|
Require(index == if_nametoindex(item->name));
|
|
Require(mtu == HostMtu(item->name));
|
|
Require(counters[0] <= SysfsCounter(item->name, "rx_packets") && counters[2] <= SysfsCounter(item->name, "tx_packets"));
|
|
Require(counters[5] <= SysfsCounter(item->name, "rx_bytes") && counters[6] <= SysfsCounter(item->name, "tx_bytes"));
|
|
if (entries <= host.size()) {
|
|
const HostInterfaceAddress& expected = host[entries - 1];
|
|
equal = equal && expected.family == AF_PACKET && std::strcmp(expected.name, item->name) == 0 && expected.index == index &&
|
|
expected.type == address[4] && expected.hardwareLength == address[6] &&
|
|
std::memcmp(expected.hardware, address + 8 + nameLength, expected.hardwareLength) == 0;
|
|
if (expected.type == 0x18) {
|
|
Require(data[3] == 0 && data[4] == (expected.loopbackUp ? 2 : 1));
|
|
if (expected.loopbackUp) Require(counters[0] > 0 && counters[2] > 0 && counters[5] > 0 && counters[6] > 0);
|
|
linkChecked = linkChecked || expected.loopbackUp;
|
|
}
|
|
if (expected.type == 0x6) Require(data[3] == 14 && address[6] == 6);
|
|
} else {
|
|
equal = false;
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
Require(item->data == nullptr);
|
|
const auto* netmask = static_cast<const std::uint8_t*>(item->netmask);
|
|
Require(address != nullptr && netmask != nullptr);
|
|
Require(reinterpret_cast<std::uintptr_t>(address) % 8 == 0);
|
|
Require(reinterpret_cast<std::uintptr_t>(netmask) % 8 == 0);
|
|
Require((address[0] == 16 && address[1] == 2) || (address[0] == 28 && address[1] == 28));
|
|
Require(netmask[0] == address[0] && netmask[1] == address[1]);
|
|
Require(address[2] == 0 && address[3] == 0 && netmask[2] == 0 && netmask[3] == 0);
|
|
if (address[1] == 2) {
|
|
for (std::size_t index = 8; index < 16; ++index) Require(address[index] == 0 && netmask[index] == 0);
|
|
} else {
|
|
for (std::size_t index = 4; index < 8; ++index) Require(address[index] == 0 && netmask[index] == 0);
|
|
}
|
|
if (item->destination) {
|
|
const auto* destination = static_cast<const std::uint8_t*>(item->destination);
|
|
Require(reinterpret_cast<std::uintptr_t>(destination) % 8 == 0);
|
|
Require(destination[0] == address[0] && destination[1] == address[1]);
|
|
}
|
|
if (address[1] == 2 && (item->flags & 0x2)) Require(item->destination != nullptr);
|
|
if (item->flags & 0x10) Require(item->destination != nullptr);
|
|
if (entries <= host.size()) {
|
|
const HostInterfaceAddress& expected = host[entries - 1];
|
|
#ifndef _WIN32
|
|
equal = equal && std::strcmp(expected.name, item->name) == 0;
|
|
#endif
|
|
const bool ipv4 = address[1] == 2;
|
|
const std::size_t offset = ipv4 ? 4 : 8;
|
|
const std::size_t length = ipv4 ? 4 : 16;
|
|
equal = equal && expected.family == (ipv4 ? AF_INET : AF_INET6) &&
|
|
std::memcmp(address + offset, expected.address, length) == 0 &&
|
|
std::memcmp(netmask + offset, expected.netmask, length) == 0;
|
|
if (!ipv4) {
|
|
std::uint32_t scope;
|
|
std::memcpy(&scope, address + 24, 4);
|
|
equal = equal && scope == expected.scope;
|
|
}
|
|
} else {
|
|
equal = false;
|
|
}
|
|
if (address[1] == 2 && std::memcmp(address + 4, loopbackAddress, 4) == 0)
|
|
loopback = loopback || (std::memcmp(netmask + 4, loopbackMask, 4) == 0 && (item->flags & 0x9) == 0x9 && !(item->flags & 0x2));
|
|
}
|
|
matched = equal && entries == host.size() && loopback;
|
|
freeifaddrs_nid_postfix(list);
|
|
}
|
|
Require(matched);
|
|
#ifndef _WIN32
|
|
Require(linkChecked);
|
|
#endif
|
|
GuestInterfaceAddress* released = nullptr;
|
|
Require(getifaddrs_nid_postfix(&released) == 0 && released != nullptr);
|
|
free_nid_postfix(released);
|
|
freeifaddrs_nid_postfix(nullptr);
|
|
*__error_nid_postfix() = 0;
|
|
Require(getifaddrs_nid_postfix(nullptr) == -1);
|
|
Require(*__error_nid_postfix() == 14);
|
|
}
|