Change NetworkInfo to use SocketAddress.

PiperOrigin-RevId: 850164819
This commit is contained in:
Francis Tsui
2025-12-29 14:44:35 -08:00
committed by Copybara-Service
parent 067b55ea85
commit ffa760663b
10 changed files with 244 additions and 107 deletions
@@ -144,12 +144,17 @@ cc_library(
"network_info.h",
],
compatible_with = ["//buildenv/target:non_prod"],
defines = [
"_WIN32_WINNT=_WIN32_WINNT_WIN10",
"WINVER=_WIN32_WINNT_WIN10",
],
tags = ["windows"],
visibility = [
"//:__subpackages__",
"//location/nearby:__subpackages__",
],
deps = [
":socket_address",
":string_utils",
":wlan_client",
"//internal/platform:logging",
@@ -15,7 +15,6 @@
#include "internal/platform/implementation/windows/network_info.h"
// clang-format off
#include <windows.h>
#include <winsock2.h>
#include <iphlpapi.h>
// clang-format on
@@ -31,27 +30,30 @@
#include "absl/base/no_destructor.h"
#include "absl/strings/str_cat.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/string_utils.h"
#include "internal/platform/implementation/windows/wlan_client.h"
#include "internal/platform/logging.h"
namespace nearby::windows {
namespace {
void AddIpUnicastAddresses(IP_ADAPTER_UNICAST_ADDRESS* unicast_addresses,
NetworkInfo::InterfaceInfo& net_interface) {
/* static */
void NetworkInfo::AddIpUnicastAddresses(
IP_ADAPTER_UNICAST_ADDRESS* unicast_addresses,
NetworkInfo::InterfaceInfo& net_interface) {
while (unicast_addresses != nullptr) {
sockaddr* address = unicast_addresses->Address.lpSockaddr;
if (address == nullptr) {
unicast_addresses = unicast_addresses->Next;
continue;
}
sockaddr_storage storage;
std::memcpy(&storage, address, unicast_addresses->Address.iSockaddrLength);
if (address->sa_family == AF_INET) {
net_interface.ipv4_addresses.push_back(storage);
} else if (address->sa_family == AF_INET6) {
net_interface.ipv6_addresses.push_back(storage);
SocketAddress socket_address;
std::memcpy(socket_address.address(), address,
unicast_addresses->Address.iSockaddrLength);
if (socket_address.family() == AF_INET) {
net_interface.ipv4_addresses.push_back(std::move(socket_address));
} else if (socket_address.family() == AF_INET6) {
net_interface.ipv6_addresses.push_back(std::move(socket_address));
}
unicast_addresses = unicast_addresses->Next;
}
@@ -60,8 +62,6 @@ void AddIpUnicastAddresses(IP_ADAPTER_UNICAST_ADDRESS* unicast_addresses,
<< net_interface.ipv6_addresses.size() << " v6 addresses.";
}
} // namespace
NetworkInfo& NetworkInfo::GetNetworkInfo() {
static absl::NoDestructor<NetworkInfo> kNetworkInfo;
return *kNetworkInfo;
@@ -18,6 +18,8 @@
// clang-format off
#include <winsock2.h>
#include <ifdef.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
// clang-format on
#include <cstdint>
@@ -25,6 +27,7 @@
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/wlan_client.h"
namespace nearby::windows {
@@ -46,8 +49,8 @@ class NetworkInfo {
uint64_t index;
InterfaceType type;
NET_LUID luid;
std::vector<sockaddr_storage> ipv4_addresses;
std::vector<sockaddr_storage> ipv6_addresses;
std::vector<SocketAddress> ipv4_addresses;
std::vector<SocketAddress> ipv6_addresses;
};
// Returns the singleton instance of this class.
@@ -63,6 +66,12 @@ class NetworkInfo {
bool RenewIpv4Address(NET_LUID luid) const;
private:
friend class NetworkInfoTest;
static void AddIpUnicastAddresses(
IP_ADAPTER_UNICAST_ADDRESS* unicast_addresses,
NetworkInfo::InterfaceInfo& net_interface);
// Lazily initializes the list of wlan interface LUIDs. If `wifi_luids` is
// not empty, it is assumed to be populated already.
void GetWifiLuids(std::vector<ULONG64>& wifi_luids);
@@ -14,19 +14,33 @@
#include "internal/platform/implementation/windows/network_info.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "internal/platform/logging.h"
namespace nearby::windows {
using ::testing::SizeIs;
class NetworkInfoTest : public ::testing::Test {
protected:
static void AddIpUnicastAddresses(
IP_ADAPTER_UNICAST_ADDRESS* unicast_addresses,
NetworkInfo::InterfaceInfo& net_interface) {
NetworkInfo::AddIpUnicastAddresses(unicast_addresses, net_interface);
}
};
namespace {
TEST(NetworkInfoTest, Refresh) {
TEST_F(NetworkInfoTest, Refresh) {
NetworkInfo network_info;
EXPECT_TRUE(network_info.Refresh());
EXPECT_FALSE(network_info.GetInterfaces().empty());
}
TEST(NetworkInfoTest, RenewIpv4Address) {
TEST_F(NetworkInfoTest, RenewIpv4Address) {
NetworkInfo network_info;
EXPECT_TRUE(network_info.Refresh());
for (const auto& net_interface : network_info.GetInterfaces()) {
@@ -37,5 +51,46 @@ TEST(NetworkInfoTest, RenewIpv4Address) {
}
}
TEST_F(NetworkInfoTest, AddIpUnicastAddressesNoAddresss) {
IP_ADAPTER_UNICAST_ADDRESS unicast_addresses;
unicast_addresses.Address.lpSockaddr = nullptr;
unicast_addresses.Next = nullptr;
NetworkInfo::InterfaceInfo net_interface;
AddIpUnicastAddresses(&unicast_addresses, net_interface);
EXPECT_TRUE(net_interface.ipv4_addresses.empty());
EXPECT_TRUE(net_interface.ipv6_addresses.empty());
}
TEST_F(NetworkInfoTest, AddIpUnicastAddresses) {
IP_ADAPTER_UNICAST_ADDRESS unicast_addresses1;
IP_ADAPTER_UNICAST_ADDRESS unicast_addresses2;
unicast_addresses1.Next = &unicast_addresses2;
unicast_addresses2.Next = nullptr;
sockaddr_in ipv4_address = {
.sin_family = AF_INET,
.sin_addr = {{{0x01, 0x02, 0x03, 0x04}}},
};
unicast_addresses1.Address = {
.lpSockaddr = reinterpret_cast<sockaddr*>(&ipv4_address),
.iSockaddrLength = sizeof(ipv4_address),
};;
sockaddr_in6 ipv6_address = {
.sin6_family = AF_INET6,
.sin6_addr = {{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}}},
};
unicast_addresses2.Address = {
.lpSockaddr = reinterpret_cast<sockaddr*>(&ipv6_address),
.iSockaddrLength = sizeof(ipv6_address),
};
NetworkInfo::InterfaceInfo net_interface;
AddIpUnicastAddresses(&unicast_addresses1, net_interface);
EXPECT_THAT(net_interface.ipv4_addresses, SizeIs(1));
EXPECT_THAT(net_interface.ipv6_addresses, SizeIs(1));
EXPECT_EQ(net_interface.ipv4_addresses[0].ToString(), "1.2.3.4");
EXPECT_EQ(net_interface.ipv6_addresses[0].ToString(),
"102:304:506:708:90a:b0c:d0e:f10");
}
} // namespace
} // namespace nearby::windows
@@ -20,6 +20,7 @@
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "absl/types/span.h"
#include "internal/platform/logging.h"
@@ -122,7 +123,7 @@ bool SocketAddress::FromServiceAddress(SocketAddress& address,
return FromBytes(address, service_address.address, service_address.port);
}
int SocketAddress::port() const {
uint16_t SocketAddress::port() const {
DCHECK(address_.ss_family == AF_INET || address_.ss_family == AF_INET6);
if (address_.ss_family == AF_INET) {
const sockaddr_in* v4_address =
@@ -138,12 +139,8 @@ int SocketAddress::port() const {
return 0;
}
bool SocketAddress::set_port(int port) {
bool SocketAddress::set_port(uint16_t port) {
DCHECK(address_.ss_family == AF_INET || address_.ss_family == AF_INET6);
if (port < 0 || port > 65535) {
LOG(ERROR) << "Invalid port: " << port;
return false;
}
if (address_.ss_family == AF_INET) {
sockaddr_in* v4_address = reinterpret_cast<sockaddr_in*>(&address_);
v4_address->sin_port = htons(port);
@@ -179,11 +176,19 @@ bool SocketAddress::IsV6LinkLocal() const {
if (address_.ss_family != AF_INET6) {
return false;
}
const sockaddr_in6* v6_address =
reinterpret_cast<const sockaddr_in6*>(&address_);
const sockaddr_in6* v6_address = ipv6_address();
return IN6_IS_ADDR_LINKLOCAL(&v6_address->sin6_addr);
}
bool SocketAddress::IsV4LinkLocal() const {
if (address_.ss_family != AF_INET) {
return false;
}
const sockaddr_in* v4_address = ipv4_address();
return (v4_address->sin_addr.S_un.S_un_b.s_b1 == 169 &&
v4_address->sin_addr.S_un.S_un_b.s_b2 == 254);
}
bool SocketAddress::SetScopeId(uint32_t scope_id) {
if (address_.ss_family != AF_INET6) {
return false;
@@ -193,4 +198,27 @@ bool SocketAddress::SetScopeId(uint32_t scope_id) {
return true;
}
ServiceAddress SocketAddress::ToServiceAddress(uint16_t port) const {
if (port == 0) {
port = this->port();
}
if (family() == AF_INET) {
return ServiceAddress{
.address = {ipv4_address()->sin_addr.S_un.S_un_b.s_b1,
ipv4_address()->sin_addr.S_un.S_un_b.s_b2,
ipv4_address()->sin_addr.S_un.S_un_b.s_b3,
ipv4_address()->sin_addr.S_un.S_un_b.s_b4},
.port = port,
};
} else {
return ServiceAddress{
.address =
std::vector<char>(ipv6_address()->sin6_addr.u.Byte,
ipv6_address()->sin6_addr.u.Byte + 16),
.port = port,
};
}
}
} // namespace nearby::windows
@@ -62,9 +62,9 @@ class SocketAddress {
int family() const { return address_.ss_family; }
// `Returns port in host byte order.
int port() const;
uint16_t port() const;
// `port` is in host byte order.
bool set_port(int port);
bool set_port(uint16_t port);
std::string ToString() const;
@@ -72,6 +72,10 @@ class SocketAddress {
// Returns false if the address is not IPv6 or is not link local.
bool IsV6LinkLocal() const;
// Returns true if the address is a link local IPv4 address, ie. 169.254.X.X.
// Returns false if the address is not IPv4 or is not link local.
bool IsV4LinkLocal() const;
// Sets the scope id of the address.
// Returns false if the address is not IPv6.
bool SetScopeId(uint32_t scope_id);
@@ -99,6 +103,11 @@ class SocketAddress {
return reinterpret_cast<const sockaddr_in6*>(&address_);
}
// Pack this SocketAddress into a ServiceAddress.
// If `port` is non-zero, override the port from this SocketAddress with
// `port` in the ServiceAddress.
ServiceAddress ToServiceAddress(uint16_t port = 0) const;
private:
sockaddr_storage address_;
};
@@ -14,6 +14,8 @@
#include "internal/platform/implementation/windows/socket_address.h"
#include <vector>
#include "gtest/gtest.h"
#include "internal/platform/service_address.h"
@@ -121,14 +123,6 @@ TEST(SocketAddressTest, SetPort) {
EXPECT_EQ(address.port(), 9090);
}
TEST(SocketAddressTest, SetInvalidPort) {
SocketAddress address;
EXPECT_TRUE(SocketAddress::FromString(address, "127.0.0.1", 8080));
EXPECT_FALSE(address.set_port(-1));
EXPECT_FALSE(address.set_port(65536));
EXPECT_EQ(address.port(), 8080);
}
TEST(SocketAddressTest, FromBytesIPv4) {
SocketAddress address;
char bytes[4] = {192, 168, 1, 1};
@@ -164,6 +158,20 @@ TEST(SocketAddressTest, IPv6LinkLocalFail) {
EXPECT_FALSE(address.IsV6LinkLocal());
}
TEST(SocketAddressTest, IPv4LinkLocalSuccess) {
SocketAddress address;
char bytes[4] = {169, 254, 1, 1};
EXPECT_TRUE(SocketAddress::FromBytes(address, bytes, 8080));
EXPECT_TRUE(address.IsV4LinkLocal());
}
TEST(SocketAddressTest, IPv4LinkLocalFail) {
SocketAddress address;
char bytes[4] = {192, 168, 1, 1};
EXPECT_TRUE(SocketAddress::FromBytes(address, bytes, 8080));
EXPECT_FALSE(address.IsV4LinkLocal());
}
TEST(SocketAddressTest, FromServiceAddressIPv4) {
SocketAddress address;
ServiceAddress service_address = {
@@ -198,5 +206,43 @@ TEST(SocketAddressTest, FromServiceAddressInvalidAddress) {
EXPECT_FALSE(SocketAddress::FromServiceAddress(address, service_address));
}
TEST(SocketAddressTest, ToServiceAddressIPv4) {
SocketAddress address;
EXPECT_TRUE(SocketAddress::FromString(address, "192.168.1.1", 8080));
ServiceAddress service_address = address.ToServiceAddress();
EXPECT_EQ(service_address.address,
(std::vector<char>{192, 168, 1, 1}));
EXPECT_EQ(service_address.port, 8080);
}
TEST(SocketAddressTest, ToServiceAddressIPv6) {
SocketAddress address;
EXPECT_TRUE(SocketAddress::FromString(address, "2001:db8::1", 8080));
ServiceAddress service_address = address.ToServiceAddress();
EXPECT_EQ(service_address.address,
(std::vector<char>{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1}));
EXPECT_EQ(service_address.port, 8080);
}
TEST(SocketAddressTest, ToServiceAddressIPv4PortOverride) {
SocketAddress address;
EXPECT_TRUE(SocketAddress::FromString(address, "192.168.1.1", 8080));
ServiceAddress service_address = address.ToServiceAddress(9090);
EXPECT_EQ(service_address.address,
(std::vector<char>{192, 168, 1, 1}));
EXPECT_EQ(service_address.port, 9090);
}
TEST(SocketAddressTest, ToServiceAddressIPv6PortOverride) {
SocketAddress address;
EXPECT_TRUE(SocketAddress::FromString(address, "2001:db8::1", 8080));
ServiceAddress service_address = address.ToServiceAddress(9090);
EXPECT_EQ(service_address.address,
(std::vector<char>{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1}));
EXPECT_EQ(service_address.port, 9090);
}
} // namespace
} // namespace nearby::windows
@@ -38,6 +38,7 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "internal/platform/implementation/windows/network_info.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/string_utils.h"
#include "internal/platform/implementation/windows/wlan_client.h"
#include "internal/platform/logging.h"
@@ -409,7 +410,8 @@ bool WifiHotspotNative::HasAssignedAddress(bool include_ipv6) {
}
NET_LUID luid;
ConvertInterfaceGuidToLuid(&interface_guid, &luid);
for (const auto& interface : network_info_.GetInterfaces()) {
for (const NetworkInfo::InterfaceInfo& interface :
network_info_.GetInterfaces()) {
if (interface.luid.Value != luid.Value) {
continue;
}
@@ -418,13 +420,9 @@ bool WifiHotspotNative::HasAssignedAddress(bool include_ipv6) {
return true;
}
}
for (const auto& address : interface.ipv4_addresses) {
DCHECK(address.ss_family == AF_INET);
const sockaddr_in* ipv4_address =
reinterpret_cast<const sockaddr_in*>(&address);
// We ignore APIPA addresses since we won't be able to connect using that.
if (ipv4_address->sin_addr.S_un.S_un_b.s_b1 != 169 ||
ipv4_address->sin_addr.S_un.S_un_b.s_b2 != 254) {
for (const SocketAddress& address : interface.ipv4_addresses) {
// We ignore APIPA addresses since we won't be able to connect using them.
if (!address.IsV4LinkLocal()) {
return true;
}
}
@@ -40,6 +40,7 @@
#include "internal/platform/implementation/windows/wifi_hotspot_server_socket.h"
#include "internal/platform/implementation/windows/wifi_hotspot_socket.h"
#include "internal/platform/logging.h"
#include "internal/platform/service_address.h"
#include "internal/platform/wifi_credential.h"
namespace nearby::windows {
@@ -61,7 +62,9 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotServerSocket::Accept() {
void WifiHotspotServerSocket::PopulateHotspotCredentials(
HotspotCredentials& hotspot_credentials) {
// Get current IP addresses of the device.
bool use_address_candidates = NearbyFlags::GetInstance().GetBoolFlag(
platform::config_package_nearby::nearby_platform_feature::
kEnableHotspotAddressCandidates);
int64_t ip_address_max_retries = NearbyFlags::GetInstance().GetInt64Flag(
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotCheckIpMaxRetries);
@@ -69,30 +72,29 @@ void WifiHotspotServerSocket::PopulateHotspotCredentials(
NearbyFlags::GetInstance().GetInt64Flag(
platform::config_package_nearby::nearby_platform_feature::
kWifiHotspotCheckIpIntervalMillis);
VLOG(1) << "maximum IP check retries=" << ip_address_max_retries
<< ", IP check interval=" << ip_address_retry_interval_millis << "ms";
std::string hotspot_ipaddr;
for (int i = 0; i < ip_address_max_retries; i++) {
hotspot_ipaddr = GetHotspotIpAddress();
if (hotspot_ipaddr.empty()) {
LOG(WARNING) << "Failed to find Hotspot's IP addr for the try: " << i + 1
<< ". Wait " << ip_address_retry_interval_millis
<< "ms snd try again";
Sleep(ip_address_retry_interval_millis);
} else {
break;
}
}
if (hotspot_ipaddr.empty()) {
LOG(WARNING) << "Failed to start accepting connection without IP "
"addresses configured on computer.";
return;
}
bool use_address_candidates = NearbyFlags::GetInstance().GetBoolFlag(
platform::config_package_nearby::nearby_platform_feature::
kEnableHotspotAddressCandidates);
if (!use_address_candidates) {
// Get current IP addresses of the device.
VLOG(1) << "maximum IP check retries=" << ip_address_max_retries
<< ", IP check interval=" << ip_address_retry_interval_millis
<< "ms";
std::string hotspot_ipaddr;
for (int i = 0; i < ip_address_max_retries; i++) {
hotspot_ipaddr = GetHotspotIpAddress();
if (hotspot_ipaddr.empty()) {
LOG(WARNING) << "Failed to find Hotspot's IP addr for the try: "
<< i + 1 << ". Wait " << ip_address_retry_interval_millis
<< "ms snd try again";
Sleep(ip_address_retry_interval_millis);
} else {
break;
}
}
if (hotspot_ipaddr.empty()) {
LOG(WARNING) << "Failed to start accepting connection without IP "
"addresses configured on computer.";
return;
}
std::vector<char> hotspot_ipaddr_bytes;
uint32_t address_int = inet_addr(hotspot_ipaddr.c_str());
if (address_int != INADDR_NONE) {
@@ -108,31 +110,28 @@ void WifiHotspotServerSocket::PopulateHotspotCredentials(
return;
}
std::vector<ServiceAddress> service_addresses;
for (const auto& interface : NetworkInfo::GetNetworkInfo().GetInterfaces()) {
if (interface.type == InterfaceType::kWifiHotspot) {
LOG(INFO) << "Found Wifi Hotspot interface, index: " << interface.index;
for (const auto& ipaddress : interface.ipv6_addresses) {
const sockaddr_in6* ipv6_address =
reinterpret_cast<const sockaddr_in6*>(&ipaddress);
service_addresses.push_back(ServiceAddress{
.address = std::vector<char>(ipv6_address->sin6_addr.u.Byte,
ipv6_address->sin6_addr.u.Byte + 16),
.port = static_cast<uint16_t>(GetPort()),
});
}
for (const auto& ipaddress : interface.ipv4_addresses) {
const sockaddr_in* ipv4_address =
reinterpret_cast<const sockaddr_in*>(&ipaddress);
service_addresses.push_back(ServiceAddress{
.address = {ipv4_address->sin_addr.S_un.S_un_b.s_b1,
ipv4_address->sin_addr.S_un.S_un_b.s_b2,
ipv4_address->sin_addr.S_un.S_un_b.s_b3,
ipv4_address->sin_addr.S_un.S_un_b.s_b4},
.port = static_cast<uint16_t>(GetPort()),
});
for (int i = 0; i < ip_address_max_retries; i++) {
for (const auto& net_interface :
NetworkInfo::GetNetworkInfo().GetInterfaces()) {
if (net_interface.type == InterfaceType::kWifiHotspot) {
LOG(INFO) << "Found Wifi Hotspot interface, index: "
<< net_interface.index;
for (const SocketAddress& ipaddress : net_interface.ipv6_addresses) {
service_addresses.push_back(ipaddress.ToServiceAddress(GetPort()));
}
for (const SocketAddress& ipaddress : net_interface.ipv4_addresses) {
service_addresses.push_back(ipaddress.ToServiceAddress(GetPort()));
}
break;
}
}
if (!service_addresses.empty()) {
break;
}
LOG(WARNING) << "Failed to find Wifi Hotspot interface. Wait "
<< ip_address_retry_interval_millis
<< "ms snd try again";
Sleep(ip_address_retry_interval_millis);
}
hotspot_credentials.SetAddressCandidates(std::move(service_addresses));
}
@@ -15,9 +15,10 @@
#include "internal/platform/implementation/windows/wifi_lan.h"
// Windows headers
#include <iphlpapi.h>
#include <windows.h>
// clang-format off
#include <winsock2.h>
#include <iphlpapi.h>
// clang-format on
// Standard C/C++ headers
#include <cstdint>
@@ -728,29 +729,16 @@ std::vector<ServiceAddress> WifiLanMedium::GetUpgradeAddressCandidates(
net_interface.type != InterfaceType::kEthernet) {
continue;
}
for (const auto& ipv6_address : net_interface.ipv6_addresses) {
SocketAddress address(ipv6_address);
for (const SocketAddress& address : net_interface.ipv6_addresses) {
// Link local addresses cannot be used for upgrade since we can't tell
// which interface on the remote device the address is valid.
if (address.IsV6LinkLocal()) {
continue;
}
ip_addresses.push_back(ServiceAddress{
.address =
std::vector<char>(address.ipv6_address()->sin6_addr.u.Byte,
address.ipv6_address()->sin6_addr.u.Byte + 16),
.port = port,
});
ip_addresses.push_back(address.ToServiceAddress(port));
}
for (const auto& ipv4address : net_interface.ipv4_addresses) {
auto address = reinterpret_cast<const sockaddr_in*>(&ipv4address);
ipv4_addresses.push_back(ServiceAddress{
.address = {address->sin_addr.S_un.S_un_b.s_b1,
address->sin_addr.S_un.S_un_b.s_b2,
address->sin_addr.S_un.S_un_b.s_b3,
address->sin_addr.S_un.S_un_b.s_b4},
.port = port,
});
for (const SocketAddress& v4_address : net_interface.ipv4_addresses) {
ipv4_addresses.push_back(v4_address.ToServiceAddress(port));
}
}
if (NearbyFlags::GetInstance().GetBoolFlag(