blob: d2c80606b667475b880f03958766cf5df26f9a98 [file] [edit]
// Copyright 2023 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/wifi/p2p_device.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <base/containers/span.h>
#include <chromeos/dbus/shill/dbus-constants.h>
#include <chromeos/patchpanel/dbus/client.h>
#include <net-base/byte_utils.h>
#include <net-base/mac_address.h>
#include <net-base/network_config.h>
#include <net-base/ip_address.h>
#include <net-base/ipv4_address.h>
#include <net-base/ipv6_address.h>
#include <net-base/rtnl_handler.h>
#include "shill/control_interface.h"
#include "shill/manager.h"
#include "shill/network/legacy_dhcp_controller.h"
#include "shill/network/network.h"
#include "shill/network/network_manager.h"
#include "shill/network/network_monitor.h"
#include "shill/supplicant/supplicant_group_proxy_interface.h"
#include "shill/supplicant/supplicant_interface_proxy_interface.h"
#include "shill/supplicant/supplicant_p2pdevice_proxy_interface.h"
#include "shill/supplicant/wpa_supplicant.h"
#include "shill/technology.h"
#include "shill/wifi/wifi_provider.h"
namespace shill {
namespace {
const char* GroupInfoState(P2PDevice::P2PDeviceState state) {
switch (state) {
case P2PDevice::P2PDeviceState::kGOStarting:
return kP2PGroupInfoStateStarting;
case P2PDevice::P2PDeviceState::kGOConfiguring:
return kP2PGroupInfoStateConfiguring;
case P2PDevice::P2PDeviceState::kGOActive:
return kP2PGroupInfoStateActive;
case P2PDevice::P2PDeviceState::kGOStopping:
return kP2PGroupInfoStateStopping;
case P2PDevice::P2PDeviceState::kUninitialized:
case P2PDevice::P2PDeviceState::kReady:
case P2PDevice::P2PDeviceState::kClientAssociating:
case P2PDevice::P2PDeviceState::kClientConfiguring:
case P2PDevice::P2PDeviceState::kClientConnected:
case P2PDevice::P2PDeviceState::kClientDisconnecting:
return kP2PGroupInfoStateIdle;
}
NOTREACHED() << "Unhandled P2P state " << static_cast<int>(state);
return kP2PGroupInfoStateIdle;
}
const char* ClientInfoState(P2PDevice::P2PDeviceState state) {
switch (state) {
case P2PDevice::P2PDeviceState::kClientAssociating:
return kP2PClientInfoStateAssociating;
case P2PDevice::P2PDeviceState::kClientConfiguring:
return kP2PClientInfoStateConfiguring;
case P2PDevice::P2PDeviceState::kClientConnected:
return kP2PClientInfoStateConnected;
case P2PDevice::P2PDeviceState::kClientDisconnecting:
return kP2PClientInfoStateDisconnecting;
case P2PDevice::P2PDeviceState::kUninitialized:
case P2PDevice::P2PDeviceState::kReady:
case P2PDevice::P2PDeviceState::kGOStarting:
case P2PDevice::P2PDeviceState::kGOConfiguring:
case P2PDevice::P2PDeviceState::kGOActive:
case P2PDevice::P2PDeviceState::kGOStopping:
return kP2PClientInfoStateIdle;
}
NOTREACHED() << "Unhandled P2P state " << static_cast<int>(state);
return kP2PClientInfoStateIdle;
}
} // namespace
// Constructor function
P2PDevice::P2PDevice(Manager* manager,
LocalDevice::IfaceType iface_type,
const std::string& primary_link_name,
uint32_t phy_index,
int32_t shill_id,
WiFiPhy::Priority priority,
LocalDevice::EventCallback callback)
: LocalDevice(
manager, iface_type, std::nullopt, phy_index, priority, callback),
primary_link_name_(primary_link_name),
shill_id_(shill_id),
state_(P2PDeviceState::kUninitialized) {
// A P2PDevice with a non-P2P interface type makes no sense.
CHECK(iface_type == LocalDevice::IfaceType::kP2PGO ||
iface_type == LocalDevice::IfaceType::kP2PClient);
log_name_ = (iface_type == LocalDevice::IfaceType::kP2PGO)
? "p2p_go_" + std::to_string(shill_id)
: "p2p_client_" + std::to_string(shill_id);
supplicant_interface_proxy_.reset();
supplicant_interface_path_ = RpcIdentifier("");
supplicant_p2pdevice_proxy_.reset();
supplicant_group_proxy_.reset();
supplicant_group_path_ = RpcIdentifier("");
supplicant_persistent_group_path_ = RpcIdentifier("");
group_ssid_ = "";
group_bssid_ = std::nullopt;
group_frequency_ = 0;
group_passphrase_ = "";
interface_address_ = std::nullopt;
go_ipv4_address_ = std::nullopt;
go_network_id_ = std::nullopt;
LOG(INFO) << log_name() << ": P2PDevice created";
}
P2PDevice::~P2PDevice() {
LOG(INFO) << log_name() << ": P2PDevice destroyed";
if (client_network_) {
client_network_->Stop();
client_network_->UnregisterEventHandler(this);
}
}
// static
const char* P2PDevice::P2PDeviceStateName(P2PDeviceState state) {
switch (state) {
case P2PDeviceState::kUninitialized:
return kP2PDeviceStateUninitialized;
case P2PDeviceState::kReady:
return kP2PDeviceStateReady;
case P2PDeviceState::kClientAssociating:
return kP2PDeviceStateClientAssociating;
case P2PDeviceState::kClientConfiguring:
return kP2PDeviceStateClientConfiguring;
case P2PDeviceState::kClientConnected:
return kP2PDeviceStateClientConnected;
case P2PDeviceState::kClientDisconnecting:
return kP2PDeviceStateClientDisconnecting;
case P2PDeviceState::kGOStarting:
return kP2PDeviceStateGOStarting;
case P2PDeviceState::kGOConfiguring:
return kP2PDeviceStateGOConfiguring;
case P2PDeviceState::kGOActive:
return kP2PDeviceStateGOActive;
case P2PDeviceState::kGOStopping:
return kP2PDeviceStateGOStopping;
}
NOTREACHED() << "Unhandled P2P state " << static_cast<int>(state);
return "Invalid";
}
void P2PDevice::UpdateGroupNetworkInfo(
const patchpanel::Client::DownstreamNetwork& downstream_network) {
go_ipv4_address_ = downstream_network.ipv4_gateway_addr;
go_network_id_ = downstream_network.network_id;
// TODO(b/331859957): cache and expose IPv6 address.
}
Stringmaps P2PDevice::GroupInfoClients() const {
Stringmaps clients;
for (auto const& peer : group_peers_) {
clients.push_back(peer.second.get()->GetPeerProperties());
}
return clients;
}
KeyValueStore P2PDevice::GetGroupInfo() const {
KeyValueStore group_info;
if (iface_type() != LocalDevice::IfaceType::kP2PGO) {
LOG(WARNING) << log_name() << ": Tried to get group info for iface_type "
<< iface_type();
return group_info;
}
group_info.Set<int32_t>(kP2PGroupInfoShillIDProperty, shill_id());
group_info.Set<String>(kP2PGroupInfoStateProperty, GroupInfoState(state_));
if (IsLinkLayerConnected()) {
group_info.Set<String>(kP2PGroupInfoSSIDProperty, group_ssid_);
group_info.Set<String>(
kP2PGroupInfoBSSIDProperty,
group_bssid_.has_value() ? group_bssid_->ToString() : "");
group_info.Set<Integer>(kP2PGroupInfoFrequencyProperty, group_frequency_);
group_info.Set<String>(kP2PGroupInfoPassphraseProperty, group_passphrase_);
group_info.Set<String>(kP2PGroupInfoInterfaceProperty, *link_name());
group_info.Set<String>(kP2PClientInfoMACAddressProperty,
interface_address_.value().ToString());
group_info.Set<Stringmaps>(kP2PGroupInfoClientsProperty,
GroupInfoClients());
if (IsNetworkLayerConnected()) {
if (go_network_id_ != std::nullopt) {
group_info.Set<int32_t>(kP2PGroupInfoNetworkIDProperty,
go_network_id_.value());
}
if (go_ipv4_address_ != std::nullopt) {
group_info.Set<String>(kP2PGroupInfoIPv4AddressProperty,
go_ipv4_address_.value().ToString());
}
}
}
return group_info;
}
KeyValueStore P2PDevice::GetClientInfo() const {
KeyValueStore client_info;
if (iface_type() != LocalDevice::IfaceType::kP2PClient) {
LOG(WARNING) << log_name() << ": Tried to get client info for iface_type "
<< iface_type();
return client_info;
}
client_info.Set<int32_t>(kP2PClientInfoShillIDProperty, shill_id());
client_info.Set<String>(kP2PClientInfoStateProperty, ClientInfoState(state_));
if (IsLinkLayerConnected()) {
Stringmap go_info;
const std::string group_bssid_str =
group_bssid_.has_value() ? group_bssid_->ToString() : "";
client_info.Set<String>(kP2PClientInfoSSIDProperty, group_ssid_);
client_info.Set<String>(kP2PClientInfoGroupBSSIDProperty, group_bssid_str);
client_info.Set<Integer>(kP2PClientInfoFrequencyProperty, group_frequency_);
client_info.Set<String>(kP2PClientInfoPassphraseProperty,
group_passphrase_);
client_info.Set<String>(kP2PClientInfoInterfaceProperty, *link_name());
client_info.Set<String>(kP2PClientInfoMACAddressProperty,
interface_address_.value().ToString());
go_info.insert(
{kP2PClientInfoGroupOwnerMACAddressProperty, group_bssid_str});
if (IsNetworkLayerConnected()) {
client_info.Set<int32_t>(kP2PClientInfoNetworkIDProperty,
client_network_->network_id());
const auto network_config = client_network_->GetNetworkConfig();
if (network_config.ipv4_address != std::nullopt) {
client_info.Set<String>(
kP2PClientInfoIPv4AddressProperty,
network_config.ipv4_address->address().ToString());
}
if (!network_config.ipv6_addresses.empty()) {
Strings ipv6_addresses;
for (auto ipv6_address : network_config.ipv6_addresses) {
ipv6_addresses.push_back(ipv6_address.address().ToString());
}
client_info.Set<Strings>(kP2PClientInfoIPv6AddressProperty,
ipv6_addresses);
}
if (network_config.ipv4_gateway != std::nullopt) {
go_info.insert({kP2PClientInfoGroupOwnerIPv4AddressProperty,
network_config.ipv4_gateway->ToString()});
}
if (network_config.ipv6_gateway != std::nullopt) {
go_info.insert({kP2PClientInfoGroupOwnerIPv6AddressProperty,
network_config.ipv6_gateway->ToString()});
}
}
client_info.Set<Stringmap>(kP2PClientInfoGroupOwnerProperty, go_info);
}
return client_info;
}
bool P2PDevice::Start() {
SetState(P2PDeviceState::kReady);
return true;
}
bool P2PDevice::Stop() {
bool ret = true;
if (InClientState()) {
if (!Disconnect()) {
ret = false;
}
} else if (InGOState()) {
if (!RemoveGroup()) {
ret = false;
}
}
SetState(P2PDeviceState::kUninitialized);
return ret;
}
bool P2PDevice::CreateGroup(std::unique_ptr<P2PService> service) {
if (state_ != P2PDeviceState::kReady) {
LOG(ERROR) << log_name() << ": Tried to create group while in state "
<< P2PDeviceStateName(state_);
return false;
}
if (!service) {
LOG(ERROR) << log_name()
<< ": Tried to create a group with an empty service.";
return false;
}
if (service_) {
LOG(ERROR) << log_name()
<< ": Attempted to create group on a device which already has a "
"service configured.";
return false;
}
KeyValueStore properties = service->GetSupplicantConfigurationParameters();
if (!StartSupplicantGroupForGO(properties)) {
return false;
}
SetService(std::move(service));
SetState(P2PDeviceState::kGOStarting);
return true;
}
bool P2PDevice::Connect(std::unique_ptr<P2PService> service) {
if (state_ != P2PDeviceState::kReady) {
LOG(ERROR) << log_name() << ": Tried to connect while in state "
<< P2PDeviceStateName(state_);
return false;
}
if (!service) {
LOG(ERROR) << log_name() << ": Tried to connect with an empty serveice.";
return false;
}
if (service_) {
LOG(ERROR) << log_name()
<< ": Attempted to connect to group on a device which already "
"has a service configured.";
return false;
}
KeyValueStore properties = service->GetSupplicantConfigurationParameters();
if (!StartSupplicantGroupForClient(properties)) {
return false;
}
SetService(std::move(service));
SetState(P2PDeviceState::kClientAssociating);
return true;
}
bool P2PDevice::RemoveGroup() {
if (!InGOState()) {
LOG(WARNING) << log_name() << ": Tried to remove a group while in state "
<< P2PDeviceStateName(state_);
return false;
}
SetState(P2PDeviceState::kGOStopping);
go_ipv4_address_ = std::nullopt;
go_network_id_ = std::nullopt;
group_network_fd_.reset();
FinishSupplicantGroup();
// TODO(b/308081318): delete service on GroupFinished
DeleteService();
return true;
}
bool P2PDevice::Disconnect() {
if (!InClientState()) {
LOG(WARNING) << log_name() << ": Tried to disconnect while in state "
<< P2PDeviceStateName(state_);
return false;
}
SetState(P2PDeviceState::kClientDisconnecting);
if (client_network_) {
client_network_->Stop();
client_network_->UnregisterEventHandler(this);
client_network_.reset();
}
FinishSupplicantGroup();
// TODO(b/308081318): delete service on GroupFinished
DeleteService();
return true;
}
bool P2PDevice::InGOState() const {
return (state_ == P2PDeviceState::kGOStarting ||
state_ == P2PDeviceState::kGOConfiguring ||
state_ == P2PDeviceState::kGOActive ||
state_ == P2PDeviceState::kGOStopping);
}
bool P2PDevice::InClientState() const {
return (state_ == P2PDeviceState::kClientAssociating ||
state_ == P2PDeviceState::kClientConfiguring ||
state_ == P2PDeviceState::kClientConnected ||
state_ == P2PDeviceState::kClientDisconnecting);
}
SupplicantP2PDeviceProxyInterface* P2PDevice::SupplicantPrimaryP2PDeviceProxy()
const {
return manager()
->wifi_provider()
->p2p_manager()
->SupplicantPrimaryP2PDeviceProxy();
}
bool P2PDevice::StartSupplicantGroupForGO(const KeyValueStore& properties) {
if (!SupplicantPrimaryP2PDeviceProxy()) {
LOG(ERROR) << log_name()
<< ": Tried to start group while the primary P2PDevice proxy is "
"not connected";
return false;
}
if (!SupplicantPrimaryP2PDeviceProxy()->GroupAdd(properties)) {
LOG(ERROR) << log_name()
<< ": Failed to GroupAdd via the primary P2PDevice proxy";
return false;
}
return true;
}
bool P2PDevice::StartSupplicantGroupForClient(const KeyValueStore& properties) {
if (!SupplicantPrimaryP2PDeviceProxy()) {
LOG(WARNING) << log_name()
<< ": Tried to join group while the primary "
"P2PDevice proxy is not connected";
return false;
}
// Right now, there are no commands available in wpa_supplicant to bypass
// P2P discovery and join an existing P2P group directly. Instead `GroupAdd`
// with persistent group object path and role specified as client can be used
// to join the P2P network. For client mode, even if group is specified as
// persistent, it will still follow the GO's lead and join as a non-persistent
// group. For GO mode, the `GroupAdd` is used directly so that it creates
// a non-persistent group.
if (!SupplicantPrimaryP2PDeviceProxy()->AddPersistentGroup(
properties, &supplicant_persistent_group_path_)) {
LOG(ERROR) << log_name()
<< ": Failed to AddPersistentGroup via the primary"
" P2PDevice proxy";
return false;
}
if (supplicant_persistent_group_path_.value().empty()) {
LOG(ERROR) << log_name()
<< ": Got empty persistent group path from "
"the primary P2PDevice proxy";
return false;
}
KeyValueStore p2pgroup_args;
p2pgroup_args.Set<RpcIdentifier>(
WPASupplicant::kGroupAddPropertyPersistentPath,
supplicant_persistent_group_path_);
if (!SupplicantPrimaryP2PDeviceProxy()->GroupAdd(p2pgroup_args)) {
LOG(ERROR) << log_name()
<< ": Failed to GroupAdd via the primary "
"P2PDevice proxy";
SupplicantPrimaryP2PDeviceProxy()->RemovePersistentGroup(
supplicant_persistent_group_path_);
supplicant_persistent_group_path_ = RpcIdentifier("");
return false;
}
return true;
}
bool P2PDevice::FinishSupplicantGroup() {
if (!supplicant_p2pdevice_proxy_) {
LOG(ERROR)
<< log_name()
<< ": Tried to stop group while P2PDevice proxy is not connected";
return false;
}
if (!supplicant_p2pdevice_proxy_->Disconnect()) {
LOG(ERROR) << log_name() << ": Failed to Disconnect via P2PDevice proxy";
return false;
}
return true;
}
void P2PDevice::SetService(std::unique_ptr<P2PService> service) {
service_ = std::move(service);
service_->SetState(LocalService::LocalServiceState::kStateStarting);
}
void P2PDevice::DeleteService() {
if (!service_) {
return;
}
service_->SetState(LocalService::LocalServiceState::kStateIdle);
service_ = nullptr;
}
void P2PDevice::SetState(P2PDeviceState state) {
if (state_ == state)
return;
LOG(INFO) << log_name() << ": State changed: " << P2PDeviceStateName(state_)
<< " -> " << P2PDeviceStateName(state);
state_ = state;
}
bool P2PDevice::IsLinkLayerConnected() const {
if (iface_type() == LocalDevice::IfaceType::kP2PClient) {
return (state_ == P2PDeviceState::kClientConfiguring ||
state_ == P2PDeviceState::kClientConnected);
} else if (iface_type() == LocalDevice::IfaceType::kP2PGO) {
return (state_ == P2PDeviceState::kGOConfiguring ||
state_ == P2PDeviceState::kGOActive);
} else {
return false;
}
}
bool P2PDevice::IsNetworkLayerConnected() const {
if (iface_type() == LocalDevice::IfaceType::kP2PClient) {
return (state_ == P2PDeviceState::kClientConnected);
} else if (iface_type() == LocalDevice::IfaceType::kP2PGO) {
return (state_ == P2PDeviceState::kGOActive);
} else {
return false;
}
}
bool P2PDevice::ConnectToSupplicantInterfaceProxy(
const RpcIdentifier& object_path) {
if (supplicant_interface_proxy_) {
LOG(WARNING) << log_name()
<< ": Tried to connect to the Interface proxy while it is "
"already connected";
return false;
}
supplicant_interface_proxy_ =
ControlInterface()->CreateSupplicantInterfaceProxy(this, object_path);
if (!supplicant_interface_proxy_) {
LOG(ERROR) << log_name()
<< ": Failed to connect to the Interface proxy, path: "
<< object_path.value();
return false;
}
supplicant_interface_path_ = object_path;
LOG(INFO) << log_name() << ": Interface proxy connected, path: "
<< supplicant_interface_path_.value();
return true;
}
void P2PDevice::DisconnectFromSupplicantInterfaceProxy() {
if (supplicant_interface_proxy_) {
LOG(INFO) << log_name() << ": Interface proxy disconnected, path: "
<< supplicant_interface_path_.value();
}
supplicant_interface_path_ = RpcIdentifier("");
supplicant_interface_proxy_.reset();
}
String P2PDevice::GetInterfaceName() const {
String ifname;
if (!supplicant_interface_proxy_->GetIfname(&ifname)) {
LOG(ERROR) << log_name() << ": Failed to GetIfname via Interface proxy";
return "";
}
return ifname;
}
std::optional<net_base::MacAddress> P2PDevice::GetInterfaceAddress() const {
ByteArray mac_address;
if (!supplicant_interface_proxy_->GetMACAddress(&mac_address)) {
LOG(ERROR) << log_name()
<< ": Failed to Get MAC address via Interface proxy";
return std::nullopt;
}
return net_base::MacAddress::CreateFromBytes(mac_address);
}
bool P2PDevice::ConnectToSupplicantP2PDeviceProxy(
const RpcIdentifier& interface) {
if (supplicant_p2pdevice_proxy_) {
LOG(WARNING)
<< log_name()
<< ": Tried to connect to P2PDevice proxy while already connected";
return false;
}
supplicant_p2pdevice_proxy_ =
ControlInterface()->CreateSupplicantP2PDeviceProxy(this, interface);
if (!supplicant_p2pdevice_proxy_) {
LOG(ERROR) << log_name() << ": Failed to connect to P2PDevice proxy, path: "
<< interface.value();
return false;
}
LOG(INFO) << log_name()
<< ": P2PDevice proxy connected, path: " << interface.value();
return true;
}
void P2PDevice::DisconnectFromSupplicantP2PDeviceProxy() {
if (supplicant_p2pdevice_proxy_) {
supplicant_p2pdevice_proxy_.reset();
LOG(INFO) << log_name() << ": P2PDevice proxy disconnected";
}
}
bool P2PDevice::ConnectToSupplicantGroupProxy(const RpcIdentifier& group) {
if (supplicant_group_proxy_) {
LOG(WARNING) << log_name()
<< ": Tried to connect to the Group proxy while it is already "
"connected";
return false;
}
supplicant_group_proxy_ =
ControlInterface()->CreateSupplicantGroupProxy(this, group);
if (!supplicant_group_proxy_) {
LOG(ERROR) << log_name() << ": Failed to connect to the Group proxy, path: "
<< group.value();
return false;
}
supplicant_group_path_ = group;
LOG(INFO) << log_name() << ": Group proxy connected, path: "
<< supplicant_group_path_.value();
return true;
}
void P2PDevice::DisconnectFromSupplicantGroupProxy(void) {
if (supplicant_group_proxy_) {
LOG(INFO) << log_name() << ": Group proxy disconnected, path: "
<< supplicant_group_path_.value();
}
supplicant_group_path_ = RpcIdentifier("");
supplicant_group_proxy_.reset();
}
String P2PDevice::GetGroupSSID() const {
ByteArray ssid;
if (!supplicant_group_proxy_->GetSSID(&ssid)) {
LOG(ERROR) << log_name() << ": Failed to GetSSID via Group proxy";
return "";
}
return net_base::byte_utils::ByteStringFromBytes(ssid);
}
std::optional<net_base::MacAddress> P2PDevice::GetGroupBSSID() const {
ByteArray bssid;
if (!supplicant_group_proxy_->GetBSSID(&bssid)) {
LOG(ERROR) << log_name() << ": Failed to GetBSSID via Group proxy";
return std::nullopt;
}
return net_base::MacAddress::CreateFromBytes(bssid);
}
Integer P2PDevice::GetGroupFrequency() const {
uint16_t frequency = 0;
if (!supplicant_group_proxy_->GetFrequency(&frequency)) {
LOG(ERROR) << log_name() << ": Failed to GetFrequency via Group proxy";
return 0;
}
return frequency;
}
String P2PDevice::GetGroupPassphrase() const {
std::string passphrase;
if (!supplicant_group_proxy_->GetPassphrase(&passphrase)) {
LOG(ERROR) << log_name() << ": Failed to GetPassphrase via Group proxy";
return "";
}
return passphrase;
}
bool P2PDevice::SetupGroup(const KeyValueStore& properties) {
RpcIdentifier interface_path = RpcIdentifier("");
if (properties.Contains<RpcIdentifier>(
WPASupplicant::kGroupStartedPropertyInterfaceObject)) {
interface_path = properties.Get<RpcIdentifier>(
WPASupplicant::kGroupStartedPropertyInterfaceObject);
}
if (interface_path.value().empty()) {
LOG(ERROR) << log_name() << ": Failed to " << __func__
<< " without interface path";
return false;
}
RpcIdentifier group_path = RpcIdentifier("");
if (properties.Contains<RpcIdentifier>(
WPASupplicant::kGroupStartedPropertyGroupObject)) {
group_path = properties.Get<RpcIdentifier>(
WPASupplicant::kGroupStartedPropertyGroupObject);
}
if (group_path.value().empty()) {
LOG(ERROR) << log_name() << ": Failed to " << __func__
<< " without group path";
return false;
}
if (!ConnectToSupplicantInterfaceProxy(interface_path) ||
!ConnectToSupplicantP2PDeviceProxy(interface_path) ||
!ConnectToSupplicantGroupProxy(group_path)) {
TeardownGroup();
return false;
}
link_name_ = GetInterfaceName();
if (link_name_.value().empty()) {
LOG(ERROR) << log_name() << ": Failed to get interface name";
return false;
} else {
LOG(INFO) << log_name() << ": Link name configured: " << link_name_.value();
}
group_ssid_ = GetGroupSSID();
if (group_ssid_.empty()) {
LOG(ERROR) << log_name() << ": Failed to get group SSID";
return false;
} else {
LOG(INFO) << log_name() << ": SSID configured: " << group_ssid_;
}
group_bssid_ = GetGroupBSSID();
if (!group_bssid_.has_value()) {
LOG(ERROR) << log_name() << ": Failed to get group BSSID";
return false;
} else {
LOG(INFO) << log_name()
<< ": BSSID configured: " << group_bssid_->ToString();
}
group_frequency_ = GetGroupFrequency();
if (group_frequency_) {
LOG(INFO) << log_name() << ": Freqency configured: " << group_frequency_;
} else {
LOG(ERROR) << log_name() << ": Failed to get group frequency";
return false;
}
group_passphrase_ = GetGroupPassphrase();
if (group_passphrase_.empty()) {
LOG(ERROR) << log_name() << ": Failed to get group passphrase";
return false;
} else {
LOG(INFO) << log_name() << ": Passphrase configured: " << group_passphrase_;
}
interface_address_ = GetInterfaceAddress();
if (interface_address_ == std::nullopt) {
LOG(ERROR) << log_name() << ": Failed to get interface address";
return false;
} else {
LOG(INFO) << log_name() << ": Interface address configured: "
<< interface_address_.value().ToString();
}
// TODO(b/308081318): This requires HotspotDevice to be fully responsible
// for states and events handling. Currently DeviceEvent::kLinkUp/Down events
// are partially handled by LocalService.
// service_->SetState(LocalService::LocalServiceState::kStateUp);
return true;
}
void P2PDevice::TeardownGroup(const KeyValueStore& properties) {
RpcIdentifier interface_path = RpcIdentifier("");
if (properties.Contains<RpcIdentifier>(
WPASupplicant::kGroupFinishedPropertyInterfaceObject)) {
interface_path = properties.Get<RpcIdentifier>(
WPASupplicant::kGroupFinishedPropertyInterfaceObject);
}
CHECK(interface_path == supplicant_interface_path_);
RpcIdentifier group_path = RpcIdentifier("");
if (properties.Contains<RpcIdentifier>(
WPASupplicant::kGroupFinishedPropertyGroupObject)) {
group_path = properties.Get<RpcIdentifier>(
WPASupplicant::kGroupFinishedPropertyGroupObject);
}
if (group_path != supplicant_group_path_) {
LOG(WARNING) << log_name() << ": " << __func__
<< " for unknown object, path: " << group_path.value();
}
TeardownGroup();
}
void P2PDevice::TeardownGroup() {
// TODO(b/322557062): Ensure that the underlying kernel interface is properly
// torn down.
group_ssid_ = "";
group_bssid_ = std::nullopt;
group_frequency_ = 0;
group_passphrase_ = "";
group_peers_.clear();
link_name_ = std::nullopt;
DisconnectFromSupplicantGroupProxy();
DisconnectFromSupplicantP2PDeviceProxy();
DisconnectFromSupplicantInterfaceProxy();
if (!supplicant_persistent_group_path_.value().empty()) {
SupplicantPrimaryP2PDeviceProxy()->RemovePersistentGroup(
supplicant_persistent_group_path_);
supplicant_persistent_group_path_ = RpcIdentifier("");
}
}
void P2PDevice::GroupStarted(const KeyValueStore& properties) {
LOG(INFO) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
switch (state_) {
// Expected P2P client state for GroupStarted event
case P2PDeviceState::kClientAssociating:
SetupGroup(properties);
SetState(P2PDeviceState::kClientConfiguring);
PostDeviceEvent(DeviceEvent::kLinkUp);
AcquireClientIP();
break;
// Expected P2P GO state for GroupStarted event
case P2PDeviceState::kGOStarting:
SetupGroup(properties);
SetState(P2PDeviceState::kGOConfiguring);
PostDeviceEvent(DeviceEvent::kLinkUp);
StartGroupNetwork();
break;
// Common states for all roles.
case P2PDeviceState::kUninitialized:
case P2PDeviceState::kReady:
// P2P client states.
case P2PDeviceState::kClientConfiguring:
case P2PDeviceState::kClientConnected:
case P2PDeviceState::kClientDisconnecting:
// P2P GO states.
case P2PDeviceState::kGOConfiguring:
case P2PDeviceState::kGOActive:
case P2PDeviceState::kGOStopping:
LOG(WARNING) << log_name() << ": Ignored " << __func__
<< " while in state " << P2PDeviceStateName(state_);
break;
}
}
void P2PDevice::GroupFinished(const KeyValueStore& properties) {
LOG(INFO) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
switch (state_) {
// Expected P2P client/GO state for GroupFinished event
case P2PDeviceState::kClientDisconnecting:
case P2PDeviceState::kGOStopping:
TeardownGroup(properties);
SetState(P2PDeviceState::kReady);
PostDeviceEvent(DeviceEvent::kLinkDown);
break;
// P2P client link failure states for GroupFinished event
case P2PDeviceState::kClientConfiguring:
case P2PDeviceState::kClientConnected:
LOG(WARNING) << log_name()
<< ": Client link failure, group finished while in state "
<< P2PDeviceStateName(state_);
TeardownGroup(properties);
SetState(P2PDeviceState::kClientDisconnecting);
PostDeviceEvent(DeviceEvent::kLinkFailure);
break;
// P2P GO link failure states for GroupFinished event
case P2PDeviceState::kGOConfiguring:
case P2PDeviceState::kGOActive:
LOG(WARNING) << log_name()
<< ": GO link failure, group finished while in state "
<< P2PDeviceStateName(state_);
TeardownGroup(properties);
SetState(P2PDeviceState::kGOStopping);
PostDeviceEvent(DeviceEvent::kLinkFailure);
break;
// P2P client/GO unknown error states for GroupFinished event
case P2PDeviceState::kClientAssociating:
case P2PDeviceState::kGOStarting:
LOG(ERROR) << log_name() << ": Ignored " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
break;
// Common states for all roles.
case P2PDeviceState::kUninitialized:
case P2PDeviceState::kReady:
LOG(WARNING) << log_name() << ": Ignored " << __func__
<< " while in state " << P2PDeviceStateName(state_);
break;
}
}
void P2PDevice::GroupFormationFailure(const std::string& reason) {
LOG(WARNING) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
switch (state_) {
// Expected P2P client state for GroupFormationFailure signal
case P2PDeviceState::kClientAssociating:
LOG(ERROR) << log_name()
<< ": Failed to connect Client, group formation failure";
SetState(P2PDeviceState::kClientDisconnecting);
PostDeviceEvent(DeviceEvent::kLinkFailure);
break;
// Expected P2P GO state for GroupFormationFailure signal
case P2PDeviceState::kGOStarting:
LOG(ERROR) << log_name()
<< ": Failed to start GO, group formation failure";
SetState(P2PDeviceState::kGOStopping);
PostDeviceEvent(DeviceEvent::kLinkFailure);
break;
// Common states for all roles.
case P2PDeviceState::kUninitialized:
case P2PDeviceState::kReady:
// P2P client states.
case P2PDeviceState::kClientConfiguring:
case P2PDeviceState::kClientConnected:
case P2PDeviceState::kClientDisconnecting:
// P2P GO states.
case P2PDeviceState::kGOConfiguring:
case P2PDeviceState::kGOActive:
case P2PDeviceState::kGOStopping:
LOG(WARNING) << log_name() << ": Ignored " << __func__
<< " while in state " << P2PDeviceStateName(state_);
break;
}
}
void P2PDevice::AcquireClientIP() {
if (client_network_for_test_) {
client_network_ = std::move(client_network_for_test_);
client_network_->RegisterEventHandler(this);
} else {
// TODO(b/310584119): use new enum instead of Technology inside
// shill::Network
client_network_ = manager()->network_manager()->CreateNetwork(
net_base::RTNLHandler::GetInstance()->GetInterfaceIndex(
link_name().value()),
link_name().value(), Technology::kWiFi, false,
manager()->patchpanel_client());
client_network_->RegisterEventHandler(this);
}
auto dhcp_opts = manager()->CreateDefaultDHCPOption();
Network::StartOptions opts = {
.dhcp = dhcp_opts,
.accept_ra = true,
.ignore_link_monitoring = true,
// TODO(b/314693271) omit probing_configuration when validation mode is
// kDisabled.
.probing_configuration =
manager()->GetPortalDetectorProbingConfiguration(),
.validation_mode = NetworkMonitor::ValidationMode::kDisabled,
};
client_network_->Start(opts);
}
bool P2PDevice::StartGroupNetwork() {
if (!manager()->patchpanel_client() ||
!manager()->patchpanel_client()->CreateLocalOnlyNetwork(
link_name().value(), base::BindOnce(&P2PDevice::OnGroupNetworkStarted,
base::Unretained(this)))) {
LOG(ERROR) << log_name() << ": Failed to create local only network";
PostDeviceEvent(DeviceEvent::kNetworkFailure);
return false;
}
return true;
}
void P2PDevice::OnConnectionUpdated(int net_interface_index) {
if (state_ != P2PDeviceState::kClientConfiguring) {
LOG(WARNING) << log_name() << ": Ignored " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
return;
}
LOG(INFO) << log_name() << ": Successfully get IP address on "
<< link_name().value();
SetState(P2PDeviceState::kClientConnected);
PostDeviceEvent(DeviceEvent::kNetworkUp);
}
void P2PDevice::OnNetworkStopped(int interface_index, bool is_failure) {
if (state_ == P2PDeviceState::kClientConfiguring) {
// Failed to fetch an IP address for client mode.
PostDeviceEvent(DeviceEvent::kNetworkFailure);
TeardownGroup();
} else if (state_ == P2PDeviceState::kClientConnected) {
PostDeviceEvent(DeviceEvent::kNetworkDown);
TeardownGroup();
} else {
LOG(WARNING) << log_name() << ": Ignored " << __func__
<< " (failure = " << is_failure << ") while in state "
<< P2PDeviceStateName(state_);
}
}
void P2PDevice::OnGroupNetworkStarted(
base::ScopedFD network_fd,
const patchpanel::Client::DownstreamNetwork& network) {
if (state_ != P2PDeviceState::kGOConfiguring) {
LOG(WARNING) << log_name() << ": Ignored " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
return;
}
if (!network_fd.is_valid()) {
LOG(ERROR) << log_name() << ": Failed to create group network on "
<< link_name().value();
PostDeviceEvent(DeviceEvent::kNetworkFailure);
return;
}
LOG(INFO) << log_name() << ": Established downstream network network_id="
<< network.network_id << " on " << link_name().value();
group_network_fd_ = std::move(network_fd);
UpdateGroupNetworkInfo(network);
SetState(P2PDeviceState::kGOActive);
PostDeviceEvent(DeviceEvent::kNetworkUp);
}
void P2PDevice::NetworkFinished() {
LOG(INFO) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
// TODO(b/308081318): teardown group/connection or ignore unexpected state
PostDeviceEvent(DeviceEvent::kNetworkDown);
}
void P2PDevice::NetworkFailure(const std::string& reason) {
LOG(WARNING) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_) << ", reason: " << reason;
// TODO(b/308081318): teardown group/connection or ignore unexpected state
PostDeviceEvent(DeviceEvent::kNetworkFailure);
}
void P2PDevice::PeerJoined(const dbus::ObjectPath& peer) {
LOG(INFO) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
if (state_ != P2PDeviceState::kGOConfiguring &&
state_ != P2PDeviceState::kGOActive) {
return;
}
if (base::Contains(group_peers_, peer)) {
LOG(WARNING) << "Ignored " << __func__
<< " while already connected, path: " << peer.value();
return;
}
group_peers_[peer] =
std::make_unique<P2PPeer>(this, peer, ControlInterface());
LOG(INFO) << log_name() << ": Peer connected, path: " << peer.value();
PostDeviceEvent(DeviceEvent::kPeerConnected);
}
void P2PDevice::PeerDisconnected(const dbus::ObjectPath& peer) {
LOG(INFO) << log_name() << ": Got " << __func__ << " while in state "
<< P2PDeviceStateName(state_);
if (state_ != P2PDeviceState::kGOConfiguring &&
state_ != P2PDeviceState::kGOActive) {
return;
}
if (!base::Contains(group_peers_, peer)) {
LOG(WARNING) << "Ignored " << __func__
<< " while not connected, path: " << peer.value();
return;
}
LOG(INFO) << log_name() << ": Peer disconnected, path: " << peer.value();
group_peers_.erase(peer);
PostDeviceEvent(DeviceEvent::kPeerDisconnected);
}
} // namespace shill