blob: 7540bbcb64846f8fc3d0494c0e3716b664dced87 [file] [log] [blame]
// Copyright 2013 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 "chromeos/network/network_device_handler_impl.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/dbus/shill/shill_device_client.h"
#include "chromeos/dbus/shill/shill_ipconfig_client.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_state_handler.h"
#include "dbus/object_path.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
std::string GetErrorNameForShillError(const std::string& shill_error_name) {
if (shill_error_name == shill::kErrorResultFailure ||
shill_error_name == shill::kErrorResultInvalidArguments) {
return NetworkDeviceHandler::kErrorFailure;
}
if (shill_error_name == shill::kErrorResultNotSupported)
return NetworkDeviceHandler::kErrorNotSupported;
if (shill_error_name == shill::kErrorResultIncorrectPin)
return NetworkDeviceHandler::kErrorIncorrectPin;
if (shill_error_name == shill::kErrorResultPinBlocked)
return NetworkDeviceHandler::kErrorPinBlocked;
if (shill_error_name == shill::kErrorResultPinRequired)
return NetworkDeviceHandler::kErrorPinRequired;
if (shill_error_name == shill::kErrorResultNotFound)
return NetworkDeviceHandler::kErrorDeviceMissing;
return NetworkDeviceHandler::kErrorUnknown;
}
void GetPropertiesCallback(const std::string& device_path,
network_handler::ResultCallback callback,
base::Optional<base::Value> result) {
if (!result) {
NET_LOG(ERROR) << "GetProperties failed: " << NetworkPathId(device_path);
std::move(callback).Run(device_path, base::nullopt);
return;
}
std::move(callback).Run(device_path, std::move(result));
}
void InvokeErrorCallback(const std::string& device_path,
network_handler::ErrorCallback error_callback,
const std::string& error_name) {
std::string error_msg = "Device Error: " + error_name;
NET_LOG(ERROR) << error_msg << ": " << device_path;
network_handler::RunErrorCallback(std::move(error_callback), device_path,
error_name, error_msg);
}
void HandleShillCallFailure(const std::string& device_path,
network_handler::ErrorCallback error_callback,
const std::string& shill_error_name,
const std::string& shill_error_message) {
network_handler::ShillErrorCallbackFunction(
GetErrorNameForShillError(shill_error_name), device_path,
std::move(error_callback), shill_error_name, shill_error_message);
}
void SetDevicePropertyInternal(const std::string& device_path,
const std::string& property_name,
const base::Value& value,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
NET_LOG(USER) << "Device.SetProperty: " << property_name << " = " << value;
ShillDeviceClient::Get()->SetProperty(
dbus::ObjectPath(device_path), property_name, value, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_path,
std::move(error_callback)));
}
std::unique_ptr<base::DictionaryValue> GetErrorData(const std::string& name) {
auto error_data = std::make_unique<base::DictionaryValue>();
error_data->SetString(network_handler::kErrorName, name);
return error_data;
}
} // namespace
NetworkDeviceHandlerImpl::NetworkDeviceHandlerImpl() = default;
NetworkDeviceHandlerImpl::~NetworkDeviceHandlerImpl() {
if (network_state_handler_)
network_state_handler_->RemoveObserver(this, FROM_HERE);
}
void NetworkDeviceHandlerImpl::GetDeviceProperties(
const std::string& device_path,
network_handler::ResultCallback callback) const {
ShillDeviceClient::Get()->GetProperties(
dbus::ObjectPath(device_path),
base::BindOnce(&GetPropertiesCallback, device_path, std::move(callback)));
}
void NetworkDeviceHandlerImpl::SetDeviceProperty(
const std::string& device_path,
const std::string& property_name,
const base::Value& value,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const char* const property_blocked[] = {
// Must only be changed by policy/owner through.
shill::kCellularAllowRoamingProperty};
for (size_t i = 0; i < base::size(property_blocked); ++i) {
if (property_name == property_blocked[i]) {
InvokeErrorCallback(
device_path, std::move(error_callback),
"SetDeviceProperty called on blocked property " + property_name);
return;
}
}
SetDevicePropertyInternal(device_path, property_name, value,
std::move(callback), std::move(error_callback));
}
void NetworkDeviceHandlerImpl::RegisterCellularNetwork(
const std::string& device_path,
const std::string& network_id,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
NET_LOG(USER) << "Device.RegisterCellularNetwork: " << device_path
<< " Id: " << network_id;
ShillDeviceClient::Get()->Register(
dbus::ObjectPath(device_path), network_id, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_path,
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::RequirePin(
const std::string& device_path,
bool require_pin,
const std::string& pin,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
NET_LOG(USER) << "Device.RequirePin: " << device_path << ": " << require_pin;
ShillDeviceClient::Get()->RequirePin(
dbus::ObjectPath(device_path), pin, require_pin, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_path,
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::EnterPin(
const std::string& device_path,
const std::string& pin,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
NET_LOG(USER) << "Device.EnterPin: " << device_path;
ShillDeviceClient::Get()->EnterPin(
dbus::ObjectPath(device_path), pin, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_path,
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::UnblockPin(
const std::string& device_path,
const std::string& puk,
const std::string& new_pin,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
NET_LOG(USER) << "Device.UnblockPin: " << device_path;
ShillDeviceClient::Get()->UnblockPin(
dbus::ObjectPath(device_path), puk, new_pin, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_path,
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::ChangePin(
const std::string& device_path,
const std::string& old_pin,
const std::string& new_pin,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
NET_LOG(USER) << "Device.ChangePin: " << device_path;
ShillDeviceClient::Get()->ChangePin(
dbus::ObjectPath(device_path), old_pin, new_pin, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_path,
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::SetCellularAllowRoaming(
const bool allow_roaming) {
cellular_allow_roaming_ = allow_roaming;
ApplyCellularAllowRoamingToShill();
}
void NetworkDeviceHandlerImpl::SetMACAddressRandomizationEnabled(
const bool enabled) {
mac_addr_randomization_enabled_ = enabled;
ApplyMACAddressRandomizationToShill();
}
void NetworkDeviceHandlerImpl::SetUsbEthernetMacAddressSource(
const std::string& source) {
if (source == usb_ethernet_mac_address_source_) {
return;
}
usb_ethernet_mac_address_source_ = source;
mac_address_change_not_supported_.clear();
ApplyUsbEthernetMacAddressSourceToShill();
}
void NetworkDeviceHandlerImpl::AddWifiWakeOnPacketConnection(
const net::IPEndPoint& ip_endpoint,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const DeviceState* device_state = GetWifiDeviceState();
if (!device_state) {
if (error_callback) {
std::move(error_callback)
.Run(kErrorDeviceMissing, GetErrorData(kErrorDeviceMissing));
}
return;
}
NET_LOG(USER) << "Device.AddWakeOnWifi: " << device_state->path();
ShillDeviceClient::Get()->AddWakeOnPacketConnection(
dbus::ObjectPath(device_state->path()), ip_endpoint, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_state->path(),
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::AddWifiWakeOnPacketOfTypes(
const std::vector<std::string>& types,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const DeviceState* device_state = GetWifiDeviceState();
if (!device_state) {
if (error_callback) {
std::move(error_callback)
.Run(kErrorDeviceMissing, GetErrorData(kErrorDeviceMissing));
}
return;
}
NET_LOG(USER) << "Device.AddWifiWakeOnPacketOfTypes: " << device_state->path()
<< " Types: " << base::JoinString(types, " ");
ShillDeviceClient::Get()->AddWakeOnPacketOfTypes(
dbus::ObjectPath(device_state->path()), types, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_state->path(),
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::RemoveWifiWakeOnPacketConnection(
const net::IPEndPoint& ip_endpoint,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const DeviceState* device_state = GetWifiDeviceState();
if (!device_state) {
if (error_callback) {
std::move(error_callback)
.Run(kErrorDeviceMissing, GetErrorData(kErrorDeviceMissing));
}
return;
}
NET_LOG(USER) << "Device.RemoveWakeOnWifi: " << device_state->path();
ShillDeviceClient::Get()->RemoveWakeOnPacketConnection(
dbus::ObjectPath(device_state->path()), ip_endpoint, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_state->path(),
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::RemoveWifiWakeOnPacketOfTypes(
const std::vector<std::string>& types,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const DeviceState* device_state = GetWifiDeviceState();
if (!device_state) {
if (error_callback) {
std::move(error_callback)
.Run(kErrorDeviceMissing, GetErrorData(kErrorDeviceMissing));
}
return;
}
NET_LOG(USER) << "Device.RemoveWifiWakeOnPacketOfTypes: "
<< device_state->path()
<< " Types: " << base::JoinString(types, " ");
ShillDeviceClient::Get()->RemoveWakeOnPacketOfTypes(
dbus::ObjectPath(device_state->path()), types, std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_state->path(),
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::RemoveAllWifiWakeOnPacketConnections(
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const DeviceState* device_state = GetWifiDeviceState();
if (!device_state) {
if (error_callback) {
std::move(error_callback)
.Run(kErrorDeviceMissing, GetErrorData(kErrorDeviceMissing));
}
return;
}
NET_LOG(USER) << "Device.RemoveAllWakeOnWifi: " << device_state->path();
ShillDeviceClient::Get()->RemoveAllWakeOnPacketConnections(
dbus::ObjectPath(device_state->path()), std::move(callback),
base::BindOnce(&HandleShillCallFailure, device_state->path(),
std::move(error_callback)));
}
void NetworkDeviceHandlerImpl::DeviceListChanged() {
ApplyCellularAllowRoamingToShill();
ApplyMACAddressRandomizationToShill();
ApplyUsbEthernetMacAddressSourceToShill();
}
void NetworkDeviceHandlerImpl::DevicePropertiesUpdated(
const DeviceState* device) {
ApplyUsbEthernetMacAddressSourceToShill();
}
void NetworkDeviceHandlerImpl::Init(
NetworkStateHandler* network_state_handler) {
DCHECK(network_state_handler);
network_state_handler_ = network_state_handler;
network_state_handler_->AddObserver(this, FROM_HERE);
}
void NetworkDeviceHandlerImpl::ApplyCellularAllowRoamingToShill() {
NetworkStateHandler::DeviceStateList list;
network_state_handler_->GetDeviceListByType(NetworkTypePattern::Cellular(),
&list);
if (list.empty()) {
NET_LOG(DEBUG) << "No cellular device available. Roaming is only supported "
"by cellular devices.";
return;
}
for (NetworkStateHandler::DeviceStateList::const_iterator it = list.begin();
it != list.end(); ++it) {
const DeviceState* device_state = *it;
bool current_allow_roaming = device_state->allow_roaming();
// If roaming is required by the provider, always try to set to true.
bool new_device_value =
device_state->provider_requires_roaming() || cellular_allow_roaming_;
// Only set the value if the current value is different from
// |new_device_value|.
if (new_device_value == current_allow_roaming)
continue;
SetDevicePropertyInternal(device_state->path(),
shill::kCellularAllowRoamingProperty,
base::Value(new_device_value), base::DoNothing(),
network_handler::ErrorCallback());
}
}
void NetworkDeviceHandlerImpl::ApplyMACAddressRandomizationToShill() {
const DeviceState* device_state = GetWifiDeviceState();
if (!device_state) {
// We'll need to ask if this is supported when we find a Wi-Fi
// device.
mac_addr_randomization_supported_ =
MACAddressRandomizationSupport::NOT_REQUESTED;
return;
}
switch (mac_addr_randomization_supported_) {
case MACAddressRandomizationSupport::NOT_REQUESTED:
GetDeviceProperties(
device_state->path(),
base::BindOnce(
&NetworkDeviceHandlerImpl::HandleMACAddressRandomization,
weak_ptr_factory_.GetWeakPtr()));
return;
case MACAddressRandomizationSupport::SUPPORTED:
SetDevicePropertyInternal(
device_state->path(), shill::kMacAddressRandomizationEnabledProperty,
base::Value(mac_addr_randomization_enabled_), base::DoNothing(),
network_handler::ErrorCallback());
return;
case MACAddressRandomizationSupport::UNSUPPORTED:
return;
}
}
void NetworkDeviceHandlerImpl::ApplyUsbEthernetMacAddressSourceToShill() {
// Do nothing else if MAC address source is not specified yet.
if (usb_ethernet_mac_address_source_.empty()) {
NET_LOG(DEBUG) << "Empty USB Ethernet MAC address source.";
return;
}
UpdatePrimaryEnabledUsbEthernetDevice();
ResetMacAddressSourceForSecondaryUsbEthernetDevices();
const DeviceState* primary_enabled_usb_ethernet_device_state =
network_state_handler_->GetDeviceState(
primary_enabled_usb_ethernet_device_path_);
// Do nothing else if device path is empty or device state is nullptr or
// device MAC address source property equals to needed value.
if (primary_enabled_usb_ethernet_device_path_.empty() ||
!primary_enabled_usb_ethernet_device_state ||
primary_enabled_usb_ethernet_device_state->mac_address_source() ==
usb_ethernet_mac_address_source_) {
return;
}
ShillDeviceClient::Get()->SetUsbEthernetMacAddressSource(
dbus::ObjectPath(primary_enabled_usb_ethernet_device_path_),
usb_ethernet_mac_address_source_, base::DoNothing(),
base::BindOnce(
&NetworkDeviceHandlerImpl::OnSetUsbEthernetMacAddressSourceError,
weak_ptr_factory_.GetWeakPtr(),
primary_enabled_usb_ethernet_device_path_,
primary_enabled_usb_ethernet_device_state->mac_address(),
usb_ethernet_mac_address_source_, network_handler::ErrorCallback()));
}
void NetworkDeviceHandlerImpl::OnSetUsbEthernetMacAddressSourceError(
const std::string& device_path,
const std::string& device_mac_address,
const std::string& mac_address_source,
network_handler::ErrorCallback error_callback,
const std::string& shill_error_name,
const std::string& shill_error_message) {
HandleShillCallFailure(device_path, std::move(error_callback),
shill_error_name, shill_error_message);
if (shill_error_name == shill::kErrorResultNotSupported &&
mac_address_source == usb_ethernet_mac_address_source_) {
mac_address_change_not_supported_.insert(device_mac_address);
ApplyUsbEthernetMacAddressSourceToShill();
}
}
bool NetworkDeviceHandlerImpl::IsUsbEnabledDevice(
const DeviceState* device_state) const {
return device_state && device_state->link_up() &&
device_state->Matches(NetworkTypePattern::Ethernet()) &&
device_state->device_bus_type() == shill::kDeviceBusTypeUsb &&
mac_address_change_not_supported_.find(device_state->mac_address()) ==
mac_address_change_not_supported_.end();
}
void NetworkDeviceHandlerImpl::UpdatePrimaryEnabledUsbEthernetDevice() {
NetworkStateHandler::DeviceStateList device_state_list;
network_state_handler_->GetDeviceListByType(NetworkTypePattern::Ethernet(),
&device_state_list);
// Try to avoid situation when both PCI and USB Ethernet devices are enabled
// and have the same MAC address. In this situation we will change back USB
// Ethernet MAC address.
if (usb_ethernet_mac_address_source_ ==
shill::kUsbEthernetMacAddressSourceBuiltinAdapterMac) {
for (const auto* device_state : device_state_list) {
if (device_state && device_state->link_up() &&
device_state->device_bus_type() == shill::kDeviceBusTypePci) {
primary_enabled_usb_ethernet_device_path_ = "";
return;
}
}
}
// Nothing change, primary USB Ethernet device still enabled.
// Note that if |primary_enabled_usb_ethernet_device_path_| is empty, this
// will be IsUsbEnabledDevice(nullptr) which returns false.
if (IsUsbEnabledDevice(network_state_handler_->GetDeviceState(
primary_enabled_usb_ethernet_device_path_))) {
return;
}
// Reset primary enabled USB Ethernet device since it isn't enabled anymore.
primary_enabled_usb_ethernet_device_path_ = "";
// Give the priority to USB Ethernet device which already has the required MAC
// address source property. It can happen after Chrome crashes, when shill
// devices have some properties and Chrome does not know which device was
// the primary USB Ethernet before the crash.
for (const auto* device_state : device_state_list) {
if (IsUsbEnabledDevice(device_state) && device_state &&
device_state->mac_address_source() ==
usb_ethernet_mac_address_source_) {
primary_enabled_usb_ethernet_device_path_ = device_state->path();
return;
}
}
for (const auto* device_state : device_state_list) {
if (IsUsbEnabledDevice(device_state)) {
primary_enabled_usb_ethernet_device_path_ = device_state->path();
return;
}
}
}
void NetworkDeviceHandlerImpl::
ResetMacAddressSourceForSecondaryUsbEthernetDevices() const {
NetworkStateHandler::DeviceStateList device_state_list;
network_state_handler_->GetDeviceListByType(NetworkTypePattern::Ethernet(),
&device_state_list);
for (const auto* device_state : device_state_list) {
if (!device_state ||
device_state->path() == primary_enabled_usb_ethernet_device_path_ ||
device_state->mac_address_source().empty() ||
device_state->mac_address_source() ==
shill::kUsbEthernetMacAddressSourceUsbAdapterMac) {
continue;
}
ShillDeviceClient::Get()->SetUsbEthernetMacAddressSource(
dbus::ObjectPath(device_state->path()),
shill::kUsbEthernetMacAddressSourceUsbAdapterMac, base::DoNothing(),
base::BindOnce(&HandleShillCallFailure, device_state->path(),
network_handler::ErrorCallback()));
}
}
void NetworkDeviceHandlerImpl::HandleMACAddressRandomization(
const std::string& device_path,
base::Optional<base::Value> properties) {
if (!properties)
return;
base::Optional<bool> supported =
properties->FindBoolKey(shill::kMacAddressRandomizationSupportedProperty);
if (!supported.has_value()) {
if (base::SysInfo::IsRunningOnChromeOS()) {
NET_LOG(ERROR) << "Failed to determine if device " << device_path
<< " supports MAC address randomization";
}
return;
}
// Try to set MAC address randomization if it's supported.
if (*supported) {
mac_addr_randomization_supported_ =
MACAddressRandomizationSupport::SUPPORTED;
ApplyMACAddressRandomizationToShill();
} else {
mac_addr_randomization_supported_ =
MACAddressRandomizationSupport::UNSUPPORTED;
}
}
const DeviceState* NetworkDeviceHandlerImpl::GetWifiDeviceState() {
return network_state_handler_->GetDeviceStateByType(
NetworkTypePattern::WiFi());
}
} // namespace chromeos