| // Copyright 2018 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/device.h" |
| |
| #include <errno.h> |
| #include <netinet/in.h> |
| #include <linux/if.h> // NOLINT - Needs definitions from netinet/in.h |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/param.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <set> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/containers/contains.h> |
| #include <base/files/file_util.h> |
| #include <base/functional/bind.h> |
| #include <base/logging.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/notreached.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/time/time.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <net-base/rtnl_handler.h> |
| |
| #include "shill/control_interface.h" |
| #include "shill/error.h" |
| #include "shill/event_dispatcher.h" |
| #include "shill/logging.h" |
| #include "shill/manager.h" |
| #include "shill/metrics.h" |
| #include "shill/network/dhcp_controller.h" |
| #include "shill/network/dhcp_provider.h" |
| #include "shill/network/network.h" |
| #include "shill/refptr_types.h" |
| #include "shill/service.h" |
| #include "shill/store/property_accessor.h" |
| #include "shill/store/store_interface.h" |
| #include "shill/technology.h" |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kDevice; |
| static std::string ObjectID(const Device* d) { |
| return d->GetRpcIdentifier().value(); |
| } |
| } // namespace Logging |
| |
| namespace { |
| |
| constexpr size_t kHardwareAddressLength = 6; |
| |
| Service::ConnectState PortalValidationStateToConnectionState( |
| PortalDetector::ValidationState validation_state) { |
| switch (validation_state) { |
| case PortalDetector::ValidationState::kInternetConnectivity: |
| return Service::kStateOnline; |
| case PortalDetector::ValidationState::kNoConnectivity: |
| return Service::kStateNoConnectivity; |
| case PortalDetector::ValidationState::kPortalSuspected: |
| return Service::kStatePortalSuspected; |
| case PortalDetector::ValidationState::kPortalRedirect: |
| return Service::kStateRedirectFound; |
| } |
| } |
| } // namespace |
| |
| const char Device::kStoragePowered[] = "Powered"; |
| |
| Device::Device(Manager* manager, |
| const std::string& link_name, |
| const std::string& mac_address, |
| int interface_index, |
| Technology technology, |
| bool fixed_ip_params) |
| : enabled_(false), |
| enabled_persistent_(true), |
| enabled_pending_(enabled_), |
| mac_address_(base::ToLowerASCII(mac_address)), |
| interface_index_(interface_index), |
| link_name_(link_name), |
| manager_(manager), |
| adaptor_(manager->control_interface()->CreateDeviceAdaptor(this)), |
| technology_(technology), |
| rtnl_handler_(net_base::RTNLHandler::GetInstance()), |
| traffic_counter_callback_id_(0), |
| weak_ptr_factory_(this) { |
| store_.RegisterConstString(kAddressProperty, &mac_address_); |
| |
| // kBgscanMethodProperty: Registered in WiFi |
| // kBgscanShortIntervalProperty: Registered in WiFi |
| // kBgscanSignalThresholdProperty: Registered in WiFi |
| |
| // kCellularAllowRoamingProperty: Registered in Cellular |
| // kEsnProperty: Registered in Cellular |
| // kHomeProviderProperty: Registered in Cellular |
| // kImeiProperty: Registered in Cellular |
| // kIccidProperty: Registered in Cellular |
| // kImsiProperty: Registered in Cellular |
| // kInhibit: Registered in Cellular |
| // kManufacturerProperty: Registered in Cellular |
| // kMdnProperty: Registered in Cellular |
| // kMeidProperty: Registered in Cellular |
| // kMinProperty: Registered in Cellular |
| // kModelIdProperty: Registered in Cellular |
| // kFirmwareRevisionProperty: Registered in Cellular |
| // kHardwareRevisionProperty: Registered in Cellular |
| // kDeviceIdProperty: Registered in Cellular |
| // kSIMLockStatusProperty: Registered in Cellular |
| // kFoundNetworksProperty: Registered in Cellular |
| // kDBusObjectProperty: Register in Cellular |
| // kPrimaryMultiplexedInterfaceProperty: Registered in Cellular |
| |
| store_.RegisterConstString(kInterfaceProperty, &link_name_); |
| HelpRegisterConstDerivedRpcIdentifier( |
| kSelectedServiceProperty, &Device::GetSelectedServiceRpcIdentifier); |
| HelpRegisterConstDerivedRpcIdentifiers(kIPConfigsProperty, |
| &Device::AvailableIPConfigs); |
| store_.RegisterConstString(kNameProperty, &link_name_); |
| store_.RegisterConstBool(kPoweredProperty, &enabled_); |
| HelpRegisterConstDerivedString(kTypeProperty, &Device::GetTechnologyString); |
| |
| CreateImplicitNetwork(fixed_ip_params); |
| |
| // kScanningProperty: Registered in WiFi, Cellular |
| // kScanIntervalProperty: Registered in WiFi, Cellular |
| // kWakeOnWiFiFeaturesEnabledProperty: Registered in WiFi |
| |
| SLOG(this, 1) << "Device(): " << link_name_ << " index: " << interface_index_; |
| } |
| |
| Device::~Device() { |
| LOG(INFO) << "~Device(): " << link_name_ << " index: " << interface_index_; |
| if (implicit_network_) { |
| implicit_network_->UnregisterEventHandler(this); |
| } |
| } |
| |
| void Device::CreateImplicitNetwork(bool fixed_ip_params) { |
| implicit_network_ = std::make_unique<Network>( |
| interface_index_, link_name_, technology_, fixed_ip_params, |
| manager_->control_interface(), manager_->dispatcher(), |
| manager_->metrics(), NetworkApplier::GetInstance()); |
| implicit_network_->RegisterEventHandler(this); |
| } |
| |
| void Device::Initialize() { |
| SLOG(this, 2) << "Initialized"; |
| } |
| |
| void Device::LinkEvent(unsigned flags, unsigned change) { |
| SLOG(this, 2) << base::StringPrintf("Device %s flags 0x%x changed 0x%x", |
| link_name_.c_str(), flags, change); |
| } |
| |
| void Device::Scan(Error* error, const std::string& reason, bool is_dbus_call) { |
| SLOG(this, 2) << __func__ << " [Device] on " << link_name() << " from " |
| << reason << (is_dbus_call ? " D-Bus call" : ""); |
| Error::PopulateAndLog(FROM_HERE, error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement Scan"); |
| } |
| |
| void Device::RegisterOnNetwork(const std::string& /*network_id*/, |
| ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog( |
| FROM_HERE, &error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement RegisterOnNetwork"); |
| std::move(callback).Run(error); |
| } |
| |
| void Device::RequirePin(const std::string& /*pin*/, |
| bool /*require*/, |
| ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog( |
| FROM_HERE, &error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement RequirePin"); |
| std::move(callback).Run(error); |
| } |
| |
| void Device::EnterPin(const std::string& /*pin*/, ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog( |
| FROM_HERE, &error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement EnterPin"); |
| std::move(callback).Run(error); |
| } |
| |
| void Device::UnblockPin(const std::string& /*unblock_code*/, |
| const std::string& /*pin*/, |
| ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog( |
| FROM_HERE, &error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement UnblockPin"); |
| std::move(callback).Run(error); |
| } |
| |
| void Device::ChangePin(const std::string& /*old_pin*/, |
| const std::string& /*new_pin*/, |
| ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog( |
| FROM_HERE, &error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement ChangePin"); |
| std::move(callback).Run(error); |
| } |
| |
| void Device::Reset(ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog( |
| FROM_HERE, &error, Error::kNotImplemented, |
| GetTechnologyName() + " device doesn't implement Reset"); |
| std::move(callback).Run(error); |
| } |
| |
| bool Device::IsConnected() const { |
| if (selected_service_) |
| return selected_service_->IsConnected(); |
| return false; |
| } |
| |
| bool Device::IsConnectedToService(const ServiceRefPtr& service) const { |
| return service == selected_service_ && IsConnected(); |
| } |
| |
| void Device::OnSelectedServiceChanged(const ServiceRefPtr&) {} |
| |
| const RpcIdentifier& Device::GetRpcIdentifier() const { |
| return adaptor_->GetRpcIdentifier(); |
| } |
| |
| std::string Device::GetStorageIdentifier() const { |
| return "device_" + DeviceStorageSuffix(); |
| } |
| |
| void Device::UpdateGeolocationObjects( |
| std::vector<GeolocationInfo>* geolocation_infos) const {} |
| |
| std::string Device::GetTechnologyName() const { |
| return TechnologyName(technology()); |
| } |
| |
| std::string Device::GetTechnologyString(Error* /*error*/) { |
| return GetTechnologyName(); |
| } |
| |
| const std::string& Device::UniqueName() const { |
| return link_name_; |
| } |
| |
| Network* Device::GetPrimaryNetwork() const { |
| // Return the implicit Network. The implementation of GetPrimaryNetwork() in |
| // the base Device class always expects that the implicit Network exists. |
| // Subclasses not using the implicit network should provide their own |
| // GetPrimaryNetwork() override as well. |
| CHECK(implicit_network_); |
| return implicit_network_.get(); |
| } |
| |
| bool Device::IsEventOnPrimaryNetwork(int interface_index) { |
| // The interface associated to the primary network may be different than the |
| // interface associated to the device when it was created (e.g. for Cellular |
| // devices using a multiplexed virtual network interface). |
| return (GetPrimaryNetwork() && |
| GetPrimaryNetwork()->interface_index() == interface_index); |
| } |
| |
| bool Device::Load(const StoreInterface* storage) { |
| const auto id = GetStorageIdentifier(); |
| if (!storage->ContainsGroup(id)) { |
| SLOG(this, 2) << "Device is not available in the persistent store: " << id; |
| return false; |
| } |
| enabled_persistent_ = true; |
| storage->GetBool(id, kStoragePowered, &enabled_persistent_); |
| return true; |
| } |
| |
| bool Device::Save(StoreInterface* storage) { |
| const auto id = GetStorageIdentifier(); |
| storage->SetBool(id, kStoragePowered, enabled_persistent_); |
| return true; |
| } |
| |
| void Device::OnBeforeSuspend(ResultCallback callback) { |
| // Nothing to be done in the general case, so immediately report success. |
| std::move(callback).Run(Error(Error::kSuccess)); |
| } |
| |
| void Device::OnAfterResume() { |
| ForceIPConfigUpdate(); |
| } |
| |
| void Device::OnDarkResume(ResultCallback callback) { |
| // Nothing to be done in the general case, so immediately report success. |
| std::move(callback).Run(Error(Error::kSuccess)); |
| } |
| |
| void Device::DropConnection() { |
| // The implementation of DropConnection() in the base Device class |
| // always stops the implicit network associated to the device. |
| // Subclasses not using the implicit network should provide their own |
| // DropConnection() override as well. |
| SLOG(this, 2) << __func__; |
| CHECK(implicit_network_); |
| implicit_network_->Stop(); |
| SelectService(nullptr); |
| } |
| |
| void Device::SetUsbEthernetMacAddressSource(const std::string& source, |
| ResultCallback callback) { |
| Error error; |
| Error::PopulateAndLog(FROM_HERE, &error, Error::kNotImplemented, |
| "SetUsbEthernetMacAddressSource from source " + source + |
| " is not implemented for " + GetTechnologyName() + |
| " device on " + link_name_ + "."); |
| std::move(callback).Run(error); |
| } |
| |
| void Device::ForceIPConfigUpdate() { |
| SLOG(this, 2) << __func__; |
| if (!IsConnected()) { |
| return; |
| } |
| // When already connected, a Network must exist. |
| CHECK(GetPrimaryNetwork()); |
| LOG(INFO) << LoggingTag() << ": forced IP config update"; |
| GetPrimaryNetwork()->RenewDHCPLease(); |
| GetPrimaryNetwork()->InvalidateIPv6Config(); |
| } |
| |
| void Device::FetchTrafficCounters(const ServiceRefPtr& old_service, |
| const ServiceRefPtr& new_service) { |
| std::set<std::string> devices{link_name_}; |
| patchpanel::Client* client = manager_->patchpanel_client(); |
| if (!client) { |
| return; |
| } |
| traffic_counter_callback_id_++; |
| traffic_counters_callback_map_[traffic_counter_callback_id_] = |
| base::BindOnce(&Device::GetTrafficCountersCallback, AsWeakPtr(), |
| old_service, new_service); |
| client->GetTrafficCounters( |
| devices, base::BindOnce(&Device::GetTrafficCountersPatchpanelCallback, |
| AsWeakPtr(), traffic_counter_callback_id_)); |
| } |
| |
| void Device::OnNeighborReachabilityEvent( |
| int interface_index, |
| const net_base::IPAddress& ip_address, |
| patchpanel::Client::NeighborRole role, |
| patchpanel::Client::NeighborStatus status) { |
| // Does nothing in the general case. |
| } |
| |
| void Device::HelpRegisterConstDerivedString( |
| std::string_view name, std::string (Device::*get)(Error* error)) { |
| store_.RegisterDerivedString( |
| name, StringAccessor( |
| new CustomAccessor<Device, std::string>(this, get, nullptr))); |
| } |
| |
| void Device::HelpRegisterConstDerivedRpcIdentifier( |
| std::string_view name, RpcIdentifier (Device::*get)(Error* error)) { |
| store_.RegisterDerivedRpcIdentifier( |
| name, RpcIdentifierAccessor( |
| new CustomAccessor<Device, RpcIdentifier>(this, get, nullptr))); |
| } |
| |
| void Device::HelpRegisterConstDerivedRpcIdentifiers( |
| std::string_view name, RpcIdentifiers (Device::*get)(Error*)) { |
| store_.RegisterDerivedRpcIdentifiers( |
| name, RpcIdentifiersAccessor(new CustomAccessor<Device, RpcIdentifiers>( |
| this, get, nullptr))); |
| } |
| |
| void Device::HelpRegisterConstDerivedUint64(std::string_view name, |
| uint64_t (Device::*get)(Error*)) { |
| store_.RegisterDerivedUint64( |
| name, |
| Uint64Accessor(new CustomAccessor<Device, uint64_t>(this, get, nullptr))); |
| } |
| |
| void Device::OnConnectionUpdated(int interface_index) { |
| if (!IsEventOnPrimaryNetwork(interface_index) || !selected_service_) { |
| return; |
| } |
| |
| // If the service is already disconnecting, ignore any update from Network to |
| // avoid disrupting the disconnection procedure. |
| if (selected_service_->IsDisconnecting()) { |
| return; |
| } |
| |
| // If the service is already in a Connected state (this happens during a roam |
| // or DHCP renewal), transitioning back to Connected isn't productive. Avoid |
| // this transition entirely and wait for portal detection to transition us to |
| // a more informative state (either Online or some portalled state). Instead, |
| // set RoamState so that clients that care about the Service's state are still |
| // able to track it. |
| if (!selected_service_->IsConnected()) { |
| // Setting Service.State to Connected resets RoamState. |
| SetServiceState(Service::kStateConnected); |
| } else { |
| // We set RoamState here to reflect the actual state of the Service during a |
| // roam. This way, we can keep Service.State at Online or a portalled state |
| // to preserve the service sort order. Note that this can be triggered by a |
| // DHCP renewal that's not a result of a roam as well, but it won't do |
| // anything in non-WiFi Services. |
| selected_service_->SetRoamState(Service::kRoamStateConnected); |
| } |
| OnConnected(); |
| |
| // Subtle: Start portal detection after transitioning the service to the |
| // Connected state because this call may immediately transition to the Online |
| // state. Always ignore any on-going portal detection such that the latest |
| // network layer properties are used to restart portal detection. This ensures |
| // that network validation over IPv4 is prioritized on dual stack networks |
| // when IPv4 provisioning completes after IPv6 provisioning. Note that |
| // currently SetupConnection() is never called a second time if IPv6 |
| // provisioning completes after IPv4 provisioning. |
| UpdatePortalDetector(Network::ValidationReason::kNetworkConnectionUpdate); |
| } |
| |
| void Device::OnNetworkStopped(int interface_index, bool is_failure) { |
| if (!IsEventOnPrimaryNetwork(interface_index) || !is_failure) { |
| return; |
| } |
| OnIPConfigFailure(); |
| } |
| |
| void Device::OnGetDHCPLease(int interface_index) {} |
| void Device::OnGetDHCPFailure(int interface_index) {} |
| void Device::OnGetSLAACAddress(int interface_index) {} |
| void Device::OnNetworkValidationStart(int interface_index) {} |
| void Device::OnNetworkValidationStop(int interface_index) {} |
| void Device::OnNetworkValidationSuccess() {} |
| void Device::OnNetworkValidationFailure() {} |
| void Device::OnIPv4ConfiguredWithDHCPLease(int interface_index) {} |
| void Device::OnIPv6ConfiguredWithSLAACAddress(int interface_index) {} |
| void Device::OnNetworkDestroyed(int interface_index) {} |
| |
| void Device::OnIPConfigFailure() { |
| if (selected_service_) { |
| Error error; |
| selected_service_->DisconnectWithFailure(Service::kFailureDHCP, &error, |
| __func__); |
| } |
| } |
| |
| void Device::OnConnected() {} |
| |
| void Device::GetTrafficCountersCallback( |
| const ServiceRefPtr& old_service, |
| const ServiceRefPtr& new_service, |
| const std::vector<patchpanel::Client::TrafficCounter>& counters) { |
| if (old_service) { |
| old_service->RefreshTrafficCounters(counters); |
| } |
| if (new_service) { |
| // Update the snapshot values, which will be used in future refreshes to |
| // diff against the counter values. Snapshot must be initialized before |
| // layer 3 configuration to ensure that we capture all traffic for the |
| // service. |
| new_service->InitializeTrafficCounterSnapshot(counters); |
| } |
| } |
| |
| void Device::GetTrafficCountersPatchpanelCallback( |
| unsigned int id, |
| const std::vector<patchpanel::Client::TrafficCounter>& counters) { |
| auto iter = traffic_counters_callback_map_.find(id); |
| if (iter == traffic_counters_callback_map_.end() || iter->second.is_null()) { |
| LOG(ERROR) << LoggingTag() << ": No callback found for ID " << id; |
| return; |
| } |
| if (counters.empty()) { |
| LOG(WARNING) << LoggingTag() << ": No counters found"; |
| } |
| auto callback = std::move(iter->second); |
| traffic_counters_callback_map_.erase(iter); |
| std::move(callback).Run(counters); |
| } |
| |
| void Device::SelectService(const ServiceRefPtr& service, |
| bool reset_old_service_state) { |
| SLOG(this, 2) << __func__ << ": service " |
| << (service ? service->log_name() : "*reset*") << " on " |
| << link_name_; |
| |
| if (selected_service_.get() == service.get()) { |
| // No change to |selected_service_|. Return early to avoid |
| // changing its state. |
| return; |
| } |
| |
| ServiceRefPtr old_service; |
| if (selected_service_) { |
| old_service = selected_service_; |
| if (reset_old_service_state && |
| selected_service_->state() != Service::kStateFailure) { |
| SetServiceState(Service::kStateIdle); |
| } |
| selected_service_->DetachNetwork(); |
| } |
| |
| selected_service_ = service; |
| |
| ResetServiceAttachedNetwork(); |
| |
| OnSelectedServiceChanged(old_service); |
| FetchTrafficCounters(old_service, selected_service_); |
| adaptor_->EmitRpcIdentifierChanged(kSelectedServiceProperty, |
| GetSelectedServiceRpcIdentifier(nullptr)); |
| } |
| |
| void Device::ResetServiceAttachedNetwork() { |
| if (selected_service_) { |
| auto primary_network = GetPrimaryNetwork(); |
| CHECK(primary_network); |
| primary_network->set_logging_tag(LoggingTag()); |
| selected_service_->AttachNetwork(primary_network->AsWeakPtr()); |
| } |
| } |
| |
| void Device::SetServiceState(Service::ConnectState state) { |
| if (selected_service_) { |
| selected_service_->SetState(state); |
| } |
| } |
| |
| void Device::SetServiceFailure(Service::ConnectFailure failure_state) { |
| if (selected_service_) { |
| selected_service_->SetFailure(failure_state); |
| } |
| } |
| |
| void Device::SetServiceFailureSilent(Service::ConnectFailure failure_state) { |
| if (selected_service_) { |
| selected_service_->SetFailureSilent(failure_state); |
| } |
| } |
| |
| bool Device::UpdatePortalDetector(Network::ValidationReason reason) { |
| SLOG(this, 1) << LoggingTag() << ": " << __func__ << " reason=" << reason; |
| |
| if (!selected_service_) { |
| LOG(INFO) << LoggingTag() << ": Skipping portal detection: no Service"; |
| return false; |
| } |
| |
| // Do not run portal detection unless in a connected state (i.e. connected, |
| // online, or portalled). |
| if (!selected_service_->IsConnected()) { |
| LOG(INFO) << LoggingTag() |
| << ": Skipping portal detection: Service is not connected"; |
| return false; |
| } |
| |
| // When already connected, a Network must exist. |
| DCHECK(GetPrimaryNetwork()); |
| |
| // If portal detection is disabled for this technology, immediately set |
| // the service state to "Online" and stop portal detection if it was |
| // running. |
| if (selected_service_->IsPortalDetectionDisabled()) { |
| LOG(INFO) << LoggingTag() |
| << ": Portal detection is disabled for this service"; |
| GetPrimaryNetwork()->StopPortalDetection(); |
| SetServiceState(Service::kStateOnline); |
| return false; |
| } |
| |
| if (!GetPrimaryNetwork()->StartPortalDetection(reason)) { |
| SetServiceState(Service::kStateNoConnectivity); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void Device::EmitMACAddress(const std::string& mac_address) { |
| // TODO(b/245984500): What about MAC changed by the supplicant? |
| if (mac_address.empty() || |
| MakeHardwareAddressFromString(mac_address).empty()) { |
| adaptor_->EmitStringChanged(kAddressProperty, mac_address_); |
| } else { |
| adaptor_->EmitStringChanged(kAddressProperty, mac_address); |
| } |
| } |
| |
| void Device::set_mac_address(const std::string& mac_address) { |
| mac_address_ = mac_address; |
| EmitMACAddress(); |
| } |
| |
| void Device::OnNetworkValidationResult(int interface_index, |
| const PortalDetector::Result& result) { |
| if (!IsEventOnPrimaryNetwork(interface_index)) { |
| return; |
| } |
| |
| if (!selected_service_) { |
| // A race can happen if the Service has disconnected in the meantime. |
| LOG(WARNING) |
| << LoggingTag() << ": " |
| << "Portal detection completed but no selected service exists."; |
| return; |
| } |
| |
| if (!selected_service_->IsConnected()) { |
| // A race can happen if the Service is currently disconnecting. |
| LOG(WARNING) << LoggingTag() << ": " |
| << "Portal detection completed but selected service is in " |
| "non-connected state."; |
| return; |
| } |
| |
| // When already connected, a Network must exist. |
| DCHECK(GetPrimaryNetwork()); |
| |
| auto validation_state = result.GetValidationState(); |
| Service::ConnectState connection_state = |
| PortalValidationStateToConnectionState(validation_state); |
| if (validation_state == |
| PortalDetector::ValidationState::kInternetConnectivity) { |
| OnNetworkValidationSuccess(); |
| } else { |
| OnNetworkValidationFailure(); |
| if (!GetPrimaryNetwork()->RestartPortalDetection()) { |
| connection_state = Service::kStateNoConnectivity; |
| } |
| } |
| |
| SetServiceState(connection_state); |
| } |
| |
| RpcIdentifier Device::GetSelectedServiceRpcIdentifier(Error* /*error*/) { |
| if (!selected_service_) { |
| return RpcIdentifier("/"); |
| } |
| return selected_service_->GetRpcIdentifier(); |
| } |
| |
| RpcIdentifiers Device::AvailableIPConfigs(Error* /*error*/) { |
| RpcIdentifiers identifiers; |
| |
| // These available IPConfigs are the ones exposed in the Device DBus object. |
| // |
| // The usual case will be a Device object associated to a single given Network |
| // where both Device and Network refer to the same network interface in the |
| // system; in this case, the IPConfig exposed by the Device applies to the |
| // same network interface as the Device references. |
| // |
| // In other cases, a Device object will have multiple associated Network |
| // objects (e.g. Cellular multiplexing), where only one of them is assumed to |
| // be "primary". This list will contain the IPConfig of the primary Network |
| // exclusively. Also note, this IPConfig for the primary Network may actually |
| // refer to a totally different network interface than the one referenced by |
| // the Device object, so even if the IPConfig is exposed in DBus by the Device |
| // object, it does not mean the IP settings shown in IPConfig will be set in |
| // same network interface that the Device references. Ideally IPConfig would |
| // also expose the interface name or index in DBus. |
| // |
| auto primary_network = GetPrimaryNetwork(); |
| if (primary_network) { |
| if (primary_network->ipconfig()) { |
| identifiers.push_back(primary_network->ipconfig()->GetRpcIdentifier()); |
| } |
| if (primary_network->ip6config()) { |
| identifiers.push_back(primary_network->ip6config()->GetRpcIdentifier()); |
| } |
| } |
| return identifiers; |
| } |
| |
| bool Device::IsUnderlyingDeviceEnabled() const { |
| return false; |
| } |
| |
| // callback |
| void Device::OnEnabledStateChanged(ResultCallback callback, |
| const Error& error) { |
| LOG(INFO) << __func__ << " (target: " << enabled_pending_ << "," |
| << " success: " << error.IsSuccess() << ") on " << link_name_; |
| |
| if (error.IsSuccess()) { |
| UpdateEnabledState(); |
| } else { |
| // Set enabled_pending_ to |enabled_| so that we don't try enabling again |
| // after an error. |
| enabled_pending_ = enabled_; |
| } |
| |
| if (!callback.is_null()) |
| std::move(callback).Run(error); |
| } |
| |
| void Device::UpdateEnabledState() { |
| SLOG(this, 1) << __func__ << " (current: " << enabled_ |
| << ", target: " << enabled_pending_ << ") on " << link_name_; |
| enabled_ = enabled_pending_; |
| if (!enabled_ && ShouldBringNetworkInterfaceDownAfterDisabled()) { |
| BringNetworkInterfaceDown(); |
| } |
| manager_->UpdateEnabledTechnologies(); |
| adaptor_->EmitBoolChanged(kPoweredProperty, enabled_); |
| } |
| |
| void Device::SetEnabled(bool enable) { |
| LOG(INFO) << __func__ << "(" << enable << ")"; |
| // TODO(b/172215298): replace DoNothing() with something that logs the error |
| // and replace PopulateAndLog in many places with just Populate |
| SetEnabledChecked(enable, false, base::DoNothing()); |
| } |
| |
| void Device::SetEnabledNonPersistent(bool enable, ResultCallback callback) { |
| SLOG(this, 1) << __func__ << "(" << enable << ")"; |
| SetEnabledChecked(enable, false, std::move(callback)); |
| } |
| |
| void Device::SetEnabledPersistent(bool enable, ResultCallback callback) { |
| SLOG(this, 1) << __func__ << "(" << enable << ")"; |
| SetEnabledChecked(enable, true, std::move(callback)); |
| } |
| |
| void Device::SetEnabledChecked(bool enable, |
| bool persist, |
| ResultCallback callback) { |
| LOG(INFO) << __func__ << ": Device " << link_name_ << " " |
| << (enable ? "starting" : "stopping"); |
| if (enable && manager_->IsTechnologyProhibited(technology())) { |
| std::move(callback).Run( |
| Error(Error::kPermissionDenied, |
| "The " + GetTechnologyName() + " technology is prohibited")); |
| return; |
| } |
| |
| if (enable == enabled_) { |
| if (enable != enabled_pending_ && persist) { |
| // Return an error, as there is an ongoing operation to achieve the |
| // opposite. |
| Error err; |
| Error::PopulateAndLog( |
| FROM_HERE, &err, Error::kOperationFailed, |
| enable ? "Cannot enable while the device is disabling." |
| : "Cannot disable while the device is enabling."); |
| std::move(callback).Run(err); |
| return; |
| } |
| LOG(INFO) << "Already in desired enable state."; |
| // We can already be in the right state, but it may not be persisted. |
| // Check and flush that too. |
| if (persist && enabled_persistent_ != enable) { |
| enabled_persistent_ = enable; |
| manager_->UpdateDevice(this); |
| } |
| |
| if (!callback.is_null()) |
| std::move(callback).Run(Error(Error::kSuccess)); |
| return; |
| } |
| |
| if (enabled_pending_ == enable) { |
| Error err; |
| Error::PopulateAndLog(FROM_HERE, &err, Error::kInProgress, |
| enable ? "Enable operation already in progress" |
| : "Disable operation already in progress"); |
| std::move(callback).Run(err); |
| return; |
| } |
| |
| if (persist) { |
| enabled_persistent_ = enable; |
| manager_->UpdateDevice(this); |
| } |
| |
| SetEnabledUnchecked(enable, std::move(callback)); |
| } |
| |
| void Device::SetEnabledUnchecked(bool enable, |
| ResultCallback on_enable_complete) { |
| LOG(INFO) << LoggingTag() << " SetEnabledUnchecked(" << std::boolalpha |
| << enable << ")"; |
| enabled_pending_ = enable; |
| EnabledStateChangedCallback chained_callback = |
| base::BindOnce(&Device::OnEnabledStateChanged, AsWeakPtr(), |
| std::move(on_enable_complete)); |
| if (enable) { |
| Start(std::move(chained_callback)); |
| } else { |
| DropConnection(); |
| if (!ShouldBringNetworkInterfaceDownAfterDisabled()) { |
| BringNetworkInterfaceDown(); |
| } |
| Stop(std::move(chained_callback)); |
| } |
| } |
| |
| void Device::OnIPConfigsPropertyUpdated(int interface_index) { |
| if (!IsEventOnPrimaryNetwork(interface_index)) { |
| return; |
| } |
| adaptor_->EmitRpcIdentifierArrayChanged(kIPConfigsProperty, |
| AvailableIPConfigs(nullptr)); |
| } |
| |
| // static |
| std::vector<uint8_t> Device::MakeHardwareAddressFromString( |
| const std::string& address_string) { |
| std::string address_nosep; |
| base::RemoveChars(address_string, ":", &address_nosep); |
| std::vector<uint8_t> address_bytes; |
| base::HexStringToBytes(address_nosep, &address_bytes); |
| if (address_bytes.size() != kHardwareAddressLength) { |
| return std::vector<uint8_t>(); |
| } |
| return address_bytes; |
| } |
| |
| // static |
| std::string Device::MakeStringFromHardwareAddress( |
| const std::vector<uint8_t>& address_bytes) { |
| CHECK_EQ(kHardwareAddressLength, address_bytes.size()); |
| return base::StringPrintf( |
| "%02x:%02x:%02x:%02x:%02x:%02x", address_bytes[0], address_bytes[1], |
| address_bytes[2], address_bytes[3], address_bytes[4], address_bytes[5]); |
| } |
| |
| bool Device::RequestRoam(const std::string& addr, Error* error) { |
| return false; |
| } |
| |
| bool Device::ShouldBringNetworkInterfaceDownAfterDisabled() const { |
| return false; |
| } |
| |
| void Device::BringNetworkInterfaceDown() { |
| // The implementation of BringNetworkInterfaceDown() in the base Device class |
| // always brings down the main network interface associated to the device. |
| // Subclasses not using the implicit network should provide their own |
| // BringNetworkInterfaceDown() override as well. |
| DCHECK(implicit_network_); |
| DCHECK(implicit_network_->interface_index() == interface_index()); |
| |
| // If fixed_ip_params is true, we don't manipulate the interface state. |
| if (!implicit_network_->fixed_ip_params()) |
| rtnl_handler_->SetInterfaceFlags(interface_index(), 0, IFF_UP); |
| } |
| |
| ControlInterface* Device::control_interface() const { |
| return manager_->control_interface(); |
| } |
| |
| EventDispatcher* Device::dispatcher() const { |
| return manager_->dispatcher(); |
| } |
| |
| Metrics* Device::metrics() const { |
| return manager_->metrics(); |
| } |
| |
| std::string Device::LoggingTag() const { |
| return link_name_ + " " + |
| (selected_service_ ? selected_service_->log_name() : "no_service"); |
| } |
| |
| } // namespace shill |