From dd13c8b2c53d7ff13db8eb07ee17a96adacb9309 Mon Sep 17 00:00:00 2001 From: Vibhav Pant Date: Wed, 23 Aug 2023 00:48:22 +0530 Subject: [PATCH] Add additional WiFi code. --- ...orkmanager_connection_active_client_glue.h | 126 +++++++++ ...sktop.NetworkManager.Connection.Active.xml | 185 ++++++++++++ .../implementation/linux/wifi_hotspot.cc | 191 +++++++++++++ .../implementation/linux/wifi_hotspot.h | 50 ++++ .../platform/implementation/linux/wifi_lan.cc | 22 +- .../platform/implementation/linux/wifi_lan.h | 5 +- .../linux/wifi_lan_server_socket.cc | 44 ++- .../linux/wifi_lan_server_socket.h | 16 +- .../implementation/linux/wifi_medium.cc | 266 ++++++++++++++++-- .../implementation/linux/wifi_medium.h | 150 +++++++++- 10 files changed, 982 insertions(+), 73 deletions(-) create mode 100644 internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h create mode 100644 internal/platform/implementation/linux/org.freedesktop.NetworkManager.Connection.Active.xml create mode 100644 internal/platform/implementation/linux/wifi_hotspot.cc create mode 100644 internal/platform/implementation/linux/wifi_hotspot.h diff --git a/internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h b/internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h new file mode 100644 index 00000000..8b077ad6 --- /dev/null +++ b/internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h @@ -0,0 +1,126 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__networkmanager_connection_active_client_glue_h__proxy__H__ +#define __sdbuscpp__networkmanager_connection_active_client_glue_h__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { +namespace Connection { + +class Active_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Connection.Active"; + +protected: + Active_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const uint32_t& state, const uint32_t& reason){ this->onStateChanged(state, reason); }); + } + + ~Active_proxy() = default; + + virtual void onStateChanged(const uint32_t& state, const uint32_t& reason) = 0; + +public: + sdbus::ObjectPath Connection() + { + return proxy_.getProperty("Connection").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath SpecificObject() + { + return proxy_.getProperty("SpecificObject").onInterface(INTERFACE_NAME); + } + + std::string Id() + { + return proxy_.getProperty("Id").onInterface(INTERFACE_NAME); + } + + std::string Uuid() + { + return proxy_.getProperty("Uuid").onInterface(INTERFACE_NAME); + } + + std::string Type() + { + return proxy_.getProperty("Type").onInterface(INTERFACE_NAME); + } + + std::vector Devices() + { + return proxy_.getProperty("Devices").onInterface(INTERFACE_NAME); + } + + uint32_t State() + { + return proxy_.getProperty("State").onInterface(INTERFACE_NAME); + } + + uint32_t StateFlags() + { + return proxy_.getProperty("StateFlags").onInterface(INTERFACE_NAME); + } + + bool Default() + { + return proxy_.getProperty("Default").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Ip4Config() + { + return proxy_.getProperty("Ip4Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Dhcp4Config() + { + return proxy_.getProperty("Dhcp4Config").onInterface(INTERFACE_NAME); + } + + bool Default6() + { + return proxy_.getProperty("Default6").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Ip6Config() + { + return proxy_.getProperty("Ip6Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Dhcp6Config() + { + return proxy_.getProperty("Dhcp6Config").onInterface(INTERFACE_NAME); + } + + bool Vpn() + { + return proxy_.getProperty("Vpn").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Controller() + { + return proxy_.getProperty("Controller").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Master() + { + return proxy_.getProperty("Master").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}}} // namespaces + +#endif diff --git a/internal/platform/implementation/linux/org.freedesktop.NetworkManager.Connection.Active.xml b/internal/platform/implementation/linux/org.freedesktop.NetworkManager.Connection.Active.xml new file mode 100644 index 00000000..faab73a0 --- /dev/null +++ b/internal/platform/implementation/linux/org.freedesktop.NetworkManager.Connection.Active.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/internal/platform/implementation/linux/wifi_hotspot.cc b/internal/platform/implementation/linux/wifi_hotspot.cc new file mode 100644 index 00000000..61735b0f --- /dev/null +++ b/internal/platform/implementation/linux/wifi_hotspot.cc @@ -0,0 +1,191 @@ +#include +#include + +#include +#include + +#include "internal/platform/implementation/linux/dbus.h" +#include "internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h" +#include "internal/platform/implementation/linux/wifi_hotspot.h" +#include "internal/platform/implementation/linux/wifi_medium.h" +#include "internal/platform/implementation/wifi.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace linux { +bool NetworkManagerWifiHotspotMedium::ConnectWifiHotspot( + HotspotCredentials *hotspot_credentials) { + if (hotspot_credentials == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": hotspot_credentials cannot be null"; + return false; + } + + auto ssid = hotspot_credentials->GetSSID(); + auto password = hotspot_credentials->GetPassword(); + + return wireless_device_->ConnectToNetwork(ssid, password, + api::WifiAuthType::kWpaPsk) == + api::WifiConnectionStatus::kConnected; +} + +bool NetworkManagerWifiHotspotMedium::DisconnectWifiHotspot() { + if (!WifiHotspotActive()) { + NEARBY_LOGS(ERROR) << __func__ << ": WiFi hotspot is not active"; + return false; + } + + sdbus::ObjectPath active_ap_path; + + try { + active_ap_path = wireless_device_->ActiveAccessPoint(); + if (active_ap_path.empty()) { + NEARBY_LOGS(ERROR) << __func__ + << ": Not connected to any access points on " + << wireless_device_->getObjectPath(); + return false; + } + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(wireless_device_, "ActiveAccessPoint", e); + } + + auto object_manager = NetworkManagerObjectManager(system_bus_); + + auto objects = object_manager.GetManagedObjects(); + + for (auto &[path, interfaces] : objects) { + if (path.find("/org/freedesktop/NetworkManager/ActiveConnection/") == 0) { + if (interfaces.count(org::freedesktop::NetworkManager::Connection:: + Active_proxy::INTERFACE_NAME) == 1) { + sdbus::ObjectPath specific_object = + interfaces[org::freedesktop::NetworkManager::Connection:: + Active_proxy::INTERFACE_NAME]["SpecificObject"]; + if (specific_object == active_ap_path) { + NEARBY_LOGS(INFO) << __func__ << ": Deactivating active connection " + << active_ap_path; + + try { + network_manager_->DeactivateConnection(path); + } catch (const sdbus::Error &e) { + DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "DeactiveConnection", + e); + return false; + } + return true; + } + } + } + } + + NEARBY_LOGS(ERROR) + << __func__ + << ": Could not find an active connection with the access point " + << active_ap_path; + return false; +} + +bool NetworkManagerWifiHotspotMedium::StartWifiHotspot( + HotspotCredentials *hotspot_credentials) { + if (WifiHotspotActive()) { + NEARBY_LOGS(ERROR) << __func__ << ": " << wireless_device_->getObjectPath() + << ": cannot start WiFi hotspot, a hotspot is already " + "active on this device"; + return false; + } + + sd_id128_t id; + if (auto ret = sd_id128_randomize(&id); ret < 0) { + NEARBY_LOGS(ERROR) << __func__ << ": error generating a 128-bit ID: " + << std::strerror(ret); + return false; + } + std::string ssid = absl::StrCat("DIRECT-", SD_ID128_TO_STRING(id)); + ssid.resize(32); + hotspot_credentials->SetSSID(ssid); + + if (auto ret = sd_id128_randomize(&id); ret < 0) { + NEARBY_LOGS(ERROR) << __func__ << ": error generating a 128-bit ID: " + << std::strerror(ret); + return false; + } + std::string password = std::string(SD_ID128_TO_STRING(id), 15); + hotspot_credentials->SetPassword(password); + + if (auto ret = sd_id128_randomize(&id); ret < 0) { + NEARBY_LOGS(ERROR) << __func__ << ": error generating a 128-bit ID: " + << std::strerror(ret); + return false; + } + + std::vector ssid_bytes(ssid.begin(), ssid.end()); + std::map> + connection_settings{ + { + "connection", + std::map{ + {"uuid", SD_ID128_TO_UUID_STRING(id)}, + {"id", "Google Nearby Hotspot"}, + {"type", "802-11-wireless"}, + {"zone", "Public"}}, + }, + {"802-11-wireless", + std::map{ + {"assigned-mac-address", "random"}, + {"mode", "ap"}, + {"ssid", ssid_bytes}, + {"security", "802-11-wireless-security"}}}, + {"802-11-wireless-security", + std::map{ + {"group", std::vector{"ccmp"}}, + {"key-mgmt", "wpa-psk"}, + { + "pairwise", + std::vector{"ccmp"}, + }, + {"proto", std::vector{"rsn"}}, + {"psk", password}}}, + {"ipv4", std::map{"method", "shared"}}, + {"ipv6", std::map{ + {"addr-gen-mode", static_cast(1)}, + {"method", "shared"}, + }}}; + std::unique_ptr active_conn; + try { + auto [path, active_path, result] = network_manager_->AddAndActivateConnection2( + connection_settings, wireless_device_->getObjectPath(), "/", + {{"persist", "volatile"}, {"bind-activation", "dbus-client"}}); + active_conn = std::make_unique(system_bus_, + active_path); + } catch (const sdbus::Error &e) { + DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "AddAndActivateConnection2", + e); + return false; + } + + auto [reason, timeout] = active_conn->WaitForConnection(); + if (timeout) { + NEARBY_LOGS(ERROR) << __func__ << ": " + << ": timed out while waiting for connection " + << active_conn->getObjectPath() + << " to be activated, last NMActiveConnectionStateReason: " + << reason.value(); + DisconnectWifiHotspot(); + return false; + } + + NEARBY_LOGS(INFO) << __func__ << ": Started a WiFi hotspot on device " + << wireless_device_->getObjectPath() << " at " + << active_conn->getObjectPath(); + return true; +} + +bool NetworkManagerWifiHotspotMedium::WifiHotspotActive() { + try { + auto mode = wireless_device_->Mode(); + return mode == 3; // NM_802_11_MODE_AP + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(wireless_device_, "Mode", e); + return false; + } +} +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/wifi_hotspot.h b/internal/platform/implementation/linux/wifi_hotspot.h new file mode 100644 index 00000000..9f4f0f55 --- /dev/null +++ b/internal/platform/implementation/linux/wifi_hotspot.h @@ -0,0 +1,50 @@ +#ifndef PLATFORM_IMPL_LINUX_WIFI_HOTSPOT_H_ +#define PLATFORM_IMPL_LINUX_WIFI_HOTSPOT_H_ + +#include +#include + +#include "internal/platform/implementation/linux/wifi_medium.h" +#include "internal/platform/implementation/wifi_hotspot.h" + +namespace nearby { + namespace linux { + class NetworkManagerWifiHotspotMedium : api::WifiHotspotMedium { + public: + NetworkManagerWifiHotspotMedium( + sdbus::IConnection &system_bus, + std::shared_ptr network_manager, + const sdbus::ObjectPath &wireless_device_object_path) + : system_bus_(system_bus), + wireless_device_(std::make_unique( + network_manager, system_bus, wireless_device_object_path)), + network_manager_(network_manager) {} + ~NetworkManagerWifiHotspotMedium() {} + + bool IsInterfaceValid() const override { return true; } + std::unique_ptr + ConnectToService(absl::string_view ip_address, int port, + CancellationFlag *cancellation_flag) override; + std::unique_ptr + ListenForService(int port) override; + + bool StartWifiHotspot(HotspotCredentials *hotspot_credentials) override; + bool StopWifiHotspot() override; + + bool ConnectWifiHotspot(HotspotCredentials *hotspot_credentials) override; + bool DisconnectWifiHotspot() override; + + absl::optional> + GetDynamicPortRange() override {return absl::nullopt;} + + private: + bool WifiHotspotActive(); + + sdbus::IConnection &system_bus_; + std::unique_ptr wireless_device_; + std::shared_ptr network_manager_; + }; + } +} + +#endif diff --git a/internal/platform/implementation/linux/wifi_lan.cc b/internal/platform/implementation/linux/wifi_lan.cc index 2d5d3f4b..4e9df5ec 100644 --- a/internal/platform/implementation/linux/wifi_lan.cc +++ b/internal/platform/implementation/linux/wifi_lan.cc @@ -16,14 +16,15 @@ #include "internal/platform/implementation/linux/wifi_lan.h" #include "internal/platform/implementation/linux/wifi_lan_server_socket.h" #include "internal/platform/implementation/linux/wifi_lan_socket.h" +#include "internal/platform/implementation/linux/wifi_medium.h" #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/logging.h" namespace nearby { namespace linux { -WifiLanMedium::WifiLanMedium(sdbus::IConnection &system_bus, - NetworkManager &network_manager) - : system_bus_(system_bus), network_manager_(network_manager), +WifiLanMedium::WifiLanMedium(sdbus::IConnection &system_bus) + : system_bus_(system_bus), + network_manager_(std::make_shared(system_bus)), avahi_(std::make_shared(system_bus)), entry_group_(nullptr) {} @@ -34,7 +35,7 @@ WifiLanMedium::~WifiLanMedium() { } bool WifiLanMedium::IsNetworkConnected() const { - auto state = network_manager_.getState(); + auto state = network_manager_->getState(); return state >= 50; // NM_STATE_CONNECTED_LOCAL } @@ -171,7 +172,7 @@ bool WifiLanMedium::StopDiscovery(const std::string &service_type) { std::unique_ptr WifiLanMedium::ConnectToService(const std::string &ip_address, int port, - CancellationFlag *cancellation_flag) { + CancellationFlag *cancellation_flag) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { NEARBY_LOGS(ERROR) << __func__ @@ -180,7 +181,7 @@ WifiLanMedium::ConnectToService(const std::string &ip_address, int port, } NEARBY_LOGS(VERBOSE) << __func__ << ": Connecting to " << ip_address << ":" - << port; + << port; struct sockaddr_in addr; addr.sin_addr.s_addr = inet_addr(ip_address.c_str()); addr.sin_family = AF_INET; @@ -198,7 +199,7 @@ WifiLanMedium::ConnectToService(const std::string &ip_address, int port, return std::make_unique(std::move(fd)); } -std::unique_ptr ListenForService(int port = 0) { +std::unique_ptr WifiLanMedium::ListenForService(int port) { auto sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { NEARBY_LOGS(ERROR) << __func__ @@ -206,7 +207,7 @@ std::unique_ptr ListenForService(int port = 0) { return nullptr; } - NEARBY_LOGS(VERBOSE) << __func__ << "Listening for services "; + NEARBY_LOGS(VERBOSE) << __func__ << "Listening for services"; struct sockaddr_in addr; addr.sin_family = AF_INET; @@ -228,11 +229,10 @@ std::unique_ptr ListenForService(int port = 0) { return nullptr; } - return std::make_unique(sdbus::UnixFd(sock)); + return std::make_unique(sock, network_manager_); } -absl::optional> - GetDynamicPortRange() { +absl::optional> GetDynamicPortRange() { return absl::nullopt; } diff --git a/internal/platform/implementation/linux/wifi_lan.h b/internal/platform/implementation/linux/wifi_lan.h index 8059512e..24010163 100644 --- a/internal/platform/implementation/linux/wifi_lan.h +++ b/internal/platform/implementation/linux/wifi_lan.h @@ -13,8 +13,7 @@ namespace nearby { namespace linux { class WifiLanMedium : public api::WifiLanMedium { public: - WifiLanMedium(sdbus::IConnection &system_bus, - NetworkManager &network_manager); + WifiLanMedium(sdbus::IConnection &system_bus); ~WifiLanMedium() override; bool IsNetworkConnected() const override; @@ -42,7 +41,7 @@ private: sdbus::IConnection &system_bus_; - NetworkManager &network_manager_; + std::shared_ptr network_manager_; std::shared_ptr avahi_; std::unique_ptr entry_group_; diff --git a/internal/platform/implementation/linux/wifi_lan_server_socket.cc b/internal/platform/implementation/linux/wifi_lan_server_socket.cc index 71927deb..20313149 100644 --- a/internal/platform/implementation/linux/wifi_lan_server_socket.cc +++ b/internal/platform/implementation/linux/wifi_lan_server_socket.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,35 +6,54 @@ #include #include #include -#include #include -#include "internal/platform/implementation/linux/wifi_lan_server_socket.h" #include "internal/platform/exception.h" +#include "internal/platform/implementation/linux/dbus.h" +#include "internal/platform/implementation/linux/wifi_lan_server_socket.h" #include "internal/platform/implementation/linux/wifi_lan_socket.h" +#include "internal/platform/implementation/linux/wifi_medium.h" #include "internal/platform/logging.h" namespace nearby { namespace linux { std::string WifiLanServerSocket::GetIPAddress() const { - struct ifaddrs *addrs = nullptr; - getifaddrs(&addrs); + std::vector connection_paths; + try { + connection_paths = network_manager_->ActiveConnections(); + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(network_manager_, "ActiveConnections", e); + return std::string(); + } - for (auto ifaddr = addrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) { - if (ifaddr->ifa_addr == nullptr) { + for (auto &path : connection_paths) { + auto active_connection = + std::make_unique(system_bus_, path); + std::string conn_type; + try { + conn_type = active_connection->Type(); + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(active_connection, "Type", e); continue; } - if (ifaddr->ifa_addr->sa_family == AF_INET) { - auto addr = - &(reinterpret_cast(ifaddr->ifa_addr))->sin_addr; - char buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN); + if (conn_type == "802-11-wireless" || conn_type == "802-3-ethernet") { + auto ip4config_path = active_connection->Ip4Config(); + NetworkManagerIP4Config ip4config(system_bus_, ip4config_path); + std::vector> address_data; - return std::string(buf); + try { + address_data = ip4config.AddressData(); + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(&ip4config, "IP4Config", e); + } + + return address_data[0]["address"]; } } + NEARBY_LOGS(ERROR) + << __func__ << ": Could not find any active IP addresses for this device"; return std::string(); } diff --git a/internal/platform/implementation/linux/wifi_lan_server_socket.h b/internal/platform/implementation/linux/wifi_lan_server_socket.h index 5bed26ce..3e139721 100644 --- a/internal/platform/implementation/linux/wifi_lan_server_socket.h +++ b/internal/platform/implementation/linux/wifi_lan_server_socket.h @@ -3,30 +3,34 @@ #include +#include #include #include "internal/platform/exception.h" +#include "internal/platform/implementation/linux/wifi_medium.h" #include "internal/platform/implementation/wifi_lan.h" namespace nearby { namespace linux { class WifiLanServerSocket : public api::WifiLanServerSocket { public: - WifiLanServerSocket(int socket) { - fd_ = sdbus::UnixFd(socket); - } - + WifiLanServerSocket(int socket, + std::shared_ptr network_manager, + sdbus::IConnection &system_bus) + : fd_(sdbus::UnixFd(socket)), network_manager_(network_manager), + system_bus_(system_bus) {} ~WifiLanServerSocket() override = default; std::string GetIPAddress() const override; - int GetPort() const override; std::unique_ptr Accept() override; - Exception Close() override; +private: sdbus::UnixFd fd_; + std::shared_ptr network_manager_; + sdbus::IConnection &system_bus_; }; } // namespace linux } // namespace nearby diff --git a/internal/platform/implementation/linux/wifi_medium.cc b/internal/platform/implementation/linux/wifi_medium.cc index 8925356b..4aa0d5a8 100644 --- a/internal/platform/implementation/linux/wifi_medium.cc +++ b/internal/platform/implementation/linux/wifi_medium.cc @@ -7,9 +7,11 @@ #include #include #include +#include #include "absl/synchronization/mutex.h" #include "internal/platform/implementation/linux/dbus.h" +#include "internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h" #include "internal/platform/implementation/linux/networkmanager_device_wireless_client_glue.h" #include "internal/platform/implementation/linux/wifi_medium.h" #include "internal/platform/implementation/wifi.h" @@ -17,21 +19,60 @@ namespace nearby { namespace linux { +std::ostream &operator<<(std::ostream &s, + const ActiveConnectionStateReason &reason) { + switch (reason) { + case kStateReasonUnknown: + return s << "The reason for the active connection state change is unknown."; + case kStateReasonNone: + return s << "No reason was given for the active connection state change."; + case kStateReasonUserDisconnected: + return s << "The active connection changed state because the user " + "disconnected it."; + case kStateReasonDeviceDisconnected: + return s << "The active connection changed state because the device it was " + "using was disconnected."; + case kStateReasonServiceStopped: + return s << "The service providing the VPN connection was stopped."; + case kStateReasonIPConfigInvalid: + return s << "The IP config of the active connection was invalid."; + case kStateReasonConnectTimeout: + return s << "The connection attempt to the VPN service timed out."; + case kStateReasonServiceStartTimeout: + return s << "A timeout occurred while starting the service providing the " + "VPN connection."; + case kStateReasonServiceStartFailed: + return s << "Starting the service providing the VPN connection failed."; + case kStateReasonNoSecrets: + return s << "Necessary secrets for the connection were not provided."; + case kStateReasonLoginFailed: + return s << "Authentication to the server failed."; + case kStateReasonConnectionRemoved: + return s << "The connection was deleted from settings."; + case kStateReasonDependencyFailed: + return s << "Master connection of this connection failed to activate."; + case kStateReasonDeviceRealizeFailed: + return s << "Could not create the software device link."; + case kStateReasonDeviceRemoved: + return s << "The device this connection depended on disappeared."; + } +} + std::unique_ptr NetworkManagerObjectManager::GetIp4Config( - const sdbus::ObjectPath &access_point) { + const sdbus::ObjectPath &active_connection) { auto objects = GetManagedObjects(); for (auto &[object_path, interfaces] : objects) { if (object_path.find("/org/freedesktop/NetworkManager/ActiveConnection/", 0) == 0) { - if (interfaces.count( - "org.freedesktop.NetworkManager.Connection.Active") == 1) { - auto props = - interfaces["org.freedesktop.NetworkManager.Connection.Active"]; + if (interfaces.count(org::freedesktop::NetworkManager::Connection:: + Active_proxy::INTERFACE_NAME) == 1) { + auto props = interfaces[org::freedesktop::NetworkManager::Connection:: + Active_proxy::INTERFACE_NAME]; sdbus::ObjectPath specific_object = props["SpecificObject"]; sdbus::ObjectPath ip4config = props["Ip4Config"]; - if (specific_object == access_point) + if (specific_object == active_connection) return std::make_unique( getProxy().getConnection(), ip4config); } @@ -56,25 +97,29 @@ api::WifiCapability &NetworkManagerWifiMedium::GetCapability() { } api::WifiInformation &NetworkManagerWifiMedium::GetInformation() { - { - absl::ReaderMutexLock l(&active_access_point_lock_); - if (!active_access_point_.has_value()) { + std::unique_ptr active_access_point; + + try { + auto ap_path = ActiveAccessPoint(); + if (ap_path.empty()) { information_ = api::WifiInformation{false}; return information_; } + active_access_point = std::make_unique( + getProxy().getConnection(), ap_path); + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(this, "ActiveAccessPoint", e); } - try { - absl::MutexLock l(&active_access_point_lock_); - auto ssid_vec = active_access_point_->Ssid(); + try { + auto ssid_vec = active_access_point->Ssid(); std::string ssid{ssid_vec.begin(), ssid_vec.end()}; information_ = - api::WifiInformation{true, ssid, active_access_point_->HwAddress(), - (int32_t)(active_access_point_->Frequency())}; + api::WifiInformation{true, ssid, active_access_point->HwAddress(), + (int32_t)(active_access_point->Frequency())}; NetworkManagerObjectManager manager(getProxy().getConnection()); - auto ip4config = - manager.GetIp4Config(active_access_point_->getObjectPath()); + auto ip4config = manager.GetIp4Config(active_access_point->getObjectPath()); if (ip4config != nullptr) { auto address_data = ip4config->AddressData(); @@ -90,17 +135,16 @@ api::WifiInformation &NetworkManagerWifiMedium::GetInformation() { information_.ip_address_4_bytes = std::string(addr_bytes, 4); } } else { - NEARBY_LOGS(ERROR) << __func__ + NEARBY_LOGS(ERROR) << __func__ << ": " << getObjectPath() << ": Could not find the Ip4Config object for " - << active_access_point_->getObjectPath(); + << active_access_point->getObjectPath(); } } catch (const sdbus::Error &e) { - absl::ReaderMutexLock l(&active_access_point_lock_); NEARBY_LOGS(ERROR) - << __func__ << ": Got error '" << e.getName() << "' with message '" - << e.getMessage() + << __func__ << ": " << getObjectPath() << ": Got error '" << e.getName() + << "' with message '" << e.getMessage() << "' while populating network information for access point " - << active_access_point_->getObjectPath(); + << active_access_point->getObjectPath(); } return information_; @@ -115,8 +159,12 @@ void NetworkManagerWifiMedium::onPropertiesChanged( return; } - for (auto &[property, _val] : changedProperties) { + for (auto &[property, val] : changedProperties) { if (property == "LastScan") { + { + absl::MutexLock l(&last_scan_lock_); + last_scan_ = val; + } absl::ReaderMutexLock l(&scan_result_callback_lock_); if (scan_result_callback_.has_value()) { // scan_result_callback_->get().OnScanResults() @@ -140,23 +188,181 @@ bool NetworkManagerWifiMedium::Scan( return false; } +std::shared_ptr +NetworkManagerWifiMedium::SearchBySSIDNoScan( + std::vector &ssid_bytes) { + absl::ReaderMutexLock l(&known_access_points_lock_); + for (auto &[object_path, ap] : known_access_points_) { + if (ap->Ssid() == ssid_bytes) { + return ap; + } + } + + return nullptr; +} + +std::shared_ptr +NetworkManagerWifiMedium::SearchBySSID(absl::string_view ssid, + absl::Duration scan_timeout) { + std::vector ssid_bytes(ssid.begin(), ssid.end()); + // First, try to see if we already know an AP with this SSID. + auto ap = SearchBySSIDNoScan(ssid_bytes); + if (ap != nullptr) { + return ap; + } + + NEARBY_LOGS(INFO) << __func__ << ": " << getObjectPath() << ": SSID " << ssid + << " not currently known by device " << getObjectPath() + << ", requesting a scan"; + + std::int64_t cur_last_scan; + { + absl::ReaderMutexLock l(&last_scan_lock_); + cur_last_scan = last_scan_; + } + + // Otherwise, request a Scan first and wait for it to finish. + try { + RequestScan( + {{"ssids", std::vector>{ssid_bytes}}}); + } catch (const sdbus::Error &e) { + DBUS_LOG_METHOD_CALL_ERROR(this, "RequestScan", e); + } + + auto scan_finish = [cur_last_scan, this]() { + this->last_scan_lock_.AssertReaderHeld(); + return cur_last_scan != this->last_scan_; + }; + + absl::Condition cond(&scan_finish); + bool success = last_scan_lock_.ReaderLockWhenWithTimeout(cond, scan_timeout); + last_scan_lock_.ReaderUnlock(); + + if (!success) { + NEARBY_LOGS(WARNING) << __func__ << ": " << getObjectPath() + << ": timed out waiting for scan to finish"; + } + + ap = SearchBySSIDNoScan(ssid_bytes); + if (ap == nullptr) { + NEARBY_LOGS(WARNING) << __func__ << ": " << getObjectPath() + << ": Couldn't find SSID " << ssid; + } + + return ap; +} + +static inline std::pair +AuthAlgAndKeyMgmt(api::WifiAuthType auth_type) { + switch (auth_type) { + case api::WifiAuthType::kUnknown: + return {"open", "none"}; + case api::WifiAuthType::kOpen: + return {"open", "none"}; + case api::WifiAuthType::kWpaPsk: + return {"shared", "wpa-psk"}; + case api::WifiAuthType::kWep: + return {"none", "wep"}; + } +} + api::WifiConnectionStatus NetworkManagerWifiMedium::ConnectToNetwork(absl::string_view ssid, absl::string_view password, api::WifiAuthType auth_type) { - return api::WifiConnectionStatus::kUnknown; + + auto ap = SearchBySSID(ssid); + if (ap == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": " << getObjectPath() + << ": Couldn't find SSID " << ssid; + return api::WifiConnectionStatus::kConnectionFailure; + } + + std::vector ssid_bytes(ssid.begin(), ssid.end()); + std::string connection_id; + + { + sd_id128_t id; + if (auto ret = sd_id128_randomize(&id); ret < 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": could not generation a connection UUID"; + return api::WifiConnectionStatus::kUnknown; + } + connection_id = SD_ID128_TO_UUID_STRING(id); + } + + auto [auth_alg, key_mgmt] = AuthAlgAndKeyMgmt(auth_type); + + std::map> + connection_settings{ + {"connection", + std::map{ + {"uuid", connection_id}, + {"autoconnect", true}, + {"id", ssid}, + {"type", "802-11-wireless"}, + {"zone", "Public"}, + }}, + {"802-11-wireless", + std::map{ + {"ssid", ssid_bytes}, + {"mode", "infrastructure"}, + {"security", "802-11-wireless-security"}, + {"assigned-mac-address", "random"}, + }}, + {"802-11-wireless-security", + std::map{{"auth-alg", auth_alg}, + {"key-mgmt", key_mgmt}}}}; + if (!password.empty()) { + connection_settings["802-11-wireless-security"]["psk"] = + std::string(password); + } + + sdbus::ObjectPath connection_path, active_conn_path; + try { + auto [cp, acp, _r] = network_manager_->AddAndActivateConnection2( + connection_settings, getObjectPath(), ap->getObjectPath(), + {{"persist", "volatile"}, {"bind-activation", "dbus-client"}}); + connection_path = std::move(cp); + active_conn_path = std::move(acp); + } catch (const sdbus::Error &e) { + DBUS_LOG_METHOD_CALL_ERROR(this, "AddAndActivateConnection2", e); + return api::WifiConnectionStatus::kUnknown; + } + + NEARBY_LOGS(INFO) << __func__ << ": " << getObjectPath() + << ": Added a new connection at " << connection_path; + auto active_connection = NetworkManagerActiveConnection( + getProxy().getConnection(), active_conn_path); + auto [reason, timeout] = active_connection.WaitForConnection(); + if (timeout) { + NEARBY_LOGS(ERROR) + << __func__ << ": " << getObjectPath() + << ": timed out while waiting for connection " << active_conn_path + << " to be activated, last NMActiveConnectionStateReason: " + << reason.value(); + return api::WifiConnectionStatus::kUnknown; + } + + if (reason.has_value()) { + NEARBY_LOGS(ERROR) << __func__ << ": " << getObjectPath() << ": connection " + << active_conn_path + << " failed to activate, NMActiveConnectionStateReason:" + << *reason; + if (*reason == ActiveConnectionStateReason::kStateReasonNoSecrets || + *reason == ActiveConnectionStateReason::kStateReasonLoginFailed) + return api::WifiConnectionStatus::kAuthFailure; + } + + return api::WifiConnectionStatus::kConnected; } bool NetworkManagerWifiMedium::VerifyInternetConnectivity() { - auto network_manager_proxy_ = sdbus::createProxy( - "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager"); - network_manager_proxy_->finishRegistration(); - try { - std::uint32_t connectivity = network_manager_.CheckConnectivity(); + std::uint32_t connectivity = network_manager_->CheckConnectivity(); return connectivity == 4; // NM_CONNECTIVITY_FULL } catch (const sdbus::Error &e) { - DBUS_LOG_METHOD_CALL_ERROR(network_manager_proxy_, "CheckConnectivity", e); + DBUS_LOG_METHOD_CALL_ERROR(network_manager_, "CheckConnectivity", e); return false; } } diff --git a/internal/platform/implementation/linux/wifi_medium.h b/internal/platform/implementation/linux/wifi_medium.h index 97c1555e..2af8ff6b 100644 --- a/internal/platform/implementation/linux/wifi_medium.h +++ b/internal/platform/implementation/linux/wifi_medium.h @@ -3,20 +3,25 @@ #include #include +#include #include #include +#include #include #include #include #include #include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/linux/dbus.h" #include "internal/platform/implementation/linux/networkmanager_accesspoint_client_glue.h" #include "internal/platform/implementation/linux/networkmanager_client_glue.h" +#include "internal/platform/implementation/linux/networkmanager_connection_active_client_glue.h" #include "internal/platform/implementation/linux/networkmanager_device_wireless_client_glue.h" #include "internal/platform/implementation/linux/networkmanager_ip4config_client_glue.h" #include "internal/platform/implementation/wifi.h" +#include "internal/platform/logging.h" namespace nearby { namespace linux { @@ -27,6 +32,11 @@ public: : ProxyInterfaces(system_bus, "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager") { registerProxy(); + try { + state_ = State(); + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(this, "State", e); + } } ~NetworkManager() { unregisterProxy(); } @@ -91,19 +101,112 @@ public: ~NetworkManagerAccessPoint() { unregisterProxy(); } }; +enum ActiveConnectionState { + kStateUnknown = 0, + kStateActivating = 1, + kStateActivated = 2, + kStateDeactivating = 3, + kStateDeactivated = 4 +}; +enum ActiveConnectionStateReason { + kStateReasonUnknown = 0, + kStateReasonNone = 1, + kStateReasonUserDisconnected = 2, + kStateReasonDeviceDisconnected = 3, + kStateReasonServiceStopped = 4, + kStateReasonIPConfigInvalid = 5, + kStateReasonConnectTimeout = 6, + kStateReasonServiceStartTimeout = 7, + kStateReasonServiceStartFailed = 8, + kStateReasonNoSecrets = 9, + kStateReasonLoginFailed = 10, + kStateReasonConnectionRemoved = 11, + kStateReasonDependencyFailed = 12, + kStateReasonDeviceRealizeFailed = 13, + kStateReasonDeviceRemoved = 14, +}; + +extern std::ostream &operator<<(std::ostream &s, + const ActiveConnectionStateReason &reason); + +class NetworkManagerActiveConnection + : public sdbus::ProxyInterfaces< + org::freedesktop::NetworkManager::Connection::Active_proxy> { +public: + NetworkManagerActiveConnection( + sdbus::IConnection &system_bus, + const sdbus::ObjectPath &active_connection_path) + : ProxyInterfaces(system_bus, "org.freedesktop.NetworkManager", + active_connection_path) { + registerProxy(); + try { + auto state = State(); + if (state >= kStateUnknown && state <= kStateDeactivated) { + state_ = static_cast(state); + } + } catch (const sdbus::Error &e) { + DBUS_LOG_PROPERTY_GET_ERROR(this, "State", e); + } + } + ~NetworkManagerActiveConnection() { unregisterProxy(); } + +protected: + void onStateChanged(const uint32_t &state, const uint32_t &reason) override + ABSL_LOCKS_EXCLUDED(state_mutex_) { + absl::MutexLock l(&state_mutex_); + if (state >= kStateUnknown && state <= kStateDeactivated) { + state_ = static_cast(state); + } + if (reason >= kStateReasonUnknown && reason <= kStateReasonDeviceRemoved) { + reason_ = static_cast(reason); + } + } + +public: + std::pair, bool> + WaitForConnection(absl::Duration timeout = absl::Seconds(10)) + ABSL_LOCKS_EXCLUDED(state_mutex_) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Waiting for an update to " + << getObjectPath() << "'s state"; + + auto state_changed = [this]() { + this->state_mutex_.AssertReaderHeld(); + return this->state_ == kStateActivated || + this->state_ == kStateDeactivated; + }; + + absl::Condition cond(&state_changed); + auto success = state_mutex_.ReaderLockWhenWithTimeout(cond, timeout); + auto reason = reason_; + auto state = state_; + state_mutex_.ReaderUnlock(); + + if (!success) { + return {reason, true}; + } + + return state == kStateActivated ? std::pair{std::nullopt, false} + : std::pair{std::optional(reason), false}; + }; + +private: + absl::Mutex state_mutex_; + ActiveConnectionState state_ ABSL_GUARDED_BY(state_mutex_); + ActiveConnectionStateReason reason_ ABSL_GUARDED_BY(state_mutex_); +}; + class NetworkManagerWifiMedium : public api::WifiMedium, - sdbus::ProxyInterfaces< - org::freedesktop::NetworkManager::Device::Wireless_proxy, - sdbus::Properties_proxy> { + public sdbus::ProxyInterfaces< + org::freedesktop::NetworkManager::Device::Wireless_proxy, + sdbus::Properties_proxy> { public: - NetworkManagerWifiMedium(NetworkManager &network_manager, + NetworkManagerWifiMedium(std::shared_ptr network_manager, sdbus::IConnection &system_bus, const sdbus::ObjectPath &wireless_device_object_path) : ProxyInterfaces(system_bus, "org.freedesktop.NetworkManager", wireless_device_object_path), - network_manager_(network_manager) { - active_access_point_ = std::nullopt; + network_manager_(std::move(network_manager)) { registerProxy(); } @@ -121,10 +224,14 @@ public: bool IsInterfaceValid() const override { return true; }; api::WifiCapability &GetCapability() override; api::WifiInformation &GetInformation() override; - bool Scan( const api::WifiMedium::ScanResultCallback &scan_result_callback) override; + std::shared_ptr + SearchBySSID(absl::string_view ssid, + absl::Duration scan_timeout = absl::Seconds(15)) + ABSL_LOCKS_EXCLUDED(known_access_points_lock_); + api::WifiConnectionStatus ConnectToNetwork(absl::string_view ssid, absl::string_view password, api::WifiAuthType auth_type) override; @@ -138,19 +245,40 @@ protected: const std::map &changedProperties, const std::vector &invalidatedProperties) override; + void onAccessPointAdded(const sdbus::ObjectPath &access_point) override + ABSL_LOCKS_EXCLUDED(known_access_points_lock_) { + absl::MutexLock l(&known_access_points_lock_); + known_access_points_.erase(access_point); + known_access_points_.emplace(access_point, getProxy().getConnection(), + access_point); + } + void onAccessPointRemoved(const sdbus::ObjectPath &access_point) override + ABSL_LOCKS_EXCLUDED(known_access_points_lock_) { + absl::MutexLock l(&known_access_points_lock_); + known_access_points_.erase(access_point); + } + private: - NetworkManager &network_manager_; + std::shared_ptr + SearchBySSIDNoScan(std::vector &ssid) + ABSL_LOCKS_EXCLUDED(known_access_points_lock_); + + std::shared_ptr network_manager_; api::WifiCapability capability_; api::WifiInformation information_{false}; - absl::Mutex active_access_point_lock_; - std::optional active_access_point_; + absl::Mutex known_access_points_lock_; + std::map> + known_access_points_ ABSL_GUARDED_BY(known_access_points_lock_); absl::Mutex scan_result_callback_lock_; std::optional< std::reference_wrapper> - scan_result_callback_; + scan_result_callback_ ABSL_GUARDED_BY(scan_result_callback_lock_); + + absl::Mutex last_scan_lock_; + std::int64_t last_scan_ ABSL_GUARDED_BY(last_scan_lock_); }; } // namespace linux