diff --git a/doc/json_schema.json b/doc/json_schema.json index 4c16c8e34..284ec5736 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -2920,7 +2920,32 @@ }, "showIpv6": { "description": "Show IPv6 addresses", - "type": "boolean", + "oneOf": [ + { + "const": true, + "description": "Show the most useful IPv6 addresses" + }, + { + "const": false, + "description": "Do not show IPv6 addresses" + }, + { + "const": "gua", + "description": "Show only global unicast IPv6 addresses (2000::/3)" + }, + { + "const": "ula", + "description": "Show only unique local IPv6 addresses (fc00::/7)" + }, + { + "const": "lla", + "description": "Show only link-local IPv6 addresses (fe80::/10)" + }, + { + "const": "unknown", + "description": "Show only IPv6 addresses that are not gua, ula or lla" + } + ], "default": false }, "showSpeed": { diff --git a/src/detection/localip/localip.h b/src/detection/localip/localip.h index e3361cfbf..685ff3f48 100644 --- a/src/detection/localip/localip.h +++ b/src/detection/localip/localip.h @@ -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; diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c index 962e259c0..1b5537ff6 100644 --- a/src/detection/localip/localip_linux.c +++ b/src/detection/localip/localip_linux.c @@ -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: diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c index 2e12280c3..7c4e32a45 100644 --- a/src/detection/localip/localip_windows.c +++ b/src/detection/localip/localip_windows.c @@ -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); diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 6b7266940..bb479099d 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -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); } diff --git a/src/modules/localip/option.h b/src/modules/localip/option.h index b809fe2fa..18205263c 100644 --- a/src/modules/localip/option.h +++ b/src/modules/localip/option.h @@ -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;