Skip to content

Commit 1abfa70

Browse files
committed
LocalIP: adds IPv6 type selection for local IP display
`{ "type": "localip", "showIpv6": "ula" /* Show ULA only */ }` Fixes #1459
1 parent baaeef5 commit 1abfa70

File tree

6 files changed

+189
-70
lines changed

6 files changed

+189
-70
lines changed

doc/json_schema.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2920,7 +2920,32 @@
29202920
},
29212921
"showIpv6": {
29222922
"description": "Show IPv6 addresses",
2923-
"type": "boolean",
2923+
"oneOf": [
2924+
{
2925+
"const": true,
2926+
"description": "Show the most useful IPv6 addresses"
2927+
},
2928+
{
2929+
"const": false,
2930+
"description": "Do not show IPv6 addresses"
2931+
},
2932+
{
2933+
"const": "gua",
2934+
"description": "Show only global unicast IPv6 addresses (2000::/3)"
2935+
},
2936+
{
2937+
"const": "ula",
2938+
"description": "Show only unique local IPv6 addresses (fc00::/7)"
2939+
},
2940+
{
2941+
"const": "lla",
2942+
"description": "Show only link-local IPv6 addresses (fe80::/10)"
2943+
},
2944+
{
2945+
"const": "unknown",
2946+
"description": "Show only IPv6 addresses that are not gua, ula or lla"
2947+
}
2948+
],
29242949
"default": false
29252950
},
29262951
"showSpeed": {

src/detection/localip/localip.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@
33
#include "fastfetch.h"
44
#include "modules/localip/option.h"
55

6+
#ifndef IN6_IS_ADDR_GLOBAL
7+
#define IN6_IS_ADDR_GLOBAL(a) \
8+
((((const uint32_t *) (a))[0] & htonl(0x70000000)) == htonl(0x20000000))
9+
#endif
10+
#ifndef IN6_IS_ADDR_UNIQUE_LOCAL
11+
#define IN6_IS_ADDR_UNIQUE_LOCAL(a) \
12+
((((const uint32_t *) (a))[0] & htonl(0xfe000000)) == htonl(0xfc000000))
13+
#endif
14+
#ifndef IN6_IS_ADDR_LINKLOCAL
15+
#define IN6_IS_ADDR_LINKLOCAL(a) \
16+
((((const uint32_t *) (a))[0] & htonl(0xffc00000)) == htonl(0xfe800000))
17+
#endif
18+
619
typedef struct FFLocalIpResult
720
{
821
FFstrbuf name;

src/detection/localip/localip_linux.c

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -111,47 +111,32 @@ static const FFLocalIpNIFlag niFlagOptions[] = {
111111
{},
112112
};
113113

114-
typedef enum __attribute__((__packed__)) FFIPv6Type
115-
{
116-
FF_IPV6_Other,
117-
FF_IPV6_GUA = 0b0001,
118-
FF_IPV6_GUA_SECONDARY = 0b0101,
119-
FF_IPV6_ULA = 0b0010,
120-
FF_IPV6_ULA_SECONDARY = 0b0110,
121-
FF_IPV6_TYPE_MASK = 0b0011,
122-
FF_IPV6_SECONDARY_FLAG = 0b1100,
123-
FF_IPV6_PREFERRED = UINT8_MAX,
124-
} FFIPv6Type;
125-
126-
static FFIPv6Type getIpType(struct ifaddrs* ifa)
114+
static FFLocalIpIpv6Type getIpv6Type(struct ifaddrs* ifa)
127115
{
128116
struct sockaddr_in6* addr = (struct sockaddr_in6*) ifa->ifa_addr;
129117

130118
FF_DEBUG("Checking IPv6 type for interface %s", ifa->ifa_name);
131119

132-
#ifndef IN6_IS_ADDR_GLOBAL
133-
#define IN6_IS_ADDR_GLOBAL(a) \
134-
((((const uint32_t *) (a))[0] & htonl(0x70000000)) == htonl(0x20000000))
135-
#endif
136-
#ifndef IN6_IS_ADDR_UNIQUE_LOCAL
137-
#define IN6_IS_ADDR_UNIQUE_LOCAL(a) \
138-
((((const uint32_t *) (a))[0] & htonl(0xfe000000)) == htonl(0xfc000000))
139-
#endif
140-
FFIPv6Type result = FF_IPV6_Other;
120+
FFLocalIpIpv6Type result = FF_LOCALIP_IPV6_TYPE_NONE;
141121
if (IN6_IS_ADDR_GLOBAL(&addr->sin6_addr))
142122
{
143-
result = FF_IPV6_GUA;
123+
result = FF_LOCALIP_IPV6_TYPE_GUA_BIT;
144124
FF_DEBUG("Interface %s has Global Unicast Address", ifa->ifa_name);
145125
}
146126
else if (IN6_IS_ADDR_UNIQUE_LOCAL(&addr->sin6_addr))
147127
{
148-
result = FF_IPV6_ULA;
128+
result = FF_LOCALIP_IPV6_TYPE_ULA_BIT;
149129
FF_DEBUG("Interface %s has Unique Local Address", ifa->ifa_name);
150130
}
131+
else if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))
132+
{
133+
result = FF_LOCALIP_IPV6_TYPE_LLA_BIT;
134+
FF_DEBUG("Interface %s has Link-Local Address", ifa->ifa_name);
135+
}
151136
else
152137
{
153-
FF_DEBUG("Interface %s has other IPv6 address type", ifa->ifa_name);
154-
return FF_IPV6_Other;
138+
FF_DEBUG("Interface %s has unknown IPv6 address type", ifa->ifa_name);
139+
return FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT;
155140
}
156141

157142
#ifdef SIOCGIFAFLAG_IN6
@@ -178,13 +163,13 @@ static FFIPv6Type getIpType(struct ifaddrs* ifa)
178163

179164
#ifdef IN6_IFF_PREFER_SOURCE
180165
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_PREFER_SOURCE)
181-
return FF_IPV6_PREFERRED;
166+
result |= FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT;
182167
#endif
183168
if (ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY | IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED
184169
#ifdef IN6_IFF_OPTIMISTIC
185170
| IN6_IFF_OPTIMISTIC
186171
#endif
187-
)) result |= FF_IPV6_SECONDARY_FLAG;
172+
)) result |= FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT;
188173
return result;
189174
#elif __linux__
190175
static FFlist addresses = {};
@@ -220,13 +205,13 @@ static FFIPv6Type getIpType(struct ifaddrs* ifa)
220205
if (memcmp(&addr->sin6_addr, entry, sizeof(struct in6_addr)) == 0)
221206
return result;
222207
}
223-
result |= FF_IPV6_SECONDARY_FLAG;
208+
result |= FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT;
224209
return result;
225210
#elif __sun
226211
if (ifa->ifa_flags & IFF_PREFERRED)
227-
return FF_IPV6_PREFERRED;
212+
result |= FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT;
228213
if (ifa->ifa_flags & (IFF_DEPRECATED | IFF_TEMPORARY | IFF_DUPLICATE))
229-
result |= FF_IPV6_SECONDARY_FLAG;
214+
result |= FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT;
230215
return result;
231216
#else
232217
return result;
@@ -509,46 +494,65 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
509494
}
510495
}
511496

512-
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
497+
if (options->ipv6Type == FF_LOCALIP_IPV6_TYPE_AUTO)
513498
{
514-
struct ifaddrs* selected = NULL;
515-
struct ifaddrs* secondary = NULL;
516-
517-
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
499+
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
518500
{
519-
FFIPv6Type type = getIpType(*pifa);
520-
if (type == FF_IPV6_PREFERRED)
521-
{
522-
selected = *pifa;
523-
FF_DEBUG("Found preferred IPv6 address for interface %s", adapter->mac->ifa_name);
524-
break;
525-
}
526-
else if (type == FF_IPV6_GUA && !selected)
501+
struct ifaddrs* selected = NULL;
502+
struct ifaddrs* secondary = NULL;
503+
504+
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
527505
{
528-
selected = *pifa;
529-
FF_DEBUG("Found GUA IPv6 address for interface %s", adapter->mac->ifa_name);
506+
FFLocalIpIpv6Type type = getIpv6Type(*pifa);
507+
if (type & FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT)
508+
{
509+
selected = *pifa;
510+
FF_DEBUG("Found preferred IPv6 address for interface %s", adapter->mac->ifa_name);
511+
break;
512+
}
513+
else if ((type & FF_LOCALIP_IPV6_TYPE_GUA_BIT) && !(type & FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT) && !selected)
514+
{
515+
selected = *pifa;
516+
FF_DEBUG("Found GUA IPv6 address for interface %s", adapter->mac->ifa_name);
517+
}
518+
else if ((type & FF_LOCALIP_IPV6_TYPE_ULA_BIT) && !(type & FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT) && !secondary)
519+
{
520+
secondary = *pifa;
521+
FF_DEBUG("Found ULA IPv6 address for interface %s", adapter->mac->ifa_name);
522+
}
530523
}
531-
else if (type == FF_IPV6_ULA && !secondary)
524+
if (!selected) selected = secondary;
525+
526+
if (selected)
527+
appendIpv6(options, &item->ipv6, selected);
528+
else if (adapter->ipv6.length > 0)
532529
{
533-
secondary = *pifa;
534-
FF_DEBUG("Found ULA IPv6 address for interface %s", adapter->mac->ifa_name);
530+
appendIpv6(options, &item->ipv6, *FF_LIST_GET(struct ifaddrs*, adapter->ipv6, 0));
531+
FF_DEBUG("Using first IPv6 address for interface %s", adapter->mac->ifa_name);
535532
}
536533
}
537-
if (!selected) selected = secondary;
538-
539-
if (selected)
540-
appendIpv6(options, &item->ipv6, selected);
541-
else if (adapter->ipv6.length > 0)
534+
else
542535
{
543-
appendIpv6(options, &item->ipv6, *FF_LIST_GET(struct ifaddrs*, adapter->ipv6, 0));
544-
FF_DEBUG("Using first IPv6 address for interface %s", adapter->mac->ifa_name);
536+
FF_DEBUG("Adding all IPv6 addresses for interface %s", adapter->mac->ifa_name);
537+
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
538+
appendIpv6(options, &item->ipv6, *pifa);
545539
}
546540
}
547541
else
548542
{
549-
FF_DEBUG("Adding all IPv6 addresses for interface %s", adapter->mac->ifa_name);
550543
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
551-
appendIpv6(options, &item->ipv6, *pifa);
544+
{
545+
FFLocalIpIpv6Type type = getIpv6Type(*pifa);
546+
if (type & options->ipv6Type)
547+
{
548+
if ((options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT) || !(type & FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT))
549+
{
550+
appendIpv6(options, &item->ipv6, *pifa);
551+
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
552+
break;
553+
}
554+
}
555+
}
552556
}
553557
}
554558
mac:

src/detection/localip/localip_windows.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,24 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
140140

141141
for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next)
142142
{
143-
FF_DEBUG("Processing unicast address: family=%d, DadState=%d",
144-
ifa->Address.lpSockaddr->sa_family, ifa->DadState);
143+
FF_DEBUG("Processing unicast address: prefix origin=%d, suffix origin=%d, family=%d, DadState=%d",
144+
ifa->PrefixOrigin, ifa->SuffixOrigin, ifa->Address.lpSockaddr->sa_family, ifa->DadState);
145145

146-
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT) && ifa->DadState != IpDadStatePreferred)
146+
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
147147
{
148-
FF_DEBUG("Skipping address (DadState=%d, not preferred)", ifa->DadState);
149-
continue;
148+
if (ifa->DadState != IpDadStatePreferred)
149+
{
150+
FF_DEBUG("Skipping address (not preferred)");
151+
continue;
152+
}
153+
154+
if (ifa->SuffixOrigin == IpSuffixOriginRandom)
155+
{
156+
FF_DEBUG("Skipping temporary address (random suffix)");
157+
continue;
158+
}
159+
160+
// MIB_UNICASTIPADDRESS_ROW::SkipAsSource
150161
}
151162

152163
if (ifa->Address.lpSockaddr->sa_family == AF_INET)
@@ -192,14 +203,27 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
192203
continue;
193204
}
194205

206+
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
207+
208+
FFLocalIpIpv6Type ipv6Type = FF_LOCALIP_IPV6_TYPE_NONE;
209+
if (IN6_IS_ADDR_GLOBAL(&ipv6->sin6_addr)) ipv6Type |= FF_LOCALIP_IPV6_TYPE_GUA_BIT;
210+
else if (IN6_IS_ADDR_UNIQUE_LOCAL(&ipv6->sin6_addr)) ipv6Type |= FF_LOCALIP_IPV6_TYPE_ULA_BIT;
211+
else if (IN6_IS_ADDR_LINKLOCAL(&ipv6->sin6_addr)) ipv6Type |= FF_LOCALIP_IPV6_TYPE_LLA_BIT;
212+
else ipv6Type |= FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT;
213+
214+
if (!(options->ipv6Type & ipv6Type))
215+
{
216+
FF_DEBUG("Skipping IPv6 address (doesn't match requested type 0x%X)", options->ipv6Type);
217+
continue;
218+
}
219+
195220
bool isDefaultRoute = ((options->showType & FF_LOCALIP_TYPE_IPV6_BIT) && ffNetifGetDefaultRouteV6()->ifIndex == adapter->IfIndex);
196221
if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute)
197222
{
198223
FF_DEBUG("Skipping IPv6 address (not on default route interface)");
199224
continue;
200225
}
201226

202-
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
203227
char addressBuffer[INET6_ADDRSTRLEN + 6];
204228
inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN);
205229

src/modules/localip/localip.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,33 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module)
198198

199199
if (unsafe_yyjson_equals_str(key, "showIpv6"))
200200
{
201-
if (yyjson_get_bool(val))
202-
options->showType |= FF_LOCALIP_TYPE_IPV6_BIT;
203-
else
204-
options->showType &= ~FF_LOCALIP_TYPE_IPV6_BIT;
201+
if (yyjson_is_bool(val))
202+
{
203+
options->ipv6Type = FF_LOCALIP_IPV6_TYPE_AUTO;
204+
if (unsafe_yyjson_get_bool(val))
205+
options->showType |= FF_LOCALIP_TYPE_IPV6_BIT;
206+
else
207+
options->showType &= ~FF_LOCALIP_TYPE_IPV6_BIT;
208+
}
209+
else if (yyjson_is_str(val))
210+
{
211+
int value;
212+
const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
213+
{ "auto", FF_LOCALIP_IPV6_TYPE_AUTO },
214+
{ "gua", FF_LOCALIP_IPV6_TYPE_GUA_BIT },
215+
{ "ula", FF_LOCALIP_IPV6_TYPE_ULA_BIT },
216+
{ "lla", FF_LOCALIP_IPV6_TYPE_LLA_BIT },
217+
{ "unknown", FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT },
218+
{},
219+
});
220+
if (error)
221+
ffPrintError(FF_LOCALIP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
222+
else
223+
{
224+
options->showType |= FF_LOCALIP_TYPE_IPV6_BIT;
225+
options->ipv6Type = (FFLocalIpIpv6Type) value;
226+
}
227+
}
205228
continue;
206229
}
207230

@@ -302,7 +325,21 @@ void ffGenerateLocalIpJsonConfig(FFLocalIpOptions* options, yyjson_mut_doc* doc,
302325

303326
yyjson_mut_obj_add_bool(doc, module, "showIpv4", !!(options->showType & FF_LOCALIP_TYPE_IPV4_BIT));
304327

305-
yyjson_mut_obj_add_bool(doc, module, "showIpv6", !!(options->showType & FF_LOCALIP_TYPE_IPV6_BIT));
328+
if (options->ipv6Type == FF_LOCALIP_IPV6_TYPE_AUTO)
329+
yyjson_mut_obj_add_bool(doc, module, "showIpv6", !!(options->showType & FF_LOCALIP_TYPE_IPV6_BIT));
330+
else
331+
{
332+
const char* str = NULL;
333+
switch (options->ipv6Type)
334+
{
335+
case FF_LOCALIP_IPV6_TYPE_GUA_BIT: str = "gua"; break;
336+
case FF_LOCALIP_IPV6_TYPE_ULA_BIT: str = "ula"; break;
337+
case FF_LOCALIP_IPV6_TYPE_LLA_BIT: str = "lla"; break;
338+
case FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT: str = "unknown"; break;
339+
default: str = "auto"; break;
340+
}
341+
yyjson_mut_obj_add_str(doc, module, "showIpv6", str);
342+
}
306343

307344
yyjson_mut_obj_add_bool(doc, module, "showMac", !!(options->showType & FF_LOCALIP_TYPE_MAC_BIT));
308345

@@ -385,6 +422,7 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options)
385422
| FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT
386423
#endif
387424
;
425+
options->ipv6Type = FF_LOCALIP_IPV6_TYPE_AUTO;
388426
ffStrbufInit(&options->namePrefix);
389427
}
390428

src/modules/localip/option.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,26 @@ typedef enum __attribute__((__packed__)) FFLocalIpType
2121
} FFLocalIpType;
2222
static_assert(sizeof(FFLocalIpType) == sizeof(uint16_t), "");
2323

24+
typedef enum __attribute__((__packed__)) FFLocalIpIpv6Type
25+
{
26+
FF_LOCALIP_IPV6_TYPE_NONE = 0b00000000,
27+
FF_LOCALIP_IPV6_TYPE_GUA_BIT = 0b00000001,
28+
FF_LOCALIP_IPV6_TYPE_ULA_BIT = 0b00000010,
29+
FF_LOCALIP_IPV6_TYPE_LLA_BIT = 0b00000100,
30+
FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT = 0b00001000, // IPv4-mapped, loopback, etc.
31+
FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT = 0b01000000, // Temporary, duplicated, etc.
32+
FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT = 0b10000000, // PREFER_SOURCE (manually set)
33+
FF_LOCALIP_IPV6_TYPE_TYPE_MASK = 0b00011111,
34+
FF_LOCALIP_IPV6_TYPE_AUTO = 0b11111111, // Used for detect option
35+
} FFLocalIpIpv6Type;
36+
static_assert(sizeof(FFLocalIpIpv6Type) == sizeof(uint8_t), "");
37+
2438
typedef struct FFLocalIpOptions
2539
{
2640
FFModuleArgs moduleArgs;
2741

2842
FFLocalIpType showType;
43+
FFLocalIpIpv6Type ipv6Type;
2944
FFstrbuf namePrefix;
3045
} FFLocalIpOptions;
3146

0 commit comments

Comments
 (0)