blob: 625d71aede19219f8173b8c97f63bd15f3960acf [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/network_interfaces_win.h"
#include <iphlpapi.h>
#include <objbase.h>
#include <ostream>
#include <string>
#include <unordered_set>
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "net/base/ip_endpoint.h"
#include "starboard/types.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
static const char kIfnameEm1[] = "em1";
static const char kIfnameVmnet[] = "VMnet";
static const unsigned char kIPv6LocalAddr[] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1};
static const unsigned char kIPv6Addr[] = {0x24, 0x01, 0xfa, 0x00, 0x00, 0x04,
0x10, 0x00, 0xbe, 0x30, 0x5b, 0xff,
0xfe, 0xe5, 0x00, 0xc3};
static const unsigned char kIPv6AddrPrefix[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Helper function to create a valid IP_ADAPTER_ADDRESSES with reasonable
// default value. The output is the |adapter_address|. All the rests are input
// to fill the |adapter_address|. |sock_addrs| are temporary storage used by
// |adapter_address| once the function is returned.
bool FillAdapterAddress(IP_ADAPTER_ADDRESSES* adapter_address,
const char* ifname,
const IPAddress& ip_address,
const IPAddress& ip_netmask,
sockaddr_storage sock_addrs[2]) {
adapter_address->AdapterName = const_cast<char*>(ifname);
adapter_address->FriendlyName = const_cast<PWCHAR>(L"interface");
adapter_address->IfType = IF_TYPE_ETHERNET_CSMACD;
adapter_address->OperStatus = IfOperStatusUp;
adapter_address->FirstUnicastAddress->DadState = IpDadStatePreferred;
adapter_address->FirstUnicastAddress->PrefixOrigin = IpPrefixOriginOther;
adapter_address->FirstUnicastAddress->SuffixOrigin = IpSuffixOriginOther;
adapter_address->FirstUnicastAddress->PreferredLifetime = 100;
adapter_address->FirstUnicastAddress->ValidLifetime = 1000;
socklen_t sock_len = sizeof(sockaddr_storage);
// Convert to sockaddr for next check.
if (!IPEndPoint(ip_address, 0)
.ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addrs[0]),
&sock_len)) {
return false;
}
adapter_address->FirstUnicastAddress->Address.lpSockaddr =
reinterpret_cast<sockaddr*>(&sock_addrs[0]);
adapter_address->FirstUnicastAddress->Address.iSockaddrLength = sock_len;
adapter_address->FirstUnicastAddress->OnLinkPrefixLength = 1;
sock_len = sizeof(sockaddr_storage);
if (!IPEndPoint(ip_netmask, 0)
.ToSockAddr(reinterpret_cast<sockaddr*>(&sock_addrs[1]),
&sock_len)) {
return false;
}
adapter_address->FirstPrefix->Address.lpSockaddr =
reinterpret_cast<sockaddr*>(&sock_addrs[1]);
adapter_address->FirstPrefix->Address.iSockaddrLength = sock_len;
adapter_address->FirstPrefix->PrefixLength = 1;
DCHECK_EQ(sock_addrs[0].ss_family, sock_addrs[1].ss_family);
if (sock_addrs[0].ss_family == AF_INET6) {
adapter_address->Ipv6IfIndex = 0;
} else {
DCHECK_EQ(sock_addrs[0].ss_family, AF_INET);
adapter_address->IfIndex = 0;
}
return true;
}
TEST(NetworkInterfacesTest, NetworkListTrimmingWindows) {
IPAddress ipv6_local_address(kIPv6LocalAddr);
IPAddress ipv6_address(kIPv6Addr);
IPAddress ipv6_prefix(kIPv6AddrPrefix);
NetworkInterfaceList results;
sockaddr_storage addresses[2];
IP_ADAPTER_ADDRESSES adapter_address = {};
IP_ADAPTER_UNICAST_ADDRESS address = {};
IP_ADAPTER_PREFIX adapter_prefix = {};
adapter_address.FirstUnicastAddress = &address;
adapter_address.FirstPrefix = &adapter_prefix;
// Address of offline links should be ignored.
ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address,
ipv6_prefix, addresses));
adapter_address.OperStatus = IfOperStatusDown;
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 0ul);
// Address on loopback interface should be trimmed out.
ASSERT_TRUE(FillAdapterAddress(
&adapter_address /* adapter_address */, kIfnameEm1 /* ifname */,
ipv6_local_address /* ip_address */, ipv6_prefix /* ip_netmask */,
addresses /* sock_addrs */));
adapter_address.IfType = IF_TYPE_SOFTWARE_LOOPBACK;
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 0ul);
// vmware address should return by default.
ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameVmnet, ipv6_address,
ipv6_prefix, addresses));
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 1ul);
EXPECT_EQ(results[0].name, kIfnameVmnet);
EXPECT_EQ(results[0].prefix_length, 1ul);
EXPECT_EQ(results[0].address, ipv6_address);
EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_NONE);
results.clear();
// vmware address should be trimmed out if policy specified so.
ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameVmnet, ipv6_address,
ipv6_prefix, addresses));
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 0ul);
results.clear();
// Addresses with incomplete DAD should be ignored.
ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address,
ipv6_prefix, addresses));
adapter_address.FirstUnicastAddress->DadState = IpDadStateTentative;
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 0ul);
results.clear();
// Addresses with allowed attribute IpSuffixOriginRandom should be returned
// and attributes should be translated correctly to
// IP_ADDRESS_ATTRIBUTE_TEMPORARY.
ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address,
ipv6_prefix, addresses));
adapter_address.FirstUnicastAddress->PrefixOrigin =
IpPrefixOriginRouterAdvertisement;
adapter_address.FirstUnicastAddress->SuffixOrigin = IpSuffixOriginRandom;
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 1ul);
EXPECT_EQ(results[0].name, kIfnameEm1);
EXPECT_EQ(results[0].prefix_length, 1ul);
EXPECT_EQ(results[0].address, ipv6_address);
EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_TEMPORARY);
results.clear();
// Addresses with preferred lifetime 0 should be returned and
// attributes should be translated correctly to
// IP_ADDRESS_ATTRIBUTE_DEPRECATED.
ASSERT_TRUE(FillAdapterAddress(&adapter_address, kIfnameEm1, ipv6_address,
ipv6_prefix, addresses));
adapter_address.FirstUnicastAddress->PreferredLifetime = 0;
adapter_address.FriendlyName = const_cast<PWCHAR>(L"FriendlyInterfaceName");
EXPECT_TRUE(internal::GetNetworkListImpl(
&results, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES, &adapter_address));
EXPECT_EQ(results.size(), 1ul);
EXPECT_EQ(results[0].friendly_name, "FriendlyInterfaceName");
EXPECT_EQ(results[0].name, kIfnameEm1);
EXPECT_EQ(results[0].prefix_length, 1ul);
EXPECT_EQ(results[0].address, ipv6_address);
EXPECT_EQ(results[0].ip_address_attributes, IP_ADDRESS_ATTRIBUTE_DEPRECATED);
results.clear();
}
bool read_int_or_bool(DWORD data_size, PVOID data) {
switch (data_size) {
case 1:
return !!*reinterpret_cast<uint8_t*>(data);
case 4:
return !!*reinterpret_cast<uint32_t*>(data);
default:
LOG(FATAL) << "That is not a type I know!";
return false;
}
}
int GetWifiOptions() {
const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance();
if (!wlanapi.initialized)
return -1;
internal::WlanHandle client;
DWORD cur_version = 0;
const DWORD kMaxClientVersion = 2;
DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client);
if (result != ERROR_SUCCESS)
return -1;
WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL;
result =
wlanapi.enum_interfaces_func(client.Get(), NULL, &interface_list_ptr);
if (result != ERROR_SUCCESS)
return -1;
std::unique_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter>
interface_list(interface_list_ptr);
for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) {
WLAN_INTERFACE_INFO* info = &interface_list->InterfaceInfo[i];
DWORD data_size;
PVOID data;
int options = 0;
result =
wlanapi.query_interface_func(client.Get(), &info->InterfaceGuid,
wlan_intf_opcode_background_scan_enabled,
NULL, &data_size, &data, NULL);
if (result != ERROR_SUCCESS)
continue;
if (!read_int_or_bool(data_size, data)) {
options |= WIFI_OPTIONS_DISABLE_SCAN;
}
internal::WlanApi::GetInstance().free_memory_func(data);
result = wlanapi.query_interface_func(client.Get(), &info->InterfaceGuid,
wlan_intf_opcode_media_streaming_mode,
NULL, &data_size, &data, NULL);
if (result != ERROR_SUCCESS)
continue;
if (read_int_or_bool(data_size, data)) {
options |= WIFI_OPTIONS_MEDIA_STREAMING_MODE;
}
internal::WlanApi::GetInstance().free_memory_func(data);
// Just the the options from the first succesful
// interface.
return options;
}
// No wifi interface found.
return -1;
}
void TryChangeWifiOptions(int options) {
int previous_options = GetWifiOptions();
std::unique_ptr<ScopedWifiOptions> scoped_options = SetWifiOptions(options);
EXPECT_EQ(previous_options | options, GetWifiOptions());
scoped_options.reset();
EXPECT_EQ(previous_options, GetWifiOptions());
}
// Test SetWifiOptions().
TEST(NetworkInterfacesTest, SetWifiOptions) {
TryChangeWifiOptions(0);
TryChangeWifiOptions(WIFI_OPTIONS_DISABLE_SCAN);
TryChangeWifiOptions(WIFI_OPTIONS_MEDIA_STREAMING_MODE);
TryChangeWifiOptions(WIFI_OPTIONS_DISABLE_SCAN |
WIFI_OPTIONS_MEDIA_STREAMING_MODE);
}
} // namespace
} // namespace net