diff --git a/lib/platform_compat.c b/lib/platform_compat.c index ece193c1..4045b7f5 100644 --- a/lib/platform_compat.c +++ b/lib/platform_compat.c @@ -60,6 +60,12 @@ int random_bytes(uint8_t *buffer, size_t len) { #endif } +/* Глобальный unicast IPv6 (2000::/3). При выборе адреса интерфейса предпочитаем его + * ULA (fc00::/7) и link-local (fe80::/10). */ +static int ipv6_is_global_unicast(const uint8_t* a) { + return (a[0] & 0xe0) == 0x20; +} + #ifndef _WIN32 uint32_t get_interface_ip_by_index(uint32_t netif_index) { struct ifaddrs *ifaddr, *ifa; @@ -89,7 +95,8 @@ int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* ad struct ifaddrs *ifaddr, *ifa; if (getifaddrs(&ifaddr) == -1) return -1; - int found = 0; + uint8_t global_match[16], local_match[16]; + int has_global = 0, has_local = 0; for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_name == NULL) continue; @@ -100,20 +107,18 @@ int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* ad struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ifa->ifa_addr; if ((sin6->sin6_addr.s6_addr[0] & 0xfe) == 0xfe && sin6->sin6_addr.s6_addr[1] == 0x80) continue; int is_temporary = (ifa->ifa_flags & IFA_F_TEMPORARY) ? 1 : 0; - if (temporary && is_temporary) { - memcpy(addr_out, &sin6->sin6_addr, 16); - found = 1; - break; - } - if (!temporary && !is_temporary) { - memcpy(addr_out, &sin6->sin6_addr, 16); - found = 1; - break; + if ((temporary && !is_temporary) || (!temporary && is_temporary)) continue; + if (ipv6_is_global_unicast(sin6->sin6_addr.s6_addr)) { + if (!has_global) { memcpy(global_match, &sin6->sin6_addr, 16); has_global = 1; } + } else { + if (!has_local) { memcpy(local_match, &sin6->sin6_addr, 16); has_local = 1; } } } freeifaddrs(ifaddr); - return found ? 0 : -1; + if (has_global) { memcpy(addr_out, global_match, 16); return 0; } + if (has_local) { memcpy(addr_out, local_match, 16); return 0; } + return -1; } #ifdef __linux__ @@ -151,8 +156,8 @@ int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* if (send(nl_sock, &req, req.nlh.nlmsg_len, 0) < 0) { close(nl_sock); return -1; } - uint8_t fallback[16], stable_addr[16], temp_addr[16]; - int has_fallback = 0, has_stable = 0, has_temp = 0; + uint8_t fallback[16], g_stable[16], g_temp[16], l_stable[16], l_temp[16]; + int has_fallback = 0, has_g_stable = 0, has_g_temp = 0, has_l_stable = 0, has_l_temp = 0; char buf[8192]; int done = 0; @@ -181,19 +186,32 @@ int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* int is_temp = (ifa_flags & IFA_F_TEMPORARY) != 0; int is_mngtmp = (ifa_flags & IFA_F_MANAGETEMPADDR) != 0; + int is_global = ipv6_is_global_unicast(addr); if (!has_fallback) { memcpy(fallback, addr, 16); has_fallback = 1; } - if (!has_stable && (!is_temp || is_mngtmp)) { memcpy(stable_addr, addr, 16); has_stable = 1; } - if (!has_temp && is_temp && !is_mngtmp) { memcpy(temp_addr, addr, 16); has_temp = 1; } + if (is_global) { + if (!has_g_stable && (!is_temp || is_mngtmp)) { memcpy(g_stable, addr, 16); has_g_stable = 1; } + if (!has_g_temp && is_temp && !is_mngtmp) { memcpy(g_temp, addr, 16); has_g_temp = 1; } + } else { + if (!has_l_stable && (!is_temp || is_mngtmp)) { memcpy(l_stable, addr, 16); has_l_stable = 1; } + if (!has_l_temp && is_temp && !is_mngtmp) { memcpy(l_temp, addr, 16); has_l_temp = 1; } + } } } close(nl_sock); - if (prefer_stable && has_stable) { memcpy(addr_out, stable_addr, 16); return 0; } - if (!prefer_stable && has_temp) { memcpy(addr_out, temp_addr, 16); return 0; } - if (has_stable) { memcpy(addr_out, stable_addr, 16); return 0; } - if (has_temp) { memcpy(addr_out, temp_addr, 16); return 0; } + if (prefer_stable) { + if (has_g_stable) { memcpy(addr_out, g_stable, 16); return 0; } + if (has_g_temp) { memcpy(addr_out, g_temp, 16); return 0; } + if (has_l_stable) { memcpy(addr_out, l_stable, 16); return 0; } + if (has_l_temp) { memcpy(addr_out, l_temp, 16); return 0; } + } else { + if (has_g_temp) { memcpy(addr_out, g_temp, 16); return 0; } + if (has_g_stable) { memcpy(addr_out, g_stable, 16); return 0; } + if (has_l_temp) { memcpy(addr_out, l_temp, 16); return 0; } + if (has_l_stable) { memcpy(addr_out, l_stable, 16); return 0; } + } if (has_fallback) { memcpy(addr_out, fallback, 16); return 0; } return -1; } @@ -335,6 +353,7 @@ int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* PIP_ADAPTER_ADDRESSES adapters = plat_win_get_adapters(AF_INET6); if (!adapters) return -1; int found = 0; + uint8_t local_match[16]; int has_local = 0; PIP_ADAPTER_ADDRESSES a = plat_win_find_adapter(adapters, netif_index); if (a) { for (PIP_ADAPTER_UNICAST_ADDRESS u = a->FirstUnicastAddress; u; u = u->Next) { @@ -344,12 +363,12 @@ int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* { static const uint8_t lb[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; if (memcmp(b, lb, 16) == 0) continue; } /* loopback */ { static const uint8_t z[16]; if (memcmp(b, z, 16) == 0) continue; } /* :: */ - memcpy(addr_out, b, 16); - found = 1; - break; + if (ipv6_is_global_unicast(b)) { memcpy(addr_out, b, 16); found = 1; break; } + if (!has_local) { memcpy(local_match, b, 16); has_local = 1; } } } free(adapters); + if (!found && has_local) { memcpy(addr_out, local_match, 16); found = 1; } return found ? 0 : -1; }