feat(libc): keep getifaddrs flags consistent with their addresses on Linux

Drop IFF_POINTOPOINT from an address without a peer and compute an IPv4 broadcast address the host omits from the netmask, since FreeBSD always provides the address when the flag is set. Check zero port, flowinfo and padding in the test.
This commit is contained in:
LuisBiceps 2026-10-09 03:37:55 +08:00 committed by Adrià Franch
parent a09b67b142
commit 5c0610f334
3 changed files with 22 additions and 10 deletions

View File

@ -89,6 +89,13 @@ std::size_t Aligned(std::size_t size) {
return (size + 7) & ~static_cast<std::size_t>(7);
}
std::vector<std::uint8_t> BroadcastAddress(const std::vector<std::uint8_t>& address, const std::vector<std::uint8_t>& netmask) {
std::vector<std::uint8_t> broadcast = address;
for (std::size_t index = 4; index < 8; ++index)
broadcast[index] = static_cast<std::uint8_t>(address[index] | static_cast<std::uint8_t>(~netmask[index]));
return broadcast;
}
#ifdef _WIN32
std::vector<std::uint8_t> PrefixMask(int family, unsigned prefix) {
const unsigned bits = family == AF_INET ? 32 : 128;
@ -98,13 +105,6 @@ std::vector<std::uint8_t> PrefixMask(int family, unsigned prefix) {
return family == AF_INET ? GuestIpv4(mask) : GuestIpv6(mask, 0);
}
std::vector<std::uint8_t> BroadcastAddress(const std::vector<std::uint8_t>& address, const std::vector<std::uint8_t>& netmask) {
std::vector<std::uint8_t> broadcast = address;
for (std::size_t index = 4; index < 8; ++index)
broadcast[index] = static_cast<std::uint8_t>(address[index] | static_cast<std::uint8_t>(~netmask[index]));
return broadcast;
}
std::uint32_t InterfaceFlags(const IP_ADAPTER_ADDRESSES* adapter) {
std::uint32_t flags = 0;
if (adapter->OperStatus == IfOperStatusUp) flags |= guestUp | guestRunning;
@ -179,8 +179,14 @@ int HostEntries(std::vector<Entry>& entries) {
if (!item->ifa_addr || (item->ifa_addr->sa_family != AF_INET && item->ifa_addr->sa_family != AF_INET6)) continue;
if (!item->ifa_netmask) throw std::runtime_error(std::string("getifaddrs: host address without a netmask on ") + item->ifa_name);
Entry entry{CheckedName(item->ifa_name), InterfaceFlags(item->ifa_flags), GuestAddress(item->ifa_addr), GuestAddress(item->ifa_netmask), {}};
const sockaddr* destination = item->ifa_flags & IFF_POINTOPOINT ? item->ifa_dstaddr : item->ifa_flags & IFF_BROADCAST ? item->ifa_broadaddr : nullptr;
if (destination && destination->sa_family == item->ifa_addr->sa_family) entry.destination = GuestAddress(destination);
const int family = item->ifa_addr->sa_family;
if (item->ifa_flags & IFF_POINTOPOINT) {
if (item->ifa_dstaddr && item->ifa_dstaddr->sa_family == family) entry.destination = GuestAddress(item->ifa_dstaddr);
else entry.flags &= ~guestPointToPoint;
} else if (family == AF_INET && (item->ifa_flags & IFF_BROADCAST)) {
entry.destination = item->ifa_broadaddr && item->ifa_broadaddr->sa_family == AF_INET
? GuestAddress(item->ifa_broadaddr) : BroadcastAddress(entry.address, entry.netmask);
}
entries.push_back(std::move(entry));
}
} catch (...) {

View File

@ -131,6 +131,12 @@ int main() {
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);

View File

@ -438,4 +438,4 @@ Throughout the project, every function at every stage either **does exactly what
- [sigaction](../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's `sigaction` (`EINVAL` for other signals, and for a handler on `SIGKILL` or `SIGSTOP`); `signal` records its handler with `SA_RESTART`, as FreeBSD's libc does. Only handlers without `SA_SIGINFO` on the signals `signal` already delivers (`SIGINT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM`) run, the same way `signal` handlers do; their mask and flags are not applied. `SA_SIGINFO` handlers and other signals (`SIGBUS`, `SIGHUP`, ...) are recorded but never called, since that needs FreeBSD's `siginfo_t` and `ucontext_t`; `raise` of an `SA_SIGINFO` handler on a signal `signal` delivers throws instead of terminating, and `raise` of the other signals still returns `EINVAL`
- [AJM](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp), [APR](../../core/libs/prx/libkernel/Apr/src/Apr.cpp) and [NGS2](../../core/libs/prx/libSceNgs2.native/src/System.cpp) state is allocated with `new` and never freed on purpose: title threads still call into these libraries while `std::exit` runs static destructors, so ordinary statics would be destroyed under them. Keep them leaked; `guest_ajm_teardown` covers the AJM case on Unix.
- [Null vertex buffer V#](../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a V# whose four dwords are all zero is bound to a zeroed vertex buffer of the attribute's size with stride 0, so every component of its attribute reads 0. This follows the V# fields in the [AMD RDNA2 ISA](https://gpuopen.com/download/rdna2-shader-instruction-set-architecture.pdf) (`NUM_RECORDS` 0, `DST_SEL` 0 selects 0), not measured on hardware.
- [getifaddrs](../../core/libs/prx/libc/src/Interfaces.cpp) (libc) lists the host's IPv4 and IPv6 addresses in FreeBSD's `ifaddrs` and sockaddr layouts. FreeBSD's per-interface AF_LINK entries (MAC, index, MTU) and their `if_data` are not produced, so interfaces without an address are omitted. Names are the host's (Linux `lo`, `eth0`; Windows NDIS names such as `ethernet_32769`, or `if<index>` when one does not fit 15 bytes), not the console's. On Windows the flags are derived from the adapter: PPP and tunnel adapters get neither `IFF_BROADCAST` nor `IFF_POINTOPOINT` because their peer address is not reported, and an address whose prefix length the host reports as unspecified gets a host mask.
- [getifaddrs](../../core/libs/prx/libc/src/Interfaces.cpp) (libc) lists the host's IPv4 and IPv6 addresses in FreeBSD's `ifaddrs` and sockaddr layouts. FreeBSD's per-interface AF_LINK entries (MAC, index, MTU) and their `if_data` are not produced, so interfaces without an address are omitted. Names are the host's (Linux `lo`, `eth0`; Windows NDIS names such as `ethernet_32769`, or `if<index>` when one does not fit 15 bytes), not the console's. A Linux point-to-point address without a peer loses `IFF_POINTOPOINT`, and an IPv4 broadcast address the host omits is computed from the netmask. On Windows the flags are only `IFF_UP`, `IFF_RUNNING`, `IFF_LOOPBACK`, `IFF_BROADCAST` and `IFF_MULTICAST`, derived from the adapter: PPP and tunnel adapters get neither `IFF_BROADCAST` nor `IFF_POINTOPOINT` because their peer address is not reported, and an address whose prefix length the host reports as unspecified gets a host mask.