ohmu
·
2026-10-15
1#include <ws2tcpip.h>
2#include <iphlpapi.h>
3
4#include "common/netif.h"
5#include "common/windows/unicode.h"
6#include "common/debug.h"
7#include "localip.h"
8
9static inline void appendFlag(FFstrbuf* flags, bool enabled, const char* name) {
10 if (!enabled) {
11 return;
12 }
13
14 if (flags->length) {
15 ffStrbufAppendC(flags, ',');
16 }
17 ffStrbufAppendS(flags, name);
18}
19
20static void fillInterfaceFlags(FFstrbuf* flags, const MIB_IF_ROW2* row) {
21 appendFlag(flags, row->InterfaceAndOperStatusFlags.HardwareInterface, "HARDWARE");
22 appendFlag(flags, row->InterfaceAndOperStatusFlags.FilterInterface, "FILTER");
23 appendFlag(flags, row->InterfaceAndOperStatusFlags.ConnectorPresent, "CONNECTOR_PRESENT");
24 appendFlag(flags, row->InterfaceAndOperStatusFlags.NotAuthenticated, "NOT_AUTHENTICATED");
25 appendFlag(flags, row->InterfaceAndOperStatusFlags.NotMediaConnected, "NO_MEDIA");
26 appendFlag(flags, row->InterfaceAndOperStatusFlags.Paused, "PAUSED");
27 appendFlag(flags, row->InterfaceAndOperStatusFlags.LowPower, "LOW_POWER");
28 appendFlag(flags, row->InterfaceAndOperStatusFlags.EndPointInterface, "ENDPOINT");
29
30 switch (row->DirectionType) {
31 case NET_IF_DIRECTION_SENDONLY:
32 appendFlag(flags, true, "SEND_ONLY");
33 break;
34 case NET_IF_DIRECTION_RECEIVEONLY:
35 appendFlag(flags, true, "RECEIVE_ONLY");
36 break;
37 case NET_IF_DIRECTION_SENDRECEIVE:
38 appendFlag(flags, true, "SEND_RECEIVE");
39 break;
40 default:
41 break;
42 }
43
44 // Exactly one value of each of these enums is reported per interface, so every value gets
45 // a flag of its own
46#define FF_LOCALIP_FLAG_CASE(value, name) \
47 case value: appendFlag(flags, true, name); break;
48
49 switch (row->MediaConnectState) {
50 FF_LOCALIP_FLAG_CASE(MediaConnectStateUnknown, "MEDIA_UNKNOWN")
51 FF_LOCALIP_FLAG_CASE(MediaConnectStateConnected, "MEDIA_CONNECTED")
52 FF_LOCALIP_FLAG_CASE(MediaConnectStateDisconnected, "MEDIA_DISCONNECTED")
53 }
54
55 // The interface type, as registered with IANA and listed in ipifcons.h
56 switch (row->Type) {
57 FF_LOCALIP_FLAG_CASE(IF_TYPE_OTHER, "OTHER")
58 FF_LOCALIP_FLAG_CASE(IF_TYPE_REGULAR_1822, "REGULAR_1822")
59 FF_LOCALIP_FLAG_CASE(IF_TYPE_HDH_1822, "HDH_1822")
60 FF_LOCALIP_FLAG_CASE(IF_TYPE_DDN_X25, "DDN_X25")
61 FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC877_X25, "RFC877_X25")
62 FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_CSMACD, "ETHERNET_CSMACD")
63 FF_LOCALIP_FLAG_CASE(IF_TYPE_IS088023_CSMACD, "IS088023_CSMACD")
64 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88024_TOKENBUS, "ISO88024_TOKENBUS")
65 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_TOKENRING, "ISO88025_TOKENRING")
66 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88026_MAN, "ISO88026_MAN")
67 FF_LOCALIP_FLAG_CASE(IF_TYPE_STARLAN, "STARLAN")
68 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_10MBIT, "PROTEON_10MBIT")
69 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROTEON_80MBIT, "PROTEON_80MBIT")
70 FF_LOCALIP_FLAG_CASE(IF_TYPE_HYPERCHANNEL, "HYPERCHANNEL")
71 FF_LOCALIP_FLAG_CASE(IF_TYPE_FDDI, "FDDI")
72 FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_B, "LAP_B")
73 FF_LOCALIP_FLAG_CASE(IF_TYPE_SDLC, "SDLC")
74 FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1, "DS1")
75 FF_LOCALIP_FLAG_CASE(IF_TYPE_E1, "E1")
76 FF_LOCALIP_FLAG_CASE(IF_TYPE_BASIC_ISDN, "BASIC_ISDN")
77 FF_LOCALIP_FLAG_CASE(IF_TYPE_PRIMARY_ISDN, "PRIMARY_ISDN")
78 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_POINT2POINT_SERIAL, "PROP_POINT2POINT_SERIAL")
79 FF_LOCALIP_FLAG_CASE(IF_TYPE_PPP, "PPP")
80 FF_LOCALIP_FLAG_CASE(IF_TYPE_SOFTWARE_LOOPBACK, "SOFTWARE_LOOPBACK")
81 FF_LOCALIP_FLAG_CASE(IF_TYPE_EON, "EON")
82 FF_LOCALIP_FLAG_CASE(IF_TYPE_ETHERNET_3MBIT, "ETHERNET_3MBIT")
83 FF_LOCALIP_FLAG_CASE(IF_TYPE_NSIP, "NSIP")
84 FF_LOCALIP_FLAG_CASE(IF_TYPE_SLIP, "SLIP")
85 FF_LOCALIP_FLAG_CASE(IF_TYPE_ULTRA, "ULTRA")
86 FF_LOCALIP_FLAG_CASE(IF_TYPE_DS3, "DS3")
87 FF_LOCALIP_FLAG_CASE(IF_TYPE_SIP, "SIP")
88 FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY, "FRAMERELAY")
89 FF_LOCALIP_FLAG_CASE(IF_TYPE_RS232, "RS232")
90 FF_LOCALIP_FLAG_CASE(IF_TYPE_PARA, "PARA")
91 FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET, "ARCNET")
92 FF_LOCALIP_FLAG_CASE(IF_TYPE_ARCNET_PLUS, "ARCNET_PLUS")
93 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM, "ATM")
94 FF_LOCALIP_FLAG_CASE(IF_TYPE_MIO_X25, "MIO_X25")
95 FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET, "SONET")
96 FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_PLE, "X25_PLE")
97 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88022_LLC, "ISO88022_LLC")
98 FF_LOCALIP_FLAG_CASE(IF_TYPE_LOCALTALK, "LOCALTALK")
99 FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_DXI, "SMDS_DXI")
100 FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_SERVICE, "FRAMERELAY_SERVICE")
101 FF_LOCALIP_FLAG_CASE(IF_TYPE_V35, "V35")
102 FF_LOCALIP_FLAG_CASE(IF_TYPE_HSSI, "HSSI")
103 FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPI, "HIPPI")
104 FF_LOCALIP_FLAG_CASE(IF_TYPE_MODEM, "MODEM")
105 FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL5, "AAL5")
106 FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_PATH, "SONET_PATH")
107 FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_VT, "SONET_VT")
108 FF_LOCALIP_FLAG_CASE(IF_TYPE_SMDS_ICIP, "SMDS_ICIP")
109 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_VIRTUAL, "PROP_VIRTUAL")
110 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_MULTIPLEXOR, "PROP_MULTIPLEXOR")
111 FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80212, "IEEE80212")
112 FF_LOCALIP_FLAG_CASE(IF_TYPE_FIBRECHANNEL, "FIBRECHANNEL")
113 FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPPIINTERFACE, "HIPPIINTERFACE")
114 FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_INTERCONNECT, "FRAMERELAY_INTERCONNECT")
115 FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8023, "AFLANE_8023")
116 FF_LOCALIP_FLAG_CASE(IF_TYPE_AFLANE_8025, "AFLANE_8025")
117 FF_LOCALIP_FLAG_CASE(IF_TYPE_CCTEMUL, "CCTEMUL")
118 FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER, "FASTETHER")
119 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN, "ISDN")
120 FF_LOCALIP_FLAG_CASE(IF_TYPE_V11, "V11")
121 FF_LOCALIP_FLAG_CASE(IF_TYPE_V36, "V36")
122 FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_64K, "G703_64K")
123 FF_LOCALIP_FLAG_CASE(IF_TYPE_G703_2MB, "G703_2MB")
124 FF_LOCALIP_FLAG_CASE(IF_TYPE_QLLC, "QLLC")
125 FF_LOCALIP_FLAG_CASE(IF_TYPE_FASTETHER_FX, "FASTETHER_FX")
126 FF_LOCALIP_FLAG_CASE(IF_TYPE_CHANNEL, "CHANNEL")
127 FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80211, "IEEE80211")
128 FF_LOCALIP_FLAG_CASE(IF_TYPE_IBM370PARCHAN, "IBM370PARCHAN")
129 FF_LOCALIP_FLAG_CASE(IF_TYPE_ESCON, "ESCON")
130 FF_LOCALIP_FLAG_CASE(IF_TYPE_DLSW, "DLSW")
131 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_S, "ISDN_S")
132 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISDN_U, "ISDN_U")
133 FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_D, "LAP_D")
134 FF_LOCALIP_FLAG_CASE(IF_TYPE_IPSWITCH, "IPSWITCH")
135 FF_LOCALIP_FLAG_CASE(IF_TYPE_RSRB, "RSRB")
136 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_LOGICAL, "ATM_LOGICAL")
137 FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0, "DS0")
138 FF_LOCALIP_FLAG_CASE(IF_TYPE_DS0_BUNDLE, "DS0_BUNDLE")
139 FF_LOCALIP_FLAG_CASE(IF_TYPE_BSC, "BSC")
140 FF_LOCALIP_FLAG_CASE(IF_TYPE_ASYNC, "ASYNC")
141 FF_LOCALIP_FLAG_CASE(IF_TYPE_CNR, "CNR")
142 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025R_DTR, "ISO88025R_DTR")
143 FF_LOCALIP_FLAG_CASE(IF_TYPE_EPLRS, "EPLRS")
144 FF_LOCALIP_FLAG_CASE(IF_TYPE_ARAP, "ARAP")
145 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_CNLS, "PROP_CNLS")
146 FF_LOCALIP_FLAG_CASE(IF_TYPE_HOSTPAD, "HOSTPAD")
147 FF_LOCALIP_FLAG_CASE(IF_TYPE_TERMPAD, "TERMPAD")
148 FF_LOCALIP_FLAG_CASE(IF_TYPE_FRAMERELAY_MPI, "FRAMERELAY_MPI")
149 FF_LOCALIP_FLAG_CASE(IF_TYPE_X213, "X213")
150 FF_LOCALIP_FLAG_CASE(IF_TYPE_ADSL, "ADSL")
151 FF_LOCALIP_FLAG_CASE(IF_TYPE_RADSL, "RADSL")
152 FF_LOCALIP_FLAG_CASE(IF_TYPE_SDSL, "SDSL")
153 FF_LOCALIP_FLAG_CASE(IF_TYPE_VDSL, "VDSL")
154 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_CRFPRINT, "ISO88025_CRFPRINT")
155 FF_LOCALIP_FLAG_CASE(IF_TYPE_MYRINET, "MYRINET")
156 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_EM, "VOICE_EM")
157 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXO, "VOICE_FXO")
158 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_FXS, "VOICE_FXS")
159 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_ENCAP, "VOICE_ENCAP")
160 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICE_OVERIP, "VOICE_OVERIP")
161 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_DXI, "ATM_DXI")
162 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_FUNI, "ATM_FUNI")
163 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_IMA, "ATM_IMA")
164 FF_LOCALIP_FLAG_CASE(IF_TYPE_PPPMULTILINKBUNDLE, "PPPMULTILINKBUNDLE")
165 FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CDLC, "IPOVER_CDLC")
166 FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_CLAW, "IPOVER_CLAW")
167 FF_LOCALIP_FLAG_CASE(IF_TYPE_STACKTOSTACK, "STACKTOSTACK")
168 FF_LOCALIP_FLAG_CASE(IF_TYPE_VIRTUALIPADDRESS, "VIRTUALIPADDRESS")
169 FF_LOCALIP_FLAG_CASE(IF_TYPE_MPC, "MPC")
170 FF_LOCALIP_FLAG_CASE(IF_TYPE_IPOVER_ATM, "IPOVER_ATM")
171 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISO88025_FIBER, "ISO88025_FIBER")
172 FF_LOCALIP_FLAG_CASE(IF_TYPE_TDLC, "TDLC")
173 FF_LOCALIP_FLAG_CASE(IF_TYPE_GIGABITETHERNET, "GIGABITETHERNET")
174 FF_LOCALIP_FLAG_CASE(IF_TYPE_HDLC, "HDLC")
175 FF_LOCALIP_FLAG_CASE(IF_TYPE_LAP_F, "LAP_F")
176 FF_LOCALIP_FLAG_CASE(IF_TYPE_V37, "V37")
177 FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_MLP, "X25_MLP")
178 FF_LOCALIP_FLAG_CASE(IF_TYPE_X25_HUNTGROUP, "X25_HUNTGROUP")
179 FF_LOCALIP_FLAG_CASE(IF_TYPE_TRANSPHDLC, "TRANSPHDLC")
180 FF_LOCALIP_FLAG_CASE(IF_TYPE_INTERLEAVE, "INTERLEAVE")
181 FF_LOCALIP_FLAG_CASE(IF_TYPE_FAST, "FAST")
182 FF_LOCALIP_FLAG_CASE(IF_TYPE_IP, "IP")
183 FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_MACLAYER, "DOCSCABLE_MACLAYER")
184 FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_DOWNSTREAM, "DOCSCABLE_DOWNSTREAM")
185 FF_LOCALIP_FLAG_CASE(IF_TYPE_DOCSCABLE_UPSTREAM, "DOCSCABLE_UPSTREAM")
186 FF_LOCALIP_FLAG_CASE(IF_TYPE_A12MPPSWITCH, "A12MPPSWITCH")
187 FF_LOCALIP_FLAG_CASE(IF_TYPE_TUNNEL, "TUNNEL")
188 FF_LOCALIP_FLAG_CASE(IF_TYPE_COFFEE, "COFFEE")
189 FF_LOCALIP_FLAG_CASE(IF_TYPE_CES, "CES")
190 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_SUBINTERFACE, "ATM_SUBINTERFACE")
191 FF_LOCALIP_FLAG_CASE(IF_TYPE_L2_VLAN, "L2_VLAN")
192 FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPVLAN, "L3_IPVLAN")
193 FF_LOCALIP_FLAG_CASE(IF_TYPE_L3_IPXVLAN, "L3_IPXVLAN")
194 FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITALPOWERLINE, "DIGITALPOWERLINE")
195 FF_LOCALIP_FLAG_CASE(IF_TYPE_MEDIAMAILOVERIP, "MEDIAMAILOVERIP")
196 FF_LOCALIP_FLAG_CASE(IF_TYPE_DTM, "DTM")
197 FF_LOCALIP_FLAG_CASE(IF_TYPE_DCN, "DCN")
198 FF_LOCALIP_FLAG_CASE(IF_TYPE_IPFORWARD, "IPFORWARD")
199 FF_LOCALIP_FLAG_CASE(IF_TYPE_MSDSL, "MSDSL")
200 FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE1394, "IEEE1394")
201 FF_LOCALIP_FLAG_CASE(IF_TYPE_IF_GSN, "IF_GSN")
202 FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_MACLAYER, "DVBRCC_MACLAYER")
203 FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_DOWNSTREAM, "DVBRCC_DOWNSTREAM")
204 FF_LOCALIP_FLAG_CASE(IF_TYPE_DVBRCC_UPSTREAM, "DVBRCC_UPSTREAM")
205 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VIRTUAL, "ATM_VIRTUAL")
206 FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS_TUNNEL, "MPLS_TUNNEL")
207 FF_LOCALIP_FLAG_CASE(IF_TYPE_SRP, "SRP")
208 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERATM, "VOICEOVERATM")
209 FF_LOCALIP_FLAG_CASE(IF_TYPE_VOICEOVERFRAMERELAY, "VOICEOVERFRAMERELAY")
210 FF_LOCALIP_FLAG_CASE(IF_TYPE_IDSL, "IDSL")
211 FF_LOCALIP_FLAG_CASE(IF_TYPE_COMPOSITELINK, "COMPOSITELINK")
212 FF_LOCALIP_FLAG_CASE(IF_TYPE_SS7_SIGLINK, "SS7_SIGLINK")
213 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_WIRELESS_P2P, "PROP_WIRELESS_P2P")
214 FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_FORWARD, "FR_FORWARD")
215 FF_LOCALIP_FLAG_CASE(IF_TYPE_RFC1483, "RFC1483")
216 FF_LOCALIP_FLAG_CASE(IF_TYPE_USB, "USB")
217 FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE8023AD_LAG, "IEEE8023AD_LAG")
218 FF_LOCALIP_FLAG_CASE(IF_TYPE_BGP_POLICY_ACCOUNTING, "BGP_POLICY_ACCOUNTING")
219 FF_LOCALIP_FLAG_CASE(IF_TYPE_FRF16_MFR_BUNDLE, "FRF16_MFR_BUNDLE")
220 FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_GATEKEEPER, "H323_GATEKEEPER")
221 FF_LOCALIP_FLAG_CASE(IF_TYPE_H323_PROXY, "H323_PROXY")
222 FF_LOCALIP_FLAG_CASE(IF_TYPE_MPLS, "MPLS")
223 FF_LOCALIP_FLAG_CASE(IF_TYPE_MF_SIGLINK, "MF_SIGLINK")
224 FF_LOCALIP_FLAG_CASE(IF_TYPE_HDSL2, "HDSL2")
225 FF_LOCALIP_FLAG_CASE(IF_TYPE_SHDSL, "SHDSL")
226 FF_LOCALIP_FLAG_CASE(IF_TYPE_DS1_FDL, "DS1_FDL")
227 FF_LOCALIP_FLAG_CASE(IF_TYPE_POS, "POS")
228 FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_IN, "DVB_ASI_IN")
229 FF_LOCALIP_FLAG_CASE(IF_TYPE_DVB_ASI_OUT, "DVB_ASI_OUT")
230 FF_LOCALIP_FLAG_CASE(IF_TYPE_PLC, "PLC")
231 FF_LOCALIP_FLAG_CASE(IF_TYPE_NFAS, "NFAS")
232 FF_LOCALIP_FLAG_CASE(IF_TYPE_TR008, "TR008")
233 FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_RDT, "GR303_RDT")
234 FF_LOCALIP_FLAG_CASE(IF_TYPE_GR303_IDT, "GR303_IDT")
235 FF_LOCALIP_FLAG_CASE(IF_TYPE_ISUP, "ISUP")
236 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_MACLAYER, "PROP_DOCS_WIRELESS_MACLAYER")
237 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_DOWNSTREAM, "PROP_DOCS_WIRELESS_DOWNSTREAM")
238 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_DOCS_WIRELESS_UPSTREAM, "PROP_DOCS_WIRELESS_UPSTREAM")
239 FF_LOCALIP_FLAG_CASE(IF_TYPE_HIPERLAN2, "HIPERLAN2")
240 FF_LOCALIP_FLAG_CASE(IF_TYPE_PROP_BWA_P2MP, "PROP_BWA_P2MP")
241 FF_LOCALIP_FLAG_CASE(IF_TYPE_SONET_OVERHEAD_CHANNEL, "SONET_OVERHEAD_CHANNEL")
242 FF_LOCALIP_FLAG_CASE(IF_TYPE_DIGITAL_WRAPPER_OVERHEAD_CHANNEL, "DIGITAL_WRAPPER_OVERHEAD_CHANNEL")
243 FF_LOCALIP_FLAG_CASE(IF_TYPE_AAL2, "AAL2")
244 FF_LOCALIP_FLAG_CASE(IF_TYPE_RADIO_MAC, "RADIO_MAC")
245 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_RADIO, "ATM_RADIO")
246 FF_LOCALIP_FLAG_CASE(IF_TYPE_IMT, "IMT")
247 FF_LOCALIP_FLAG_CASE(IF_TYPE_MVL, "MVL")
248 FF_LOCALIP_FLAG_CASE(IF_TYPE_REACH_DSL, "REACH_DSL")
249 FF_LOCALIP_FLAG_CASE(IF_TYPE_FR_DLCI_ENDPT, "FR_DLCI_ENDPT")
250 FF_LOCALIP_FLAG_CASE(IF_TYPE_ATM_VCI_ENDPT, "ATM_VCI_ENDPT")
251 FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_CHANNEL, "OPTICAL_CHANNEL")
252 FF_LOCALIP_FLAG_CASE(IF_TYPE_OPTICAL_TRANSPORT, "OPTICAL_TRANSPORT")
253 FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE80216_WMAN, "IEEE80216_WMAN")
254 FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP, "WWANPP")
255 FF_LOCALIP_FLAG_CASE(IF_TYPE_WWANPP2, "WWANPP2")
256 FF_LOCALIP_FLAG_CASE(IF_TYPE_IEEE802154, "IEEE802154")
257 FF_LOCALIP_FLAG_CASE(IF_TYPE_XBOX_WIRELESS, "XBOX_WIRELESS")
258 }
259
260#undef FF_LOCALIP_FLAG_CASE
261}
262
263static bool isDefaultRouteInterface(const FFLocalIpOptions* options, NET_IFINDEX ifIndex) {
264 // Both route lookups are cached, so this is cheap even inside the per interface loop
265 return ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex) ||
266 ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
267}
268
269const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) {
270 FF_DEBUG("Starting local IP detection with showType=0x%X, namePrefix='%.*s'",
271 options->showType,
272 (int) options->namePrefix.length,
273 options->namePrefix.chars);
274
275 ADDRESS_FAMILY family = options->showType & FF_LOCALIP_TYPE_IPV4_BIT
276 ? options->showType & FF_LOCALIP_TYPE_IPV6_BIT ? AF_UNSPEC : AF_INET
277 : AF_INET6;
278 bool queryV4 = (options->showType & FF_LOCALIP_TYPE_IPV4_BIT) != 0;
279 bool queryV6 = (options->showType & FF_LOCALIP_TYPE_IPV6_BIT) != 0;
280
281 // Every interface is queried with GetIfEntry2() rather than by walking a table of all of them:
282 // GetIfTable2() costs about 0.95 ms on a machine with a few dozen adapters however few of them
283 // are reported, because it marshals every registered one, while GetIfEntry2() costs about 24 us
284 // per interface. With 52 adapters registered and 8 interfaces carrying an address the per
285 // interface API is 3.6 times faster overall; the table only wins past roughly 40 interfaces with
286 // an address, and with FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT (the default) at most one interface
287 // can pass the filter below anyway.
288 bool defaultRouteOnly = (options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) != 0;
289 if (defaultRouteOnly) {
290 if (queryV4) {
291 FF_DEBUG("Querying the IPv4 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV4()->ifIndex);
292 }
293 if (queryV6) {
294 FF_DEBUG("Querying the IPv6 default route interface only: ifIndex=%u", (unsigned) ffNetifGetDefaultRouteV6()->ifIndex);
295 }
296 }
297
298 PMIB_UNICASTIPADDRESS_TABLE table = nullptr;
299 const char* errorMessage = nullptr;
300
301 // The unicast address table is the only API that reports per interface addresses and it cannot
302 // be filtered by interface, so it is queried once and only the rows of the interfaces which pass
303 // the filter below are read
304 if (!NETIO_SUCCESS(GetUnicastIpAddressTable(family, &table))) {
305 FF_DEBUG("GetUnicastIpAddressTable failed");
306 errorMessage = "GetUnicastIpAddressTable() failed";
307 goto cleanup;
308 }
309
310 [[maybe_unused]] int adapterCount = 0, processedCount = 0;
311
312 // Rows of the same interface are not adjacent (IPv4 rows come first),
313 // so each interface is handled at its first row
314 for (ULONG i = 0; i < table->NumEntries; ++i) {
315 NET_IFINDEX ifIndex = table->Table[i].InterfaceIndex;
316
317 bool seen = false;
318 for (ULONG j = 0; j < i && !seen; ++j) {
319 seen = table->Table[j].InterfaceIndex == ifIndex;
320 }
321 if (seen) {
322 continue;
323 }
324
325 adapterCount++;
326
327 // Checked first to avoid querying interfaces we won't show
328 if (defaultRouteOnly && !isDefaultRouteInterface(options, ifIndex)) {
329 FF_DEBUG("Skipping interface %u (not default route interface)", (unsigned) ifIndex);
330 continue;
331 }
332
333 MIB_IF_ROW2 ifRow = { .InterfaceIndex = ifIndex };
334 if (!NETIO_SUCCESS(GetIfEntry2(&ifRow))) {
335 FF_DEBUG("GetIfEntry2(%u) failed", (unsigned) ifIndex);
336 continue;
337 }
338
339 FF_DEBUG("Processing adapter %d: IfIndex=%u, IfType=%u, OperStatus=%u",
340 adapterCount,
341 (unsigned) ifIndex,
342 (unsigned) ifRow.Type,
343 (unsigned) ifRow.OperStatus);
344
345 if (ifRow.OperStatus != IfOperStatusUp) {
346 FF_DEBUG("Skipping adapter %u (not operational, status=%d)", (unsigned) ifIndex, ifRow.OperStatus);
347 continue;
348 }
349
350 bool isLoop = ifRow.Type == IF_TYPE_SOFTWARE_LOOPBACK;
351 FF_DEBUG("Adapter %u: isLoopback=%s", (unsigned) ifIndex, isLoop ? "true" : "false");
352
353 if (isLoop && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) {
354 FF_DEBUG("Skipping loopback adapter %u (loopback not requested)", (unsigned) ifIndex);
355 continue;
356 }
357
358 FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(ifRow.Alias);
359 FF_DEBUG("Adapter %u name: '%s'", (unsigned) ifIndex, name.chars);
360
361 if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) {
362 FF_DEBUG("Skipping adapter %u (name doesn't match prefix '%.*s')",
363 (unsigned) ifIndex,
364 (int) options->namePrefix.length,
365 options->namePrefix.chars);
366 continue;
367 }
368
369 processedCount++;
370 FF_DEBUG("Creating result item for adapter %u ('%s')", (unsigned) ifIndex, name.chars);
371
372 FFLocalIpResult* item = FF_LIST_ADD(FFLocalIpResult, *results);
373 ffStrbufInitMove(&item->name, &name);
374 ffStrbufInit(&item->ipv4);
375 ffStrbufInit(&item->ipv6);
376 ffStrbufInit(&item->mac);
377 ffStrbufInit(&item->flags);
378 item->defaultRoute = FF_LOCALIP_TYPE_NONE;
379 item->speed = -1;
380 item->mtu = -1;
381
382 if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT) {
383 fillInterfaceFlags(&item->flags, &ifRow);
384 FF_DEBUG("Adapter %u flags: '%s'", (unsigned) ifIndex, item->flags.chars);
385 }
386
387 uint32_t typesToAdd = options->showType & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT);
388 FF_DEBUG("Types to add for adapter %u: 0x%X", (unsigned) ifIndex, typesToAdd);
389
390 [[maybe_unused]] int ipv4Count = 0, ipv6Count = 0;
391
392 for (ULONG j = i; j < table->NumEntries; ++j) {
393 MIB_UNICASTIPADDRESS_ROW* ifa = &table->Table[j];
394 if (ifa->InterfaceIndex != ifIndex) {
395 continue;
396 }
397
398 FF_DEBUG("Processing unicast address: prefix origin=%d, suffix origin=%d, family=%d, DadState=%d",
399 ifa->PrefixOrigin,
400 ifa->SuffixOrigin,
401 ifa->Address.si_family,
402 ifa->DadState);
403
404 if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)) {
405 if (ifa->DadState != IpDadStatePreferred) {
406 FF_DEBUG("Skipping address (not preferred)");
407 continue;
408 }
409
410 if (ifa->SuffixOrigin == IpSuffixOriginRandom) {
411 FF_DEBUG("Skipping temporary address (random suffix)");
412 continue;
413 }
414
415 // MIB_UNICASTIPADDRESS_ROW::SkipAsSource
416 }
417
418 if (ifa->Address.si_family == AF_INET) {
419 if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV4_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
420 FF_DEBUG("Skipping IPv4 address (not requested in typesToAdd=0x%X)", typesToAdd);
421 continue;
422 }
423
424 bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV4_BIT) && ffNetifGetDefaultRouteV4()->ifIndex == ifIndex);
425 if (defaultRouteOnly && !isDefaultRoute) {
426 FF_DEBUG("Skipping IPv4 address (not on default route interface)");
427 continue;
428 }
429
430 SOCKADDR_IN* ipv4 = &ifa->Address.Ipv4;
431 char addressBuffer[INET_ADDRSTRLEN + 10];
432 char* end = RtlIpv4AddressToStringA(&ipv4->sin_addr, addressBuffer);
433
434 if ((options->showType & FF_LOCALIP_TYPE_PREFIX_LEN_BIT) && ifa->OnLinkPrefixLength) {
435 end += snprintf(end, 10, "/%u", (unsigned) ifa->OnLinkPrefixLength);
436 }
437
438 FF_DEBUG("Adding IPv4 address: %s (isDefaultRoute=%s)", addressBuffer, isDefaultRoute ? "true" : "false");
439
440 if (item->ipv4.length) {
441 ffStrbufAppendC(&item->ipv4, ',');
442 }
443 ffStrbufAppendNS(&item->ipv4, (uint32_t) (end - addressBuffer), addressBuffer);
444 if (isDefaultRoute) {
445 item->defaultRoute |= FF_LOCALIP_TYPE_IPV4_BIT;
446 }
447
448 ipv4Count++;
449 typesToAdd &= ~(unsigned) FF_LOCALIP_TYPE_IPV4_BIT;
450 if (typesToAdd == 0) {
451 break;
452 }
453 } else if (ifa->Address.si_family == AF_INET6) {
454 if (!(typesToAdd & (FF_LOCALIP_TYPE_IPV6_BIT | FF_LOCALIP_TYPE_ALL_IPS_BIT))) {
455 FF_DEBUG("Skipping IPv6 address (not requested in typesToAdd=0x%X)", typesToAdd);
456 continue;
457 }
458
459 SOCKADDR_IN6* ipv6 = &ifa->Address.Ipv6;
460
461 FFLocalIpIpv6Type ipv6Type = FF_LOCALIP_IPV6_TYPE_NONE;
462 if (IN6_IS_ADDR_GLOBAL(&ipv6->sin6_addr)) {
463 ipv6Type |= FF_LOCALIP_IPV6_TYPE_GUA_BIT;
464 } else if (IN6_IS_ADDR_UNIQUE_LOCAL(&ipv6->sin6_addr)) {
465 ipv6Type |= FF_LOCALIP_IPV6_TYPE_ULA_BIT;
466 } else if (IN6_IS_ADDR_LINKLOCAL(&ipv6->sin6_addr)) {
467 ipv6Type |= FF_LOCALIP_IPV6_TYPE_LLA_BIT;
468 } else {
469 ipv6Type |= FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT;
470 }
471
472 if (!(options->ipv6Type & ipv6Type)) {
473 FF_DEBUG("Skipping IPv6 address (doesn't match requested type 0x%X)", options->ipv6Type);
474 continue;
475 }
476
477 bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == ifIndex);
478 if (defaultRouteOnly && !isDefaultRoute) {
479 FF_DEBUG("Skipping IPv6 address (not on default route interface)");
480 continue;
481 }
482
483 char addressBuffer[INET6_ADDRSTRLEN + 10];
484 char* end = RtlIpv6AddressToStringA(&ipv6->sin6_addr, addressBuffer);
485
486 if ((options->showType & FF_LOCALIP_TYPE_PREFIX_LEN_BIT) && ifa->OnLinkPrefixLength) {
487 end += snprintf(end, 10, "/%u", (unsigned) ifa->OnLinkPrefixLength);
488 }
489
490 FF_DEBUG("Adding IPv6 address: %s (isDefaultRoute=%s)", addressBuffer, isDefaultRoute ? "true" : "false");
491
492 if (item->ipv6.length) {
493 ffStrbufAppendC(&item->ipv6, ',');
494 }
495 ffStrbufAppendNS(&item->ipv6, (uint32_t) (end - addressBuffer), addressBuffer);
496 if (isDefaultRoute) {
497 item->defaultRoute |= FF_LOCALIP_TYPE_IPV6_BIT;
498 }
499
500 ipv6Count++;
501 typesToAdd &= ~(unsigned) FF_LOCALIP_TYPE_IPV6_BIT;
502 if (typesToAdd == 0) {
503 break;
504 }
505 }
506 }
507
508 FF_DEBUG("Adapter %u: collected %d IPv4 and %d IPv6 addresses", (unsigned) ifIndex, ipv4Count, ipv6Count);
509
510 if (options->showType & FF_LOCALIP_TYPE_SPEED_BIT) {
511 item->speed = (int32_t) (ifRow.ReceiveLinkSpeed / 1000000);
512 FF_DEBUG("Adapter %u speed: %d Mbps (raw: %llu)", (unsigned) ifIndex, item->speed, ifRow.ReceiveLinkSpeed);
513 }
514 if (options->showType & FF_LOCALIP_TYPE_MTU_BIT) {
515 item->mtu = (int32_t) ifRow.Mtu;
516 FF_DEBUG("Adapter %u MTU: %d", (unsigned) ifIndex, item->mtu);
517 }
518 if (options->showType & FF_LOCALIP_TYPE_MAC_BIT && ifRow.PhysicalAddressLength == 6) {
519 const uint8_t* ptr = ifRow.PhysicalAddress;
520 ffStrbufSetF(&item->mac, "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
521 FF_DEBUG("Adapter %u MAC: %s", (unsigned) ifIndex, item->mac.chars);
522 }
523 }
524
525 FF_DEBUG("Local IP detection completed: scanned %d adapters, processed %d, results count: %u",
526 adapterCount,
527 processedCount,
528 results->length);
529
530cleanup:
531 if (table) {
532 FreeMibTable(table);
533 }
534 return errorMessage;
535}