Merge pull request #4682 from bubblegg/feat/netctl-get-if-stat
feat(libSceNetCtl): add sceNetCtlGetIfStat
This commit is contained in:
commit
30caf2b2a5
|
|
@ -1265,6 +1265,13 @@ struct NetCtlNatInfo { std::uint8_t opaque[128]; };
|
|||
|
||||
union NetCtlInfo { std::uint8_t opaque[256]; };
|
||||
|
||||
struct NetCtlIfStat {
|
||||
std::uint32_t device;
|
||||
std::uint64_t tx_bytes;
|
||||
std::uint64_t rx_bytes;
|
||||
std::uint32_t reserved[8];
|
||||
};
|
||||
|
||||
using NetCtlCallback = void (*)(int, void*);
|
||||
|
||||
struct HttpEpoll;
|
||||
|
|
|
|||
|
|
@ -7,12 +7,18 @@
|
|||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#ifdef __linux__
|
||||
#include <netpacket/packet.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#ifdef _WIN32
|
||||
#include <vector>
|
||||
#endif
|
||||
|
|
@ -78,12 +84,56 @@ static bool HostHasAddress(int family) {
|
|||
#endif
|
||||
}
|
||||
|
||||
extern "C" std::uint64_t NetCtlReadByteCounter_nid_no_patch(const char* path) {
|
||||
std::ifstream file(path);
|
||||
std::uint64_t value = 0;
|
||||
if (!(file >> value)) throw std::runtime_error(std::string("sceNetCtlGetIfStat: no byte counter in ") + path);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void HostTraffic(std::uint64_t& sent, std::uint64_t& received) {
|
||||
sent = 0;
|
||||
received = 0;
|
||||
#ifdef _WIN32
|
||||
MIB_IF_TABLE2* table = nullptr;
|
||||
if (GetIfTable2(&table) != NO_ERROR) throw std::runtime_error("sceNetCtlGetIfStat: the host interface table is unavailable");
|
||||
for (ULONG index = 0; index < table->NumEntries; ++index) {
|
||||
const auto& row = table->Table[index];
|
||||
if (row.OperStatus != IfOperStatusUp || row.Type == IF_TYPE_SOFTWARE_LOOPBACK || !row.InterfaceAndOperStatusFlags.HardwareInterface) continue;
|
||||
sent += row.OutOctets;
|
||||
received += row.InOctets;
|
||||
}
|
||||
FreeMibTable(table);
|
||||
#elif defined(__linux__)
|
||||
ifaddrs* interfaces = nullptr;
|
||||
if (getifaddrs(&interfaces) != 0) throw std::runtime_error("sceNetCtlGetIfStat: the host interface list is unavailable");
|
||||
for (auto* item = interfaces; item; item = item->ifa_next) {
|
||||
if (!item->ifa_addr || item->ifa_addr->sa_family != AF_PACKET) continue;
|
||||
if (!(item->ifa_flags & IFF_UP) || (item->ifa_flags & IFF_LOOPBACK)) continue;
|
||||
const std::string statistics = std::string("/sys/class/net/") + item->ifa_name + "/statistics/";
|
||||
sent += NetCtlReadByteCounter_nid_no_patch((statistics + "tx_bytes").c_str());
|
||||
received += NetCtlReadByteCounter_nid_no_patch((statistics + "rx_bytes").c_str());
|
||||
}
|
||||
freeifaddrs(interfaces);
|
||||
#else
|
||||
throw std::runtime_error("sceNetCtlGetIfStat: host interface byte counters are not implemented on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNetCtlCheckCallback(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat* stat) {
|
||||
if (!stat) return SCE_NET_CTL_ERROR_INVALID_ADDR;
|
||||
if (!HostHasAddress(AF_UNSPEC)) return SCE_NET_CTL_ERROR_NOT_CONNECTED;
|
||||
*stat = {};
|
||||
HostTraffic(stat->tx_bytes, stat->rx_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNetCtlGetInfo(int code, NetCtlInfo* info) {
|
||||
(void)code;
|
||||
(void)info;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ int APS5_VABI sceNetResolverAbort(int, int);
|
|||
int APS5_VABI sceNetResolverDestroy(int);
|
||||
int APS5_VABI sceNetResolverGetError(int, int*);
|
||||
int APS5_VABI sceNetCtlGetState(int*);
|
||||
int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat*);
|
||||
std::uint64_t NetCtlReadByteCounter_nid_no_patch(const char*);
|
||||
int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*);
|
||||
int APS5_VABI open_nid_postfix(const char*, int, int);
|
||||
int APS5_VABI socket_nid_postfix(int, int, int);
|
||||
|
|
@ -777,6 +779,34 @@ int main() {
|
|||
int state = -1;
|
||||
Require(sceNetCtlGetState(&state) == 0);
|
||||
Require(state == 0 || state == 3);
|
||||
Require(sceNetCtlGetIfStat(nullptr) == static_cast<int>(0x80412107));
|
||||
{
|
||||
const char* counterPath = "guest_sce_net_byte_counter.txt";
|
||||
std::FILE* counter = std::fopen(counterPath, "w");
|
||||
Require(counter != nullptr);
|
||||
Require(std::fputs("1099511627776", counter) >= 0);
|
||||
Require(std::fclose(counter) == 0);
|
||||
Require(NetCtlReadByteCounter_nid_no_patch(counterPath) == 1099511627776ull);
|
||||
Require(std::remove(counterPath) == 0);
|
||||
bool missing = false;
|
||||
try {
|
||||
NetCtlReadByteCounter_nid_no_patch(counterPath);
|
||||
} catch (const std::runtime_error&) {
|
||||
missing = true;
|
||||
}
|
||||
Require(missing);
|
||||
}
|
||||
NetCtlIfStat first{};
|
||||
first.device = 7;
|
||||
first.reserved[7] = 7;
|
||||
const int statResult = sceNetCtlGetIfStat(&first);
|
||||
Require(statResult == (state == 3 ? 0 : static_cast<int>(0x80412108)));
|
||||
if (statResult == 0) {
|
||||
Require(first.device == 0 && first.reserved[7] == 0);
|
||||
NetCtlIfStat second{};
|
||||
Require(sceNetCtlGetIfStat(&second) == 0);
|
||||
Require(second.tx_bytes >= first.tx_bytes && second.rx_bytes >= first.rx_bytes);
|
||||
}
|
||||
if (ipv6Unavailable) {
|
||||
std::puts("skipped the IPv6 socket checks, the host has no usable IPv6 loopback");
|
||||
return 77;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
### Unknown function info
|
||||
|
||||
- AudioIn and NpSessionSignaling exports added without an implementation have assumed signatures
|
||||
- `sceNetCtlGetIfStat` signature and 56-byte layout come from PPSA09030's call site and SharpProspero's `SceNetCtlIfStat`; the device kind is always 0, the byte counters are the host's totals over its up non-loopback hardware interfaces (not "since cleared"), and the not-connected result is assumed to match `sceNetCtlGetState`
|
||||
- Font, Http2, Net, Ssl, SystemService and libkernel exports imported by PPSA12544 and added without an implementation have unknown signatures
|
||||
- The Share, NpTrophy2, NpEntitlementAccess and WebBrowserDialog exports imported by PPSA12544's Unity plugins and added without an implementation have unknown signatures
|
||||
- Acm, GameLiveStreaming, Net, NpEntitlementAccess, NpManager, NpWebApi2, VoiceQoS, WebBrowserDialog and libkernel exports imported by PPSA14632 and added without an implementation are declared without parameters
|
||||
|
|
|
|||
Loading…
Reference in New Issue