mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-27 23:30:47 +01:00
@@ -1,3 +1,11 @@
|
||||
# Unreleased
|
||||
|
||||
Changes:
|
||||
* LocalIP flags on Windows now describe the interface itself — its operational status, its media connection state and its type, for example `ETHERNET_CSMACD`, `IEEE80211` or `SOFTWARE_LOOPBACK` — instead of the `IP_ADAPTER_*` bits.
|
||||
|
||||
Features:
|
||||
* Improved performance of LocalIP, NetIO and DNS on Windows
|
||||
|
||||
# 2.69.0
|
||||
|
||||
Changes:
|
||||
|
||||
+229
-55
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user