LocalIP / NetIO / DNS (Windows): improves performance

Fixes #2614
This commit is contained in:
李通洲
2026-09-26 11:50:37 +08:00
committed by Carter Li
parent 0c3b852bf7
commit 7fdd1d8e93
4 changed files with 659 additions and 204 deletions
+229 -55
View File
@@ -1,67 +1,241 @@
#include "detection/dns/dns.h"
#include "common/netif.h"
#include "common/mallocHelper.h"
#include "common/debug.h"
#include "common/windows/registry.h"
#include <ws2tcpip.h>
#include <iphlpapi.h>
// The resolvers of an interface are stored in the registry, under a key named after its GUID:
// HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{GUID} IPv4
// HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\{GUID} IPv6
// A statically configured `NameServer` takes precedence over the value handed out by DHCP, which is
// the precedence the IP stack applies as well - verified against GetAdaptersAddresses() on a machine
// with both set, where only the static list was reported.
//
// This replaces GetAdaptersAddresses(), which builds the unicast, anycast, multicast and DNS server
// lists of every adapter - about 9 ms on a machine with 52 of them - even though the module only
// reports the resolvers of the interface that carries the default route. Reading the one key that
// matters costs about 16 us (12 us to open it, 4 us to read the value, see
// src/common/windows/registry.c), and the module as a whole goes from about 11 ms to about 0.3 ms.
//
// Known limitation: a resolver learned from a router advertisement (RDNSS) is not written to the
// registry by any Windows version, so on a SLAAC-only network (no DHCPv6 lease, no static
// `NameServer`) IPv6 resolvers are not reported at all. GetAdaptersAddresses() reported them.
#define FF_DNS_REGISTRY_KEY_V4 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\"
#define FF_DNS_REGISTRY_KEY_V6 L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces\\"
// Enough for either key path plus the 38 characters of a GUID
#define FF_DNS_REGISTRY_PATH_SIZE 256
static void appendAddress(FFlist* results, FFstrbuf* servers, uint32_t start, uint32_t end, FFDNSShowType showType) {
// A link local address may carry a zone index, which the reported address never includes
char address[INET6_ADDRSTRLEN];
uint32_t length = end - start;
if (length >= sizeof(address)) {
length = sizeof(address) - 1;
}
uint32_t i = 0;
for (; i < length && servers->chars[start + i] != '%'; ++i) {
address[i] = servers->chars[start + i];
}
address[i] = '\0';
if (showType & FF_DNS_TYPE_IPV4_BIT) {
IN_ADDR ipv4;
if (InetPtonA(AF_INET, address, &ipv4) == 1) {
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv4AddressToStringA(&ipv4, item->chars) - item->chars);
return;
}
}
if (showType & FF_DNS_TYPE_IPV6_BIT) {
IN6_ADDR ipv6;
if (InetPtonA(AF_INET6, address, &ipv6) == 1) {
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET6_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6, item->chars) - item->chars);
}
}
}
static void appendAddressList(FFstrbuf* servers, FFDNSShowType showType, FFlist* results) {
uint32_t start = 0;
while (start < servers->length) {
while (start < servers->length && (servers->chars[start] == ',' || servers->chars[start] == ' ' || servers->chars[start] == '\t')) {
++start;
}
uint32_t end = start;
while (end < servers->length && servers->chars[end] != ',' && servers->chars[end] != ' ' && servers->chars[end] != '\t') {
++end;
}
if (end > start) {
appendAddress(results, servers, start, end, showType);
}
start = end;
}
}
// A DHCPv6 lease stores its resolvers as a plain list of 16 byte addresses
static void appendAddressArray(const BYTE* data, uint32_t dataLength, FFDNSShowType showType, FFlist* results) {
if (!(showType & FF_DNS_TYPE_IPV6_BIT)) {
return;
}
for (uint32_t offset = 0; offset + sizeof(IN6_ADDR) <= dataLength; offset += sizeof(IN6_ADDR)) {
IN6_ADDR ipv6;
memcpy(&ipv6, data + offset, sizeof(ipv6));
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET6_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6, item->chars) - item->chars);
}
}
// `Dhcpv6InterfaceOptions` stores the DHCPv6 options of the last lease as an array of records: a 20
// byte header (option code, 0, payload length, 0, 0xFFFFFFFF) followed by the payload, padded to 16
// bytes. Option 23 is OPTION_DNS_SERVERS (RFC 3646).
static void appendDhcpv6Options(const BYTE* data, uint32_t dataLength, FFDNSShowType showType, FFlist* results) {
for (uint32_t offset = 0; offset + 20 <= dataLength;) {
uint32_t code, length;
memcpy(&code, data + offset, sizeof(code));
memcpy(&length, data + offset + 8, sizeof(length));
uint32_t payloadLength = dataLength - (offset + 20);
if (payloadLength > length) {
payloadLength = length;
}
if (code == 23) {
appendAddressArray(data + offset + 20, payloadLength, showType, results);
return;
}
if (payloadLength < length) {
return;
}
offset += 20 + ((payloadLength + 15) & ~(uint32_t) 15);
}
}
static void detectV4(const wchar_t* guid, FFDNSOptions* options, FFlist* results) {
wchar_t keyPath[FF_DNS_REGISTRY_PATH_SIZE] = FF_DNS_REGISTRY_KEY_V4;
wcscat(keyPath, guid);
FF_DEBUG("v4 key '%ls'", keyPath);
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, keyPath, &hKey, nullptr)) {
FF_DEBUG("v4 key open failed");
return;
}
FF_STRBUF_AUTO_DESTROY servers = ffStrbufCreate();
// `NameServer` holds the statically configured list and wins over the one handed out by DHCP. A
// value that exists but holds no address must not shadow the fallback, hence the length checks.
bool got = (ffRegReadStrbuf(hKey, L"NameServer", &servers, nullptr) && servers.length) ||
(ffRegReadStrbuf(hKey, L"DhcpNameServer", &servers, nullptr) && servers.length);
FF_DEBUG("v4 got=%d servers='%s'", got, servers.chars);
if (!got) {
return;
}
appendAddressList(&servers, options->showType, results);
FF_DEBUG("v4 results=%u", results->length);
}
static void detectV6(const wchar_t* guid, FFDNSOptions* options, FFlist* results) {
wchar_t keyPath[FF_DNS_REGISTRY_PATH_SIZE] = FF_DNS_REGISTRY_KEY_V6;
wcscat(keyPath, guid);
FF_DEBUG("v6 key '%ls'", keyPath);
FF_AUTO_CLOSE_FD HANDLE hKey = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, keyPath, &hKey, nullptr)) {
FF_DEBUG("v6 key open failed");
return;
}
FF_STRBUF_AUTO_DESTROY servers = ffStrbufCreate();
if (ffRegReadStrbuf(hKey, L"NameServer", &servers, nullptr) && servers.length) {
FF_DEBUG("v6 servers='%s'", servers.chars);
appendAddressList(&servers, options->showType, results);
FF_DEBUG("v6 results=%u", results->length);
return;
}
// The two DHCPv6 values below are documented as REG_BINARY and are always written that way; the
// registry reader reports the bytes without their type, so the parsing below trusts that.
BYTE data[4096];
FFArgBuffer dhcpv6Servers = { .data = data, .length = sizeof(data) };
if (ffRegReadData(hKey, L"Dhcpv6DNSServers", &dhcpv6Servers, nullptr)) {
FF_DEBUG("v6 Dhcpv6DNSServers cb=%u", dhcpv6Servers.length);
appendAddressArray(data, dhcpv6Servers.length, options->showType, results);
return;
}
FFArgBuffer dhcpv6Options = { .data = data, .length = sizeof(data) };
if (ffRegReadData(hKey, L"Dhcpv6InterfaceOptions", &dhcpv6Options, nullptr)) {
FF_DEBUG("v6 Dhcpv6InterfaceOptions cb=%u", dhcpv6Options.length);
appendDhcpv6Options(data, dhcpv6Options.length, options->showType, results);
return;
}
FF_DEBUG("v6 no resolver");
}
const char* ffDetectDNS(FFDNSOptions* options, FFlist* results) {
FF_AUTO_FREE IP_ADAPTER_ADDRESSES* adapter_addresses = nullptr;
// Multiple attempts in case interfaces change while
// we are in the middle of querying them.
DWORD adapter_addresses_buffer_size = 0;
for (int attempts = 0;; ++attempts) {
if (adapter_addresses_buffer_size) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size);
assert(adapter_addresses);
}
DWORD error = GetAdaptersAddresses(
options->showType & FF_DNS_TYPE_IPV4_BIT
? options->showType & FF_DNS_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
: AF_INET6,
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_FRIENDLY_NAME,
nullptr,
adapter_addresses,
&adapter_addresses_buffer_size);
if (error == ERROR_SUCCESS) {
break;
} else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) {
continue;
} else {
return "GetAdaptersAddresses() failed";
}
FF_DEBUG("enter, showType=%u", (unsigned) options->showType);
// The interface that carries the default route is the one the IP stack resolves names through.
// ffNetifGetDefaultRouteV4() already ignores the interfaces that are not operational.
NET_IFINDEX ifIndex = (NET_IFINDEX) ffNetifGetDefaultRouteV4()->ifIndex;
FF_DEBUG("ifIndex=%u", (unsigned) ifIndex);
if (!ifIndex) {
ifIndex = (NET_IFINDEX) ffNetifGetDefaultRouteV6()->ifIndex;
}
if (!ifIndex) {
return "Failed to detect default route";
}
uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex;
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) {
if (adapter->IfIndex != defaultRouteIfIndex) {
continue;
}
if (adapter->OperStatus != IfOperStatusUp) {
continue;
}
for (IP_ADAPTER_DNS_SERVER_ADDRESS_XP* ifa = adapter->FirstDnsServerAddress; ifa; ifa = ifa->Next) {
// The element must only be added once it can be filled: `FF_LIST_ADD` does not zero the
// new element, so an unrecognized address family used to leave an uninitialized FFstrbuf
// in the list, which the callers then read and free
if (ifa->Address.lpSockaddr->sa_family == AF_INET) {
SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr;
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv4AddressToStringA(&ipv4->sin_addr, item->chars) - item->chars);
} else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) {
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
FFstrbuf* item = FF_LIST_ADD(FFstrbuf, *results);
ffStrbufInitA(item, INET6_ADDRSTRLEN);
item->length = (uint32_t) (RtlIpv6AddressToStringA(&ipv6->sin6_addr, item->chars) - item->chars);
}
}
break;
NET_LUID luid;
if (ConvertInterfaceIndexToLuid(ifIndex, &luid) != NO_ERROR) {
return "ConvertInterfaceIndexToLuid() failed";
}
GUID guid;
if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
return "ConvertInterfaceLuidToGuid() failed";
}
wchar_t guidStr[64];
// clang-format off
swprintf(guidStr, ARRAY_SIZE(guidStr), L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
(unsigned long) guid.Data1,
(unsigned int) guid.Data2,
(unsigned int) guid.Data3,
(unsigned int) guid.Data4[0],
(unsigned int) guid.Data4[1],
(unsigned int) guid.Data4[2],
(unsigned int) guid.Data4[3],
(unsigned int) guid.Data4[4],
(unsigned int) guid.Data4[5],
(unsigned int) guid.Data4[6],
(unsigned int) guid.Data4[7]);
// clang-format on
// IPv4 first, so the list is grouped by address family the same way the printed output is
if (options->showType & FF_DNS_TYPE_IPV4_BIT) {
detectV4(guidStr, options, results);
}
if (options->showType & FF_DNS_TYPE_IPV6_BIT) {
detectV6(guidStr, options, results);
}
FF_DEBUG("return, results=%u", results->length);
return nullptr;
}
+359 -94
View File
@@ -2,27 +2,269 @@
#include <iphlpapi.h>
#include "common/netif.h"
#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
#include "common/debug.h"
#include "localip.h"
#define FF_LOCALIP_NIFLAG(name) { IP_ADAPTER_##name, #name }
static inline void appendFlag(FFstrbuf* flags, bool enabled, const char* name) {
if (!enabled) {
return;
}
static const FFLocalIpNIFlag niFlagOptions[] = {
FF_LOCALIP_NIFLAG(DDNS_ENABLED),
FF_LOCALIP_NIFLAG(REGISTER_ADAPTER_SUFFIX),
FF_LOCALIP_NIFLAG(DHCP_ENABLED),
FF_LOCALIP_NIFLAG(RECEIVE_ONLY),
FF_LOCALIP_NIFLAG(NO_MULTICAST),
FF_LOCALIP_NIFLAG(IPV6_OTHER_STATEFUL_CONFIG),
FF_LOCALIP_NIFLAG(NETBIOS_OVER_TCPIP_ENABLED),
FF_LOCALIP_NIFLAG(IPV4_ENABLED),
FF_LOCALIP_NIFLAG(IPV6_ENABLED),
FF_LOCALIP_NIFLAG(IPV6_MANAGE_ADDRESS_CONFIG),
// sentinel
{},
};
if (flags->length) {
ffStrbufAppendC(flags, ',');
}
ffStrbufAppendS(flags, name);
}
static void fillInterfaceFlags(FFstrbuf* flags, const MIB_IF_ROW2* row) {
appendFlag(flags, row->InterfaceAndOperStatusFlags.HardwareInterface, "HARDWARE");
appendFlag(flags, row->InterfaceAndOperStatusFlags.FilterInterface, "FILTER");
appendFlag(flags, row->InterfaceAndOperStatusFlags.ConnectorPresent, "CONNECTOR_PRESENT");
appendFlag(flags, row->InterfaceAndOperStatusFlags.NotAuthenticated, "NOT_AUTHENTICATED");
appendFlag(flags, row->InterfaceAndOperStatusFlags.NotMediaConnected, "NO_MEDIA");
appendFlag(flags, row->InterfaceAndOperStatusFlags.Paused, "PAUSED");
appendFlag(flags, row->InterfaceAndOperStatusFlags.LowPower, "LOW_POWER");
appendFlag(flags, row->InterfaceAndOperStatusFlags.EndPointInterface, "ENDPOINT");
switch (row->DirectionType) {
case NET_IF_DIRECTION_SENDONLY:
appendFlag(flags, true, "SEND_ONLY");
break;
case NET_IF_DIRECTION_RECEIVEONLY:
appendFlag(flags, true, "RECEIVE_ONLY");
break;
case NET_IF_DIRECTION_SENDRECEIVE:
appendFlag(flags, true, "SEND_RECEIVE");
break;
default:
break;
}
// Exactly one value of each of these enums is reported per interface, so every value gets
// a flag of its own
#define FF_LOCALIP_FLAG_CASE(value, name) \
case value: appendFlag(flags, true, name); break;
switch (row->MediaConnectState) {
FF_LOCALIP_FLAG_CASE(MediaConnectStateUnknown, "MEDIA_UNKNOWN")
FF_LOCALIP_FLAG_CASE(MediaConnectStateConnected, "MEDIA_CONNECTED")
FF_LOCALIP_FLAG_CASE(MediaConnectStateDisconnected, "MEDIA_DISCONNECTED")
}
// The interface type, as registered with IANA and listed in ipifcons.h
switch (row->Type) {
FF_LOCALIP_FLAG_CASE(IF_TYPE_OTHER, "OTHER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_REGULAR_1822, "REGULAR_1822")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HDH_1822, "HDH_1822")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DDN_X25, "DDN_X25")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC877_X25, "RFC877_X25")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_CSMACD, "ETHERNET_CSMACD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IS088023_CSMACD, "IS088023_CSMACD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88024_TOKENBUS, "ISO88024_TOKENBUS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_TOKENRING, "ISO88025_TOKENRING")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88026_MAN, "ISO88026_MAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_STARLAN, "STARLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_10MBIT, "PROTEON_10MBIT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_80MBIT, "PROTEON_80MBIT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HYPERCHANNEL, "HYPERCHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FDDI, "FDDI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_B, "LAP_B")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SDLC, "SDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1, "DS1")
FF_LOCALIP_FLAG_CASE(IF_TYPE_E1, "E1")
FF_LOCALIP_FLAG_CASE(IF_TYPE_BASIC_ISDN, "BASIC_ISDN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PRIMARY_ISDN, "PRIMARY_ISDN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_POINT2POINT_SERIAL, "PROP_POINT2POINT_SERIAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PPP, "PPP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SOFTWARE_LOOPBACK, "SOFTWARE_LOOPBACK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_EON, "EON")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_3MBIT, "ETHERNET_3MBIT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_NSIP, "NSIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SLIP, "SLIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ULTRA, "ULTRA")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS3, "DS3")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SIP, "SIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY, "FRAMERELAY")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RS232, "RS232")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PARA, "PARA")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET, "ARCNET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET_PLUS, "ARCNET_PLUS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM, "ATM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MIO_X25, "MIO_X25")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET, "SONET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_PLE, "X25_PLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88022_LLC, "ISO88022_LLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LOCALTALK, "LOCALTALK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_DXI, "SMDS_DXI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_SERVICE, "FRAMERELAY_SERVICE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V35, "V35")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HSSI, "HSSI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPI, "HIPPI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MODEM, "MODEM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL5, "AAL5")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_PATH, "SONET_PATH")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_VT, "SONET_VT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_ICIP, "SMDS_ICIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_VIRTUAL, "PROP_VIRTUAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_MULTIPLEXOR, "PROP_MULTIPLEXOR")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80212, "IEEE80212")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FIBRECHANNEL, "FIBRECHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPIINTERFACE, "HIPPIINTERFACE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_INTERCONNECT, "FRAMERELAY_INTERCONNECT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8023, "AFLANE_8023")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8025, "AFLANE_8025")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CCTEMUL, "CCTEMUL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER, "FASTETHER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN, "ISDN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V11, "V11")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V36, "V36")
FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_64K, "G703_64K")
FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_2MB, "G703_2MB")
FF_LOCALIP_FLAG_CASE(IF_TYPE_QLLC, "QLLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER_FX, "FASTETHER_FX")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CHANNEL, "CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80211, "IEEE80211")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IBM370PARCHAN, "IBM370PARCHAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ESCON, "ESCON")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DLSW, "DLSW")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_S, "ISDN_S")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_U, "ISDN_U")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_D, "LAP_D")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPSWITCH, "IPSWITCH")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RSRB, "RSRB")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_LOGICAL, "ATM_LOGICAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0, "DS0")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0_BUNDLE, "DS0_BUNDLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_BSC, "BSC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ASYNC, "ASYNC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CNR, "CNR")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025R_DTR, "ISO88025R_DTR")
FF_LOCALIP_FLAG_CASE(IF_TYPE_EPLRS, "EPLRS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ARAP, "ARAP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_CNLS, "PROP_CNLS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HOSTPAD, "HOSTPAD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TERMPAD, "TERMPAD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_MPI, "FRAMERELAY_MPI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X213, "X213")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ADSL, "ADSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RADSL, "RADSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SDSL, "SDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VDSL, "VDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_CRFPRINT, "ISO88025_CRFPRINT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MYRINET, "MYRINET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_EM, "VOICE_EM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXO, "VOICE_FXO")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXS, "VOICE_FXS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_ENCAP, "VOICE_ENCAP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_OVERIP, "VOICE_OVERIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_DXI, "ATM_DXI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_FUNI, "ATM_FUNI")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_IMA, "ATM_IMA")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PPPMULTILINKBUNDLE, "PPPMULTILINKBUNDLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CDLC, "IPOVER_CDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CLAW, "IPOVER_CLAW")
FF_LOCALIP_FLAG_CASE(IF_TYPE_STACKTOSTACK, "STACKTOSTACK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VIRTUALIPADDRESS, "VIRTUALIPADDRESS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MPC, "MPC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_ATM, "IPOVER_ATM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_FIBER, "ISO88025_FIBER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TDLC, "TDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_GIGABITETHERNET, "GIGABITETHERNET")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HDLC, "HDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_F, "LAP_F")
FF_LOCALIP_FLAG_CASE(IF_TYPE_V37, "V37")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_MLP, "X25_MLP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_HUNTGROUP, "X25_HUNTGROUP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TRANSPHDLC, "TRANSPHDLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_INTERLEAVE, "INTERLEAVE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FAST, "FAST")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IP, "IP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_MACLAYER, "DOCSCABLE_MACLAYER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_DOWNSTREAM, "DOCSCABLE_DOWNSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_UPSTREAM, "DOCSCABLE_UPSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_A12MPPSWITCH, "A12MPPSWITCH")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TUNNEL, "TUNNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_COFFEE, "COFFEE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_CES, "CES")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_SUBINTERFACE, "ATM_SUBINTERFACE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_L2_VLAN, "L2_VLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPVLAN, "L3_IPVLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPXVLAN, "L3_IPXVLAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITALPOWERLINE, "DIGITALPOWERLINE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MEDIAMAILOVERIP, "MEDIAMAILOVERIP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DTM, "DTM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DCN, "DCN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IPFORWARD, "IPFORWARD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MSDSL, "MSDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE1394, "IEEE1394")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IF_GSN, "IF_GSN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_MACLAYER, "DVBRCC_MACLAYER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_DOWNSTREAM, "DVBRCC_DOWNSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_UPSTREAM, "DVBRCC_UPSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VIRTUAL, "ATM_VIRTUAL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS_TUNNEL, "MPLS_TUNNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SRP, "SRP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERATM, "VOICEOVERATM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERFRAMERELAY, "VOICEOVERFRAMERELAY")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IDSL, "IDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_COMPOSITELINK, "COMPOSITELINK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SS7_SIGLINK, "SS7_SIGLINK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_WIRELESS_P2P, "PROP_WIRELESS_P2P")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_FORWARD, "FR_FORWARD")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC1483, "RFC1483")
FF_LOCALIP_FLAG_CASE(IF_TYPE_USB, "USB")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE8023AD_LAG, "IEEE8023AD_LAG")
FF_LOCALIP_FLAG_CASE(IF_TYPE_BGP_POLICY_ACCOUNTING, "BGP_POLICY_ACCOUNTING")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FRF16_MFR_BUNDLE, "FRF16_MFR_BUNDLE")
FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_GATEKEEPER, "H323_GATEKEEPER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_PROXY, "H323_PROXY")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS, "MPLS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MF_SIGLINK, "MF_SIGLINK")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HDSL2, "HDSL2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SHDSL, "SHDSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1_FDL, "DS1_FDL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_POS, "POS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_IN, "DVB_ASI_IN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_OUT, "DVB_ASI_OUT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PLC, "PLC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_NFAS, "NFAS")
FF_LOCALIP_FLAG_CASE(IF_TYPE_TR008, "TR008")
FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_RDT, "GR303_RDT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_IDT, "GR303_IDT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ISUP, "ISUP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_MACLAYER, "PROP_DOCS_WIRELESS_MACLAYER")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_DOWNSTREAM, "PROP_DOCS_WIRELESS_DOWNSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_UPSTREAM, "PROP_DOCS_WIRELESS_UPSTREAM")
FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPERLAN2, "HIPERLAN2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_BWA_P2MP, "PROP_BWA_P2MP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_OVERHEAD_CHANNEL, "SONET_OVERHEAD_CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITAL_WRAPPER_OVERHEAD_CHANNEL, "DIGITAL_WRAPPER_OVERHEAD_CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL2, "AAL2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_RADIO_MAC, "RADIO_MAC")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_RADIO, "ATM_RADIO")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IMT, "IMT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_MVL, "MVL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_REACH_DSL, "REACH_DSL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_DLCI_ENDPT, "FR_DLCI_ENDPT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VCI_ENDPT, "ATM_VCI_ENDPT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_CHANNEL, "OPTICAL_CHANNEL")
FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_TRANSPORT, "OPTICAL_TRANSPORT")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80216_WMAN, "IEEE80216_WMAN")
FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP, "WWANPP")
FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP2, "WWANPP2")
FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE802154, "IEEE802154")
FF_LOCALIP_FLAG_CASE(IF_TYPE_XBOX_WIRELESS, "XBOX_WIRELESS")
}
#undef FF_LOCALIP_FLAG_CASE
}
static bool isDefaultRouteInterface(const FFLocalIpOptions* options, NET_IFINDEX ifIndex) {
// Both route lookups are cached, so this is cheap even inside the per interface loop
return ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex) ||
((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
}
const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
FF_DEBUG("Starting local IP detection with showType=0x%X, namePrefix='%.*s'",
@@ -30,89 +272,102 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
(int) options->namePrefix.length,
options->namePrefix.chars);
FF_AUTO_FREE IP_ADAPTER_ADDRESSES* adapter_addresses = nullptr;
ADDRESS_FAMILY family = options->showType & FF_LOCALIP_TYPE_IPV4_BIT
? options->showType & FF_LOCALIP_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
: AF_INET6;
bool queryV4 = (options->showType & FF_LOCALIP_TYPE_IPV4_BIT) != 0;
bool queryV6 = (options->showType & FF_LOCALIP_TYPE_IPV6_BIT) != 0;
// Multiple attempts in case interfaces change while
// we are in the middle of querying them.
DWORD adapter_addresses_buffer_size = 0;
for (int attempts = 0;; ++attempts) {
FF_DEBUG("Attempt %d to get adapter addresses, buffer size: %lu", attempts + 1, adapter_addresses_buffer_size);
if (adapter_addresses_buffer_size) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size);
assert(adapter_addresses);
// Every interface is queried with GetIfEntry2() rather than by walking a table of all of them:
// GetIfTable2() costs about 0.95 ms on a machine with a few dozen adapters however few of them
// are reported, because it marshals every registered one, while GetIfEntry2() costs about 24 us
// per interface. With 52 adapters registered and 8 interfaces carrying an address the per
// interface API is 3.6 times faster overall; the table only wins past roughly 40 interfaces with
// an address, and with FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT (the default) at most one interface
// can pass the filter below anyway.
bool defaultRouteOnly = (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) != 0;
if (defaultRouteOnly) {
if (queryV4) {
FF_DEBUG("Querying the IPv4 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV4()->ifIndex);
}
DWORD family = options->showType & FF_LOCALIP_TYPE_IPV4_BIT
? options->showType & FF_LOCALIP_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
: AF_INET6;
FF_DEBUG("Calling GetAdaptersAddresses with family=%u, flags=0x%X", (unsigned) family, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER);
DWORD error = GetAdaptersAddresses(
family,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
nullptr,
adapter_addresses,
&adapter_addresses_buffer_size);
if (error == ERROR_SUCCESS) {
FF_DEBUG("GetAdaptersAddresses succeeded on attempt %d", attempts + 1);
break;
} else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) {
FF_DEBUG("Buffer overflow, need %lu bytes, retrying", adapter_addresses_buffer_size);
continue;
} else {
FF_DEBUG("GetAdaptersAddresses failed with error %lu after %d attempts", error, attempts + 1);
return "GetAdaptersAddresses() failed";
if (queryV6) {
FF_DEBUG("Querying the IPv6 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV6()->ifIndex);
}
}
PMIB_UNICASTIPADDRESS_TABLE table = nullptr;
const char* errorMessage = nullptr;
// The unicast address table is the only API that reports per interface addresses and it cannot
// be filtered by interface, so it is queried once and only the rows of the interfaces which pass
// the filter below are read
if (!NETIO_SUCCESS(GetUnicastIpAddressTable(family, &table))) {
FF_DEBUG("GetUnicastIpAddressTable failed");
errorMessage = "GetUnicastIpAddressTable() failed";
goto cleanup;
}
[[maybe_unused]] int adapterCount = 0, processedCount = 0;
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) {
// Rows of the same interface are not adjacent (IPv4 rows come first),
// so each interface is handled at its first row
for (ULONG i = 0; i < table->NumEntries; ++i) {
NET_IFINDEX ifIndex = table->Table[i].InterfaceIndex;
bool seen = false;
for (ULONG j = 0; j < i && !seen; ++j) {
seen = table->Table[j].InterfaceIndex == ifIndex;
}
if (seen) {
continue;
}
adapterCount++;
// Checked first to avoid querying interfaces we won't show
if (defaultRouteOnly && !isDefaultRouteInterface(options, ifIndex)) {
FF_DEBUG("Skipping interface %u (not default route interface)", (unsigned) ifIndex);
continue;
}
MIB_IF_ROW2 ifRow = { .InterfaceIndex = ifIndex };
if (!NETIO_SUCCESS(GetIfEntry2(&ifRow))) {
FF_DEBUG("GetIfEntry2(%u) failed", (unsigned) ifIndex);
continue;
}
FF_DEBUG("Processing adapter %d: IfIndex=%u, IfType=%u, OperStatus=%u",
adapterCount,
(unsigned) adapter->IfIndex,
(unsigned) adapter->IfType,
(unsigned) adapter->OperStatus);
(unsigned) ifIndex,
(unsigned) ifRow.Type,
(unsigned) ifRow.OperStatus);
if (adapter->OperStatus != IfOperStatusUp) {
FF_DEBUG("Skipping adapter %u (not operational, status=%d)", (unsigned) adapter->IfIndex, adapter->OperStatus);
if (ifRow.OperStatus != IfOperStatusUp) {
FF_DEBUG("Skipping adapter %u (not operational, status=%d)", (unsigned) ifIndex, ifRow.OperStatus);
continue;
}
bool isLoop = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK;
FF_DEBUG("Adapter %u: isLoopback=%s", (unsigned) adapter->IfIndex, isLoop ? "true" : "false");
bool isLoop = ifRow.Type == IF_TYPE_SOFTWARE_LOOPBACK;
FF_DEBUG("Adapter %u: isLoopback=%s", (unsigned) ifIndex, isLoop ? "true" : "false");
if (isLoop && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) {
FF_DEBUG("Skipping loopback adapter %u (loopback not requested)", (unsigned) adapter->IfIndex);
FF_DEBUG("Skipping loopback adapter %u (loopback not requested)", (unsigned) ifIndex);
continue;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(adapter->FriendlyName);
FF_DEBUG("Adapter %u name: '%s'", (unsigned) adapter->IfIndex, name.chars);
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(ifRow.Alias);
FF_DEBUG("Adapter %u name: '%s'", (unsigned) ifIndex, name.chars);
if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
FF_DEBUG("Skipping adapter %u (name doesn't match prefix '%.*s')",
(unsigned) adapter->IfIndex,
(unsigned) ifIndex,
(int) options->namePrefix.length,
options->namePrefix.chars);
continue;
}
if (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) {
if (!((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == adapter->IfIndex) &&
!((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == adapter->IfIndex)) {
FF_DEBUG("Skipping interface %u (not default route interface)", (unsigned) adapter->IfIndex);
continue;
}
}
processedCount++;
FF_DEBUG("Creating result item for adapter %u ('%s')", (unsigned) adapter->IfIndex, name.chars);
FF_DEBUG("Creating result item for adapter %u ('%s')", (unsigned) ifIndex, name.chars);
FFLocalIpResult* item = FF_LIST_ADD(FFLocalIpResult, *results);
ffStrbufInitMove(&item->name, &name);
@@ -124,16 +379,26 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
item->speed = -1;
item->mtu = -1;
if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT) {
fillInterfaceFlags(&item->flags, &ifRow);
FF_DEBUG("Adapter %u flags: '%s'", (unsigned) ifIndex, item->flags.chars);
}
uint32_t typesToAdd = options->showType & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT);
FF_DEBUG("Types to add for adapter %u: 0x%X", (unsigned) adapter->IfIndex, typesToAdd);
FF_DEBUG("Types to add for adapter %u: 0x%X", (unsigned) ifIndex, typesToAdd);
[[maybe_unused]] int ipv4Count = 0, ipv6Count = 0;
for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next) {
for (ULONG j = i; j < table->NumEntries; ++j) {
MIB_UNICASTIPADDRESS_ROW* ifa = &table->Table[j];
if (ifa->InterfaceIndex != ifIndex) {
continue;
}
FF_DEBUG("Processing unicast address: prefix origin=%d, suffix origin=%d, family=%d, DadState=%d",
ifa->PrefixOrigin,
ifa->SuffixOrigin,
ifa->Address.lpSockaddr->sa_family,
ifa->Address.si_family,
ifa->DadState);
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)) {
@@ -150,19 +415,19 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
// MIB_UNICASTIPADDRESS_ROW::SkipAsSource
}
if (ifa->Address.lpSockaddr->sa_family == AF_INET) {
if (ifa->Address.si_family == AF_INET) {
if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
FF_DEBUG("Skipping IPv4 address (not requested in typesToAdd=0x%X)", typesToAdd);
continue;
}
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == adapter->IfIndex);
if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) {
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex);
if (defaultRouteOnly && !isDefaultRoute) {
FF_DEBUG("Skipping IPv4 address (not on default route interface)");
continue;
}
SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr;
SOCKADDR_IN* ipv4 = &ifa->Address.Ipv4;
char addressBuffer[INET_ADDRSTRLEN + 10];
char* end = RtlIpv4AddressToStringA(&ipv4->sin_addr, addressBuffer);
@@ -185,13 +450,13 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
if (typesToAdd == 0) {
break;
}
} else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) {
} else if (ifa->Address.si_family == AF_INET6) {
if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
FF_DEBUG("Skipping IPv6 address (not requested in typesToAdd=0x%X)", typesToAdd);
continue;
}
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
SOCKADDR_IN6* ipv6 = &ifa->Address.Ipv6;
FFLocalIpIpv6Type ipv6Type = FF_LOCALIP_IPV6_TYPE_NONE;
if (IN6_IS_ADDR_GLOBAL(&ipv6->sin6_addr)) {
@@ -209,8 +474,8 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
continue;
}
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == adapter->IfIndex);
if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) {
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
if (defaultRouteOnly && !isDefaultRoute) {
FF_DEBUG("Skipping IPv6 address (not on default route interface)");
continue;
}
@@ -240,24 +505,20 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
}
}
FF_DEBUG("Adapter %u: collected %d IPv4 and %d IPv6 addresses", (unsigned) adapter->IfIndex, ipv4Count, ipv6Count);
FF_DEBUG("Adapter %u: collected %d IPv4 and %d IPv6 addresses", (unsigned) ifIndex, ipv4Count, ipv6Count);
if (options->showType & FF_LOCALIP_TYPE_SPEED_BIT) {
item->speed = (int32_t) (adapter->ReceiveLinkSpeed / 1000000);
FF_DEBUG("Adapter %u speed: %d Mbps (raw: %llu)", (unsigned) adapter->IfIndex, item->speed, adapter->ReceiveLinkSpeed);
item->speed = (int32_t) (ifRow.ReceiveLinkSpeed / 1000000);
FF_DEBUG("Adapter %u speed: %d Mbps (raw: %llu)", (unsigned) ifIndex, item->speed, ifRow.ReceiveLinkSpeed);
}
if (options->showType & FF_LOCALIP_TYPE_MTU_BIT) {
item->mtu = (int32_t) adapter->Mtu;
FF_DEBUG("Adapter %u MTU: %d", (unsigned) adapter->IfIndex, item->mtu);
item->mtu = (int32_t) ifRow.Mtu;
FF_DEBUG("Adapter %u MTU: %d", (unsigned) ifIndex, item->mtu);
}
if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT) {
ffLocalIpFillNIFlags(&item->flags, adapter->Flags, niFlagOptions);
FF_DEBUG("Adapter %u flags: 0x%lX -> '%s'", (unsigned) adapter->IfIndex, adapter->Flags, item->flags.chars);
}
if (options->showType & FF_LOCALIP_TYPE_MAC_BIT && adapter->PhysicalAddressLength == 6) {
uint8_t* ptr = adapter->PhysicalAddress;
if (options->showType & FF_LOCALIP_TYPE_MAC_BIT && ifRow.PhysicalAddressLength == 6) {
const uint8_t* ptr = ifRow.PhysicalAddress;
ffStrbufSetF(&item->mac, "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
FF_DEBUG("Adapter %u MAC: %s", (unsigned) adapter->IfIndex, item->mac.chars);
FF_DEBUG("Adapter %u MAC: %s", (unsigned) ifIndex, item->mac.chars);
}
}
@@ -266,5 +527,9 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
processedCount,
results->length);
return nullptr;
cleanup:
if (table) {
FreeMibTable(table);
}
return errorMessage;
}
+63 -55
View File
@@ -1,71 +1,79 @@
#include "netio.h"
#include "common/netif.h"
#include "common/mallocHelper.h"
#include "common/windows/unicode.h"
#include <ws2tcpip.h>
#include <iphlpapi.h>
const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) {
FF_AUTO_FREE IP_ADAPTER_ADDRESSES* adapter_addresses = nullptr;
// Multiple attempts in case interfaces change while
// we are in the middle of querying them.
DWORD adapter_addresses_buffer_size = 0;
for (int attempts = 0;; ++attempts) {
if (adapter_addresses_buffer_size) {
adapter_addresses = (IP_ADAPTER_ADDRESSES*) realloc(adapter_addresses, adapter_addresses_buffer_size);
assert(adapter_addresses);
}
DWORD error = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
nullptr,
adapter_addresses,
&adapter_addresses_buffer_size);
if (error == ERROR_SUCCESS) {
break;
} else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) {
continue;
} else {
return "GetAdaptersAddresses() failed";
}
static void addInterface(FFlist* result, const FFNetIOOptions* options, NET_IFINDEX ifIndex, bool isDefaultRoute) {
MIB_IF_ROW2 ifRow = { .InterfaceIndex = ifIndex };
if (!NETIO_SUCCESS(GetIfEntry2(&ifRow))) {
return;
}
// localip reports the interfaces that are operational only, so the same filter is applied here
if (ifRow.OperStatus != IfOperStatusUp) {
return;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(ifRow.Alias);
if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
return;
}
FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result);
*counters = (FFNetIOResult) {
.name = ffStrbufCreateMove(&name),
.txBytes = ifRow.OutOctets,
.rxBytes = ifRow.InOctets,
.txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts),
.rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts),
.rxErrors = ifRow.InErrors,
.txErrors = ifRow.OutErrors,
.rxDrops = ifRow.InDiscards,
.txDrops = ifRow.OutDiscards,
.defaultRoute = isDefaultRoute,
};
}
const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) {
uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex;
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) {
bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex;
if (options->defaultRouteOnly && !isDefaultRoute) {
continue;
}
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(adapter->FriendlyName);
if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
continue;
}
MIB_IF_ROW2 ifRow = { .InterfaceIndex = adapter->IfIndex };
if (GetIfEntry2(&ifRow) == NO_ERROR) {
FFNetIOResult* counters = FF_LIST_ADD(FFNetIOResult, *result);
*counters = (FFNetIOResult) {
.name = ffStrbufCreateMove(&name),
.txBytes = ifRow.OutOctets,
.rxBytes = ifRow.InOctets,
.txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts),
.rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts),
.rxErrors = ifRow.InErrors,
.txErrors = ifRow.OutErrors,
.rxDrops = ifRow.InDiscards,
.txDrops = ifRow.OutDiscards,
.defaultRoute = isDefaultRoute,
};
}
// With `defaultRouteOnly` (the default) the interface carrying the default route is the only one
// that can be reported, so it is queried with the single entry API instead of walking a table of
// every interface: GetIfEntry2() on one interface costs about 12 us.
if (options->defaultRouteOnly) {
addInterface(result, options, (NET_IFINDEX) defaultRouteIfIndex, true);
return nullptr;
}
// The interface set is taken from the unicast address table, the same source localip uses, so
// both modules report the same interfaces. Walking the interface table instead would also report
// the NDIS filter layers of every adapter (Npcap, QoS, WFP, ...) and the adapters that carry no
// address at all, and those cannot be told apart from real adapters by any MIB_IF_ROW2 field.
// GetUnicastIpAddressTable() costs about 72 us, GetIfTable2() about 860 us.
PMIB_UNICASTIPADDRESS_TABLE addressTable = nullptr;
if (!NETIO_SUCCESS(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable))) {
return "GetUnicastIpAddressTable() failed";
}
for (ULONG i = 0; i < addressTable->NumEntries; ++i) {
NET_IFINDEX ifIndex = addressTable->Table[i].InterfaceIndex;
// Rows of the same interface are not adjacent (IPv4 rows come first),
// so each interface is handled at its first row
bool seen = false;
for (ULONG j = 0; j < i && !seen; ++j) {
seen = addressTable->Table[j].InterfaceIndex == ifIndex;
}
if (seen) {
continue;
}
addInterface(result, options, ifIndex, ifIndex == defaultRouteIfIndex);
}
FreeMibTable(addressTable);
return nullptr;
}