Avi Drissman | 6459548 | 2022-09-14 20:52:29 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/base/network_interfaces_win.h" |
| 6 | |
| 7 | #include <iphlpapi.h> |
| 8 | #include <objbase.h> |
| 9 | |
| 10 | #include <ostream> |
| 11 | #include <string> |
| 12 | #include <unordered_set> |
| 13 | |
Hans Wennborg | 725d043 | 2020-06-18 13:54:16 | [diff] [blame] | 14 | #include "base/logging.h" |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 15 | #include "base/strings/utf_string_conversions.h" |
| 16 | #include "build/build_config.h" |
| 17 | #include "net/base/ip_endpoint.h" |
| 18 | #include "testing/gtest/include/gtest/gtest.h" |
| 19 | |
| 20 | namespace net { |
| 21 | |
| 22 | namespace { |
| 23 | |
| 24 | static const char kIfnameEm1[] = "em1"; |
| 25 | static const char kIfnameVmnet[] = "VMnet"; |
| 26 | |
| 27 | static const unsigned char kIPv6LocalAddr[] = {0, 0, 0, 0, 0, 0, 0, 0, |
| 28 | 0, 0, 0, 0, 0, 0, 0, 1}; |
| 29 | |
| 30 | static const unsigned char kIPv6Addr[] = {0x24, 0x01, 0xfa, 0x00, 0x00, 0x04, |
| 31 | 0x10, 0x00, 0xbe, 0x30, 0x5b, 0xff, |
| 32 | 0xfe, 0xe5, 0x00, 0xc3}; |
| 33 | static const unsigned char kIPv6AddrPrefix[] = { |
| 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| 36 | |
| 37 | // Helper function to create a valid IP_ADAPTER_ADDRESSES with reasonable |
| 38 | // default value. The output is the |adapter_address|. All the rests are input |
| 39 | // to fill the |adapter_address|. |sock_addrs| are temporary storage used by |
| 40 | // |adapter_address| once the function is returned. |
| 41 | bool FillAdapterAddress(IP_ADAPTER_ADDRESSES* adapter_address, |
| 42 | const char* ifname, |
| 43 | const IPAddress& ip_address, |
| 44 | const IPAddress& ip_netmask, |
| 45 | sockaddr_storage sock_addrs[2]) { |
| 46 | adapter_address->AdapterName = const_cast<char*>(ifname); |
| 47 | adapter_address->FriendlyName = const_cast<PWCHAR>(L"interface"); |
| 48 | adapter_address->IfType = IF_TYPE_ETHERNET_CSMACD; |
| 49 | adapter_address->OperStatus = IfOperStatusUp; |
| 50 | adapter_address->FirstUnicastAddress->DadState = IpDadStatePreferred; |
| 51 | adapter_address->FirstUnicastAddress->PrefixOrigin = IpPrefixOriginOther; |
| 52 | adapter_address->FirstUnicastAddress->SuffixOrigin = IpSuffixOriginOther; |
| 53 | adapter_address->FirstUnicastAddress->PreferredLifetime = 100; |
| 54 | adapter_address->FirstUnicastAddress->ValidLifetime = 1000; |
| 55 | |
Erik Ovelius | b5191857 | 2022-09-02 18:52:12 | [diff] [blame] | 56 | DCHECK(sizeof(adapter_address->PhysicalAddress) > 5); |
| 57 | // Generate 06:05:04:03:02:01 |
| 58 | adapter_address->PhysicalAddressLength = 6; |
| 59 | for (unsigned long i = 0; i < adapter_address->PhysicalAddressLength; i++) { |
| 60 | adapter_address->PhysicalAddress[i] = |
| 61 | adapter_address->PhysicalAddressLength - i; |
| 62 | } |
| 63 | |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 64 | socklen_t sock_len = sizeof(sockaddr_storage); |
| 65 | |
| 66 | // Convert to sockaddr for next check. |
| 67 | if (!IPEndPoint(ip_address, 0) |
| 68 | .ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addrs[0]), |
| 69 | &sock_len)) { |
| 70 | return false; |
| 71 | } |
| 72 | adapter_address->FirstUnicastAddress->Address.lpSockaddr = |
| 73 | reinterpret_cast<sockaddr*>(&sock_addrs[0]); |
| 74 | adapter_address->FirstUnicastAddress->Address.iSockaddrLength = sock_len; |
| 75 | adapter_address->FirstUnicastAddress->OnLinkPrefixLength = 1; |
| 76 | |
| 77 | sock_len = sizeof(sockaddr_storage); |
| 78 | if (!IPEndPoint(ip_netmask, 0) |
| 79 | .ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addrs[1]), |
| 80 | &sock_len)) { |
| 81 | return false; |
| 82 | } |
| 83 | adapter_address->FirstPrefix->Address.lpSockaddr = |
| 84 | reinterpret_cast<sockaddr*>(&sock_addrs[1]); |
| 85 | adapter_address->FirstPrefix->Address.iSockaddrLength = sock_len; |
| 86 | adapter_address->FirstPrefix->PrefixLength = 1; |
| 87 | |
| 88 | DCHECK_EQ(sock_addrs[0].ss_family, sock_addrs[1].ss_family); |
| 89 | if (sock_addrs[0].ss_family == AF_INET6) { |
| 90 | adapter_address->Ipv6IfIndex = 0; |
| 91 | } else { |
| 92 | DCHECK_EQ(sock_addrs[0].ss_family, AF_INET); |
| 93 | adapter_address->IfIndex = 0; |
| 94 | } |
| 95 | |
| 96 | return true; |
| 97 | } |
| 98 | |
| 99 | TEST(NetworkInterfacesTest, NetworkListTrimmingWindows) { |
| 100 | IPAddress ipv6_local_address(kIPv6LocalAddr); |
| 101 | IPAddress ipv6_address(kIPv6Addr); |
| 102 | IPAddress ipv6_prefix(kIPv6AddrPrefix); |
| 103 | |
| 104 | NetworkInterfaceList results; |
| 105 | sockaddr_storage addresses[2]; |
| 106 | IP_ADAPTER_ADDRESSES adapter_address = {}; |
| 107 | IP_ADAPTER_UNICAST_ADDRESS address = {}; |
| 108 | IP_ADAPTER_PREFIX adapter_prefix = {}; |
| 109 | adapter_address.FirstUnicastAddress = &address; |
| 110 | adapter_address.FirstPrefix = &adapter_prefix; |
| 111 | |
| 112 | // Address of offline links should be ignored. |
| 113 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address, |
| 114 | ipv6_prefix, addresses)); |
| 115 | adapter_address.OperStatus = IfOperStatusDown; |
| 116 | |
| 117 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 118 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 119 | |
| 120 | EXPECT_EQ(results.size(), 0ul); |
| 121 | |
| 122 | // Address on loopback interface should be trimmed out. |
| 123 | ASSERT_TRUE(FillAdapterAddress( |
| 124 | &adapter_address /* adapter_address */, kIfnameEm1 /* ifname */, |
| 125 | ipv6_local_address /* ip_address */, ipv6_prefix /* ip_netmask */, |
| 126 | addresses /* sock_addrs */)); |
| 127 | adapter_address.IfType = IF_TYPE_SOFTWARE_LOOPBACK; |
| 128 | |
| 129 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 130 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 131 | EXPECT_EQ(results.size(), 0ul); |
| 132 | |
| 133 | // vmware address should return by default. |
| 134 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameVmnet, ipv6_address, |
| 135 | ipv6_prefix, addresses)); |
| 136 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 137 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 138 | EXPECT_EQ(results.size(), 1ul); |
| 139 | EXPECT_EQ(results[0].name, kIfnameVmnet); |
| 140 | EXPECT_EQ(results[0].prefix_length, 1ul); |
| 141 | EXPECT_EQ(results[0].address, ipv6_address); |
| 142 | EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_NONE); |
| 143 | results.clear(); |
| 144 | |
| 145 | // vmware address should be trimmed out if policy specified so. |
| 146 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameVmnet, ipv6_address, |
| 147 | ipv6_prefix, addresses)); |
| 148 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 149 | &results, EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 150 | EXPECT_EQ(results.size(), 0ul); |
| 151 | results.clear(); |
| 152 | |
| 153 | // Addresses with incomplete DAD should be ignored. |
| 154 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address, |
| 155 | ipv6_prefix, addresses)); |
| 156 | adapter_address.FirstUnicastAddress->DadState = IpDadStateTentative; |
| 157 | |
| 158 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 159 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 160 | EXPECT_EQ(results.size(), 0ul); |
| 161 | results.clear(); |
| 162 | |
| 163 | // Addresses with allowed attribute IpSuffixOriginRandom should be returned |
| 164 | // and attributes should be translated correctly to |
| 165 | // IP_ADDRESS_ATTRIBUTE_TEMPORARY. |
| 166 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address, |
| 167 | ipv6_prefix, addresses)); |
| 168 | adapter_address.FirstUnicastAddress->PrefixOrigin = |
| 169 | IpPrefixOriginRouterAdvertisement; |
| 170 | adapter_address.FirstUnicastAddress->SuffixOrigin = IpSuffixOriginRandom; |
| 171 | |
| 172 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 173 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 174 | EXPECT_EQ(results.size(), 1ul); |
| 175 | EXPECT_EQ(results[0].name, kIfnameEm1); |
| 176 | EXPECT_EQ(results[0].prefix_length, 1ul); |
| 177 | EXPECT_EQ(results[0].address, ipv6_address); |
| 178 | EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_TEMPORARY); |
| 179 | results.clear(); |
| 180 | |
| 181 | // Addresses with preferred lifetime 0 should be returned and |
| 182 | // attributes should be translated correctly to |
| 183 | // IP_ADDRESS_ATTRIBUTE_DEPRECATED. |
| 184 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address, |
| 185 | ipv6_prefix, addresses)); |
| 186 | adapter_address.FirstUnicastAddress->PreferredLifetime = 0; |
| 187 | adapter_address.FriendlyName = const_cast<PWCHAR>(L"FriendlyInterfaceName"); |
| 188 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 189 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 190 | EXPECT_EQ(results.size(), 1ul); |
| 191 | EXPECT_EQ(results[0].friendly_name, "FriendlyInterfaceName"); |
| 192 | EXPECT_EQ(results[0].name, kIfnameEm1); |
| 193 | EXPECT_EQ(results[0].prefix_length, 1ul); |
| 194 | EXPECT_EQ(results[0].address, ipv6_address); |
| 195 | EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_DEPRECATED); |
| 196 | results.clear(); |
| 197 | } |
| 198 | |
Erik Ovelius | b5191857 | 2022-09-02 18:52:12 | [diff] [blame] | 199 | TEST(NetworkInterfacesTest, NetworkListExtractMacAddress) { |
| 200 | IPAddress ipv6_local_address(kIPv6LocalAddr); |
| 201 | IPAddress ipv6_address(kIPv6Addr); |
| 202 | IPAddress ipv6_prefix(kIPv6AddrPrefix); |
| 203 | |
| 204 | NetworkInterfaceList results; |
| 205 | sockaddr_storage addresses[2]; |
| 206 | IP_ADAPTER_ADDRESSES adapter_address = {}; |
| 207 | IP_ADAPTER_UNICAST_ADDRESS address = {}; |
| 208 | IP_ADAPTER_PREFIX adapter_prefix = {}; |
| 209 | adapter_address.FirstUnicastAddress = &address; |
| 210 | adapter_address.FirstPrefix = &adapter_prefix; |
| 211 | |
| 212 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address, |
| 213 | ipv6_prefix, addresses)); |
| 214 | |
| 215 | Eui48MacAddress expected_mac_address = {0x6, 0x5, 0x4, 0x3, 0x2, 0x1}; |
| 216 | |
| 217 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 218 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 219 | ASSERT_EQ(results.size(), 1ul); |
| 220 | ASSERT_EQ(results[0].mac_address, expected_mac_address); |
| 221 | } |
| 222 | |
| 223 | TEST(NetworkInterfacesTest, NetworkListExtractMacAddressInvalidLength) { |
| 224 | IPAddress ipv6_local_address(kIPv6LocalAddr); |
| 225 | IPAddress ipv6_address(kIPv6Addr); |
| 226 | IPAddress ipv6_prefix(kIPv6AddrPrefix); |
| 227 | |
| 228 | NetworkInterfaceList results; |
| 229 | sockaddr_storage addresses[2]; |
| 230 | IP_ADAPTER_ADDRESSES adapter_address = {}; |
| 231 | IP_ADAPTER_UNICAST_ADDRESS address = {}; |
| 232 | IP_ADAPTER_PREFIX adapter_prefix = {}; |
| 233 | adapter_address.FirstUnicastAddress = &address; |
| 234 | adapter_address.FirstPrefix = &adapter_prefix; |
| 235 | |
| 236 | ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address, |
| 237 | ipv6_prefix, addresses)); |
| 238 | // Not EUI-48 Mac address, so it is not extracted. |
| 239 | adapter_address.PhysicalAddressLength = 8; |
| 240 | |
| 241 | EXPECT_TRUE(internal::GetNetworkListImpl( |
| 242 | &results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address)); |
| 243 | ASSERT_EQ(results.size(), 1ul); |
| 244 | EXPECT_FALSE(results[0].mac_address.has_value()); |
| 245 | } |
| 246 | |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 247 | bool read_int_or_bool(DWORD data_size, PVOID data) { |
| 248 | switch (data_size) { |
| 249 | case 1: |
| 250 | return !!*reinterpret_cast<uint8_t*>(data); |
| 251 | case 4: |
| 252 | return !!*reinterpret_cast<uint32_t*>(data); |
| 253 | default: |
| 254 | LOG(FATAL) << "That is not a type I know!"; |
| 255 | return false; |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | int GetWifiOptions() { |
| 260 | const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance(); |
| 261 | if (!wlanapi.initialized) |
| 262 | return -1; |
| 263 | |
| 264 | internal::WlanHandle client; |
| 265 | DWORD cur_version = 0; |
| 266 | const DWORD kMaxClientVersion = 2; |
| 267 | DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client); |
| 268 | if (result != ERROR_SUCCESS) |
| 269 | return -1; |
| 270 | |
Raul Tambre | 94493c65 | 2019-03-11 17:18:35 | [diff] [blame] | 271 | WLAN_INTERFACE_INFO_LIST* interface_list_ptr = nullptr; |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 272 | result = |
Raul Tambre | 94493c65 | 2019-03-11 17:18:35 | [diff] [blame] | 273 | wlanapi.enum_interfaces_func(client.Get(), nullptr, &interface_list_ptr); |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 274 | if (result != ERROR_SUCCESS) |
| 275 | return -1; |
| 276 | std::unique_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter> |
| 277 | interface_list(interface_list_ptr); |
| 278 | |
| 279 | for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) { |
| 280 | WLAN_INTERFACE_INFO* info = &interface_list->InterfaceInfo[i]; |
| 281 | DWORD data_size; |
| 282 | PVOID data; |
| 283 | int options = 0; |
| 284 | result = |
| 285 | wlanapi.query_interface_func(client.Get(), &info->InterfaceGuid, |
| 286 | wlan_intf_opcode_background_scan_enabled, |
Raul Tambre | 94493c65 | 2019-03-11 17:18:35 | [diff] [blame] | 287 | nullptr, &data_size, &data, nullptr); |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 288 | if (result != ERROR_SUCCESS) |
| 289 | continue; |
| 290 | if (!read_int_or_bool(data_size, data)) { |
| 291 | options |= WIFI_OPTIONS_DISABLE_SCAN; |
| 292 | } |
| 293 | internal::WlanApi::GetInstance().free_memory_func(data); |
| 294 | |
| 295 | result = wlanapi.query_interface_func(client.Get(), &info->InterfaceGuid, |
| 296 | wlan_intf_opcode_media_streaming_mode, |
Raul Tambre | 94493c65 | 2019-03-11 17:18:35 | [diff] [blame] | 297 | nullptr, &data_size, &data, nullptr); |
Sergey Ulanov | 5bb07d3 | 2017-07-12 04:14:54 | [diff] [blame] | 298 | if (result != ERROR_SUCCESS) |
| 299 | continue; |
| 300 | if (read_int_or_bool(data_size, data)) { |
| 301 | options |= WIFI_OPTIONS_MEDIA_STREAMING_MODE; |
| 302 | } |
| 303 | internal::WlanApi::GetInstance().free_memory_func(data); |
| 304 | |
| 305 | // Just the the options from the first succesful |
| 306 | // interface. |
| 307 | return options; |
| 308 | } |
| 309 | |
| 310 | // No wifi interface found. |
| 311 | return -1; |
| 312 | } |
| 313 | |
| 314 | void TryChangeWifiOptions(int options) { |
| 315 | int previous_options = GetWifiOptions(); |
| 316 | std::unique_ptr<ScopedWifiOptions> scoped_options = SetWifiOptions(options); |
| 317 | EXPECT_EQ(previous_options | options, GetWifiOptions()); |
| 318 | scoped_options.reset(); |
| 319 | EXPECT_EQ(previous_options, GetWifiOptions()); |
| 320 | } |
| 321 | |
| 322 | // Test SetWifiOptions(). |
| 323 | TEST(NetworkInterfacesTest, SetWifiOptions) { |
| 324 | TryChangeWifiOptions(0); |
| 325 | TryChangeWifiOptions(WIFI_OPTIONS_DISABLE_SCAN); |
| 326 | TryChangeWifiOptions(WIFI_OPTIONS_MEDIA_STREAMING_MODE); |
| 327 | TryChangeWifiOptions(WIFI_OPTIONS_DISABLE_SCAN | |
| 328 | WIFI_OPTIONS_MEDIA_STREAMING_MODE); |
| 329 | } |
| 330 | |
| 331 | } // namespace |
| 332 | |
| 333 | } // namespace net |