LocalIP: adds IPv6 type selection for local IP display

`{ "type": "localip", "showIpv6": "ula" /* Show ULA only */ }`

Fixes #1459
This commit is contained in:
李通洲
2025-09-10 10:33:38 +08:00
parent baaeef5ab5
commit 1abfa70237
6 changed files with 189 additions and 70 deletions
+13
View File
@@ -3,6 +3,19 @@
#include "fastfetch.h"
#include "modules/localip/option.h"
#ifndef IN6_IS_ADDR_GLOBAL
#define IN6_IS_ADDR_GLOBAL(a) \
((((const uint32_t *) (a))[0] & htonl(0x70000000)) == htonl(0x20000000))
#endif
#ifndef IN6_IS_ADDR_UNIQUE_LOCAL
#define IN6_IS_ADDR_UNIQUE_LOCAL(a) \
((((const uint32_t *) (a))[0] & htonl(0xfe000000)) == htonl(0xfc000000))
#endif
#ifndef IN6_IS_ADDR_LINKLOCAL
#define IN6_IS_ADDR_LINKLOCAL(a) \
((((const uint32_t *) (a))[0] & htonl(0xffc00000)) == htonl(0xfe800000))
#endif
typedef struct FFLocalIpResult
{
FFstrbuf name;
+62 -58
View File
@@ -111,47 +111,32 @@ static const FFLocalIpNIFlag niFlagOptions[] = {
{},
};
typedef enum __attribute__((__packed__)) FFIPv6Type
{
FF_IPV6_Other,
FF_IPV6_GUA = 0b0001,
FF_IPV6_GUA_SECONDARY = 0b0101,
FF_IPV6_ULA = 0b0010,
FF_IPV6_ULA_SECONDARY = 0b0110,
FF_IPV6_TYPE_MASK = 0b0011,
FF_IPV6_SECONDARY_FLAG = 0b1100,
FF_IPV6_PREFERRED = UINT8_MAX,
} FFIPv6Type;
static FFIPv6Type getIpType(struct ifaddrs* ifa)
static FFLocalIpIpv6Type getIpv6Type(struct ifaddrs* ifa)
{
struct sockaddr_in6* addr = (struct sockaddr_in6*) ifa->ifa_addr;
FF_DEBUG("Checking IPv6 type for interface %s", ifa->ifa_name);
#ifndef IN6_IS_ADDR_GLOBAL
#define IN6_IS_ADDR_GLOBAL(a) \
((((const uint32_t *) (a))[0] & htonl(0x70000000)) == htonl(0x20000000))
#endif
#ifndef IN6_IS_ADDR_UNIQUE_LOCAL
#define IN6_IS_ADDR_UNIQUE_LOCAL(a) \
((((const uint32_t *) (a))[0] & htonl(0xfe000000)) == htonl(0xfc000000))
#endif
FFIPv6Type result = FF_IPV6_Other;
FFLocalIpIpv6Type result = FF_LOCALIP_IPV6_TYPE_NONE;
if (IN6_IS_ADDR_GLOBAL(&addr->sin6_addr))
{
result = FF_IPV6_GUA;
result = FF_LOCALIP_IPV6_TYPE_GUA_BIT;
FF_DEBUG("Interface %s has Global Unicast Address", ifa->ifa_name);
}
else if (IN6_IS_ADDR_UNIQUE_LOCAL(&addr->sin6_addr))
{
result = FF_IPV6_ULA;
result = FF_LOCALIP_IPV6_TYPE_ULA_BIT;
FF_DEBUG("Interface %s has Unique Local Address", ifa->ifa_name);
}
else if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))
{
result = FF_LOCALIP_IPV6_TYPE_LLA_BIT;
FF_DEBUG("Interface %s has Link-Local Address", ifa->ifa_name);
}
else
{
FF_DEBUG("Interface %s has other IPv6 address type", ifa->ifa_name);
return FF_IPV6_Other;
FF_DEBUG("Interface %s has unknown IPv6 address type", ifa->ifa_name);
return FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT;
}
#ifdef SIOCGIFAFLAG_IN6
@@ -178,13 +163,13 @@ static FFIPv6Type getIpType(struct ifaddrs* ifa)
#ifdef IN6_IFF_PREFER_SOURCE
if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_PREFER_SOURCE)
return FF_IPV6_PREFERRED;
result |= FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT;
#endif
if (ifr6.ifr_ifru.ifru_flags6 & (IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY | IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED
#ifdef IN6_IFF_OPTIMISTIC
| IN6_IFF_OPTIMISTIC
#endif
)) result |= FF_IPV6_SECONDARY_FLAG;
)) result |= FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT;
return result;
#elif __linux__
static FFlist addresses = {};
@@ -220,13 +205,13 @@ static FFIPv6Type getIpType(struct ifaddrs* ifa)
if (memcmp(&addr->sin6_addr, entry, sizeof(struct in6_addr)) == 0)
return result;
}
result |= FF_IPV6_SECONDARY_FLAG;
result |= FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT;
return result;
#elif __sun
if (ifa->ifa_flags & IFF_PREFERRED)
return FF_IPV6_PREFERRED;
result |= FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT;
if (ifa->ifa_flags & (IFF_DEPRECATED | IFF_TEMPORARY | IFF_DUPLICATE))
result |= FF_IPV6_SECONDARY_FLAG;
result |= FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT;
return result;
#else
return result;
@@ -509,46 +494,65 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
}
}
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
if (options->ipv6Type == FF_LOCALIP_IPV6_TYPE_AUTO)
{
struct ifaddrs* selected = NULL;
struct ifaddrs* secondary = NULL;
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
{
FFIPv6Type type = getIpType(*pifa);
if (type == FF_IPV6_PREFERRED)
struct ifaddrs* selected = NULL;
struct ifaddrs* secondary = NULL;
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
{
selected = *pifa;
FF_DEBUG("Found preferred IPv6 address for interface %s", adapter->mac->ifa_name);
break;
FFLocalIpIpv6Type type = getIpv6Type(*pifa);
if (type & FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT)
{
selected = *pifa;
FF_DEBUG("Found preferred IPv6 address for interface %s", adapter->mac->ifa_name);
break;
}
else if ((type & FF_LOCALIP_IPV6_TYPE_GUA_BIT) && !(type & FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT) && !selected)
{
selected = *pifa;
FF_DEBUG("Found GUA IPv6 address for interface %s", adapter->mac->ifa_name);
}
else if ((type & FF_LOCALIP_IPV6_TYPE_ULA_BIT) && !(type & FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT) && !secondary)
{
secondary = *pifa;
FF_DEBUG("Found ULA IPv6 address for interface %s", adapter->mac->ifa_name);
}
}
else if (type == FF_IPV6_GUA && !selected)
if (!selected) selected = secondary;
if (selected)
appendIpv6(options, &item->ipv6, selected);
else if (adapter->ipv6.length > 0)
{
selected = *pifa;
FF_DEBUG("Found GUA IPv6 address for interface %s", adapter->mac->ifa_name);
}
else if (type == FF_IPV6_ULA && !secondary)
{
secondary = *pifa;
FF_DEBUG("Found ULA IPv6 address for interface %s", adapter->mac->ifa_name);
appendIpv6(options, &item->ipv6, *FF_LIST_GET(struct ifaddrs*, adapter->ipv6, 0));
FF_DEBUG("Using first IPv6 address for interface %s", adapter->mac->ifa_name);
}
}
if (!selected) selected = secondary;
if (selected)
appendIpv6(options, &item->ipv6, selected);
else if (adapter->ipv6.length > 0)
else
{
appendIpv6(options, &item->ipv6, *FF_LIST_GET(struct ifaddrs*, adapter->ipv6, 0));
FF_DEBUG("Using first IPv6 address for interface %s", adapter->mac->ifa_name);
FF_DEBUG("Adding all IPv6 addresses for interface %s", adapter->mac->ifa_name);
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
appendIpv6(options, &item->ipv6, *pifa);
}
}
else
{
FF_DEBUG("Adding all IPv6 addresses for interface %s", adapter->mac->ifa_name);
FF_LIST_FOR_EACH(struct ifaddrs*, pifa, adapter->ipv6)
appendIpv6(options, &item->ipv6, *pifa);
{
FFLocalIpIpv6Type type = getIpv6Type(*pifa);
if (type & options->ipv6Type)
{
if ((options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT) || !(type & FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT))
{
appendIpv6(options, &item->ipv6, *pifa);
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
break;
}
}
}
}
}
mac:
+30 -6
View File
@@ -140,13 +140,24 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next)
{
FF_DEBUG("Processing unicast address: family=%d, DadState=%d",
ifa->Address.lpSockaddr->sa_family, ifa->DadState);
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->DadState);
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT) && ifa->DadState != IpDadStatePreferred)
if (!(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT))
{
FF_DEBUG("Skipping address (DadState=%d, not preferred)", ifa->DadState);
continue;
if (ifa->DadState != IpDadStatePreferred)
{
FF_DEBUG("Skipping address (not preferred)");
continue;
}
if (ifa->SuffixOrigin == IpSuffixOriginRandom)
{
FF_DEBUG("Skipping temporary address (random suffix)");
continue;
}
// MIB_UNICASTIPADDRESS_ROW::SkipAsSource
}
if (ifa->Address.lpSockaddr->sa_family == AF_INET)
@@ -192,6 +203,20 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
continue;
}
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
FFLocalIpIpv6Type ipv6Type = FF_LOCALIP_IPV6_TYPE_NONE;
if (IN6_IS_ADDR_GLOBAL(&ipv6->sin6_addr)) ipv6Type |= FF_LOCALIP_IPV6_TYPE_GUA_BIT;
else if (IN6_IS_ADDR_UNIQUE_LOCAL(&ipv6->sin6_addr)) ipv6Type |= FF_LOCALIP_IPV6_TYPE_ULA_BIT;
else if (IN6_IS_ADDR_LINKLOCAL(&ipv6->sin6_addr)) ipv6Type |= FF_LOCALIP_IPV6_TYPE_LLA_BIT;
else ipv6Type |= FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT;
if (!(options->ipv6Type & ipv6Type))
{
FF_DEBUG("Skipping IPv6 address (doesn't match requested type 0x%X)", options->ipv6Type);
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)
{
@@ -199,7 +224,6 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
continue;
}
SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr;
char addressBuffer[INET6_ADDRSTRLEN + 6];
inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN);
+43 -5
View File
@@ -198,10 +198,33 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module)
if (unsafe_yyjson_equals_str(key, "showIpv6"))
{
if (yyjson_get_bool(val))
options->showType |= FF_LOCALIP_TYPE_IPV6_BIT;
else
options->showType &= ~FF_LOCALIP_TYPE_IPV6_BIT;
if (yyjson_is_bool(val))
{
options->ipv6Type = FF_LOCALIP_IPV6_TYPE_AUTO;
if (unsafe_yyjson_get_bool(val))
options->showType |= FF_LOCALIP_TYPE_IPV6_BIT;
else
options->showType &= ~FF_LOCALIP_TYPE_IPV6_BIT;
}
else if (yyjson_is_str(val))
{
int value;
const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
{ "auto", FF_LOCALIP_IPV6_TYPE_AUTO },
{ "gua", FF_LOCALIP_IPV6_TYPE_GUA_BIT },
{ "ula", FF_LOCALIP_IPV6_TYPE_ULA_BIT },
{ "lla", FF_LOCALIP_IPV6_TYPE_LLA_BIT },
{ "unknown", FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT },
{},
});
if (error)
ffPrintError(FF_LOCALIP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
else
{
options->showType |= FF_LOCALIP_TYPE_IPV6_BIT;
options->ipv6Type = (FFLocalIpIpv6Type) value;
}
}
continue;
}
@@ -302,7 +325,21 @@ void ffGenerateLocalIpJsonConfig(FFLocalIpOptions* options, yyjson_mut_doc* doc,
yyjson_mut_obj_add_bool(doc, module, "showIpv4", !!(options->showType & FF_LOCALIP_TYPE_IPV4_BIT));
yyjson_mut_obj_add_bool(doc, module, "showIpv6", !!(options->showType & FF_LOCALIP_TYPE_IPV6_BIT));
if (options->ipv6Type == FF_LOCALIP_IPV6_TYPE_AUTO)
yyjson_mut_obj_add_bool(doc, module, "showIpv6", !!(options->showType & FF_LOCALIP_TYPE_IPV6_BIT));
else
{
const char* str = NULL;
switch (options->ipv6Type)
{
case FF_LOCALIP_IPV6_TYPE_GUA_BIT: str = "gua"; break;
case FF_LOCALIP_IPV6_TYPE_ULA_BIT: str = "ula"; break;
case FF_LOCALIP_IPV6_TYPE_LLA_BIT: str = "lla"; break;
case FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT: str = "unknown"; break;
default: str = "auto"; break;
}
yyjson_mut_obj_add_str(doc, module, "showIpv6", str);
}
yyjson_mut_obj_add_bool(doc, module, "showMac", !!(options->showType & FF_LOCALIP_TYPE_MAC_BIT));
@@ -385,6 +422,7 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options)
| FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT
#endif
;
options->ipv6Type = FF_LOCALIP_IPV6_TYPE_AUTO;
ffStrbufInit(&options->namePrefix);
}
+15
View File
@@ -21,11 +21,26 @@ typedef enum __attribute__((__packed__)) FFLocalIpType
} FFLocalIpType;
static_assert(sizeof(FFLocalIpType) == sizeof(uint16_t), "");
typedef enum __attribute__((__packed__)) FFLocalIpIpv6Type
{
FF_LOCALIP_IPV6_TYPE_NONE = 0b00000000,
FF_LOCALIP_IPV6_TYPE_GUA_BIT = 0b00000001,
FF_LOCALIP_IPV6_TYPE_ULA_BIT = 0b00000010,
FF_LOCALIP_IPV6_TYPE_LLA_BIT = 0b00000100,
FF_LOCALIP_IPV6_TYPE_UNKNOWN_BIT = 0b00001000, // IPv4-mapped, loopback, etc.
FF_LOCALIP_IPV6_TYPE_SECONDARY_BIT = 0b01000000, // Temporary, duplicated, etc.
FF_LOCALIP_IPV6_TYPE_PREFERRED_BIT = 0b10000000, // PREFER_SOURCE (manually set)
FF_LOCALIP_IPV6_TYPE_TYPE_MASK = 0b00011111,
FF_LOCALIP_IPV6_TYPE_AUTO = 0b11111111, // Used for detect option
} FFLocalIpIpv6Type;
static_assert(sizeof(FFLocalIpIpv6Type) == sizeof(uint8_t), "");
typedef struct FFLocalIpOptions
{
FFModuleArgs moduleArgs;
FFLocalIpType showType;
FFLocalIpIpv6Type ipv6Type;
FFstrbuf namePrefix;
} FFLocalIpOptions;