Files
Clansty 244bbb8104 feat: report the host's interfaces in the MIB_IFROW calls
- GetIfEntry, GetIfTable and GetNumberOfInterfaces answer from the host's interfaces. GetIfEntry names one by the index the record handed in carries, GetIfTable puts the rows behind a count, and a table that does not fit is refused with ERROR_INSUFFICIENT_BUFFER and the size it needs.

- The interface walk moved out of host_api.cpp into interfaces.cpp, so the records GetAdaptersAddresses reports and the rows these calls report come from one enumeration of the host rather than two.

- interfaces_test checks the record against the MIB_IFROW offsets Windows declares and the cutting of a long name. The new guest fixture interface_client.c reads the tables through the runtime, in 64-bit and 32-bit builds: it checks that the count, the rows and GetIfEntry agree, that a sorted table lists the rows by index, and that an unknown index and a short table fail as Windows fails them.

Assisted-by: Claude Code:deepseek-v4.1-flash-1017

AI-Scope: Read the MIB_IFROW and MIB_IFTABLE layouts, moved the interface enumeration into interfaces.cpp, wrote the three handlers and both tests, and checked them with ctest (native_interface_table_contract, native_interface_table_client, native_interface_table_client_x32).
2026-11-02 12:03:56 +08:00

77 lines
3.0 KiB
C++

#include "interfaces.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <ifaddrs.h>
#include <map>
#include <net/if.h>
#include <set>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
namespace raven {
std::vector<HostInterface> host_interfaces()
{
std::vector<HostInterface> interfaces;
ifaddrs *list = nullptr;
if (::getifaddrs(&list))
return interfaces;
std::set<std::string> seen;
for (const auto *entry = list; entry; entry = entry->ifa_next) {
if (!entry->ifa_name || !seen.insert(entry->ifa_name).second)
continue;
HostInterface interface;
interface.name = entry->ifa_name;
interface.index = ::if_nametoindex(entry->ifa_name);
interface.status = (entry->ifa_flags & IFF_UP) && (entry->ifa_flags & IFF_RUNNING) ? 1 : 2;
interface.type = (entry->ifa_flags & IFF_LOOPBACK) ? if_type_loopback :
::access(("/sys/class/net/" + interface.name + "/wireless").c_str(), F_OK) == 0 ? if_type_wireless : if_type_ethernet;
if (const int probe = ::socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); probe >= 0) {
ifreq request{};
std::strncpy(request.ifr_name, entry->ifa_name, IFNAMSIZ - 1);
if (::ioctl(probe, SIOCGIFMTU, &request) == 0)
interface.mtu = uint32_t(request.ifr_mtu);
if (interface.type != if_type_loopback && ::ioctl(probe, SIOCGIFHWADDR, &request) == 0) {
std::memcpy(interface.hardware, request.ifr_hwaddr.sa_data, 6);
interface.hardware_length = 6;
}
::close(probe);
}
interfaces.push_back(std::move(interface));
}
::freeifaddrs(list);
return interfaces;
}
std::vector<uint8_t> mib_if_row(const HostInterface &interface)
{
std::vector<uint8_t> row(mib_if_row_size, 0);
const auto put32 = [&](size_t at, uint32_t value) { std::memcpy(row.data() + at, &value, 4); };
// \DEVICE\TCPIP_{index}: Windows names an interface by the GUID the driver gave it, and every interface here has an index it answers
// to instead, so the name carries that and keeps the shape a program parses.
char name[64];
const int written = std::snprintf(name, sizeof name, "\\DEVICE\\TCPIP_{00000000-0000-0000-0000-%012u}", interface.index);
const size_t name_length = std::min<size_t>(written > 0 ? size_t(written) : 0, 255);
for (size_t at = 0; at < name_length; ++at)
row[at * 2] = uint8_t(name[at]);
put32(512, interface.index);
put32(516, interface.type);
put32(520, interface.mtu);
// The link speed is the one a medium of this kind would have; nothing here measures it.
put32(524, interface.type == if_type_loopback ? 10000000 : 1000000000);
put32(528, interface.hardware_length);
std::memcpy(row.data() + 532, interface.hardware, std::min<uint32_t>(interface.hardware_length, 8));
// AdminStatus is enabled; OperStatus is up only while the interface carries traffic.
put32(540, 1);
put32(544, interface.status == 1 ? 1 : 2);
const size_t description = std::min<size_t>(interface.name.size(), 255);
put32(600, uint32_t(description));
std::memcpy(row.data() + 604, interface.name.data(), description);
return row;
}
}