Add additional WiFi code.

This commit is contained in:
Vibhav Pant
2023-08-23 00:48:22 +05:30
parent d58f8cad12
commit dd13c8b2c5
10 changed files with 982 additions and 73 deletions
@@ -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 <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
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<sdbus::ObjectPath> 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
@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/">
<!--
org.freedesktop.NetworkManager.Connection.Active:
@short_description: Active Connection.
Objects that implement the Connection.Active interface represent an
attempt to connect to a network using the details provided by a Connection
object. The Connection.Active object tracks the life-cycle of the
connection attempt and if successful indicates whether the connected
network is the "default" or preferred network for access. NetworkManager
has the concept of connections, which can be thought of as settings, a
profile or a configuration that can be applied on a networking device.
Such settings-connections are exposed as D-Bus object and the
active-connection expresses this relationship between device and
settings-connection. At any time a settings-connection can only be
activated on one device and vice versa. However, during activation and
deactivation multiple active-connections can reference the same device or
settings-connection as they are waiting to be activated or to be
deactivated.
-->
<interface name="org.freedesktop.NetworkManager.Connection.Active">
<annotation name="org.gtk.GDBus.C.Name" value="ActiveConnection"/>
<!--
Connection:
The path of the connection.
-->
<property name="Connection" type="o" access="read"/>
<!--
SpecificObject:
A specific object associated with the active connection. This property
reflects the specific object used during connection activation, and will
not change over the lifetime of the ActiveConnection once set.
-->
<property name="SpecificObject" type="o" access="read"/>
<!--
Id:
The ID of the connection, provided as a convenience so that clients do not
have to retrieve all connection details.
-->
<property name="Id" type="s" access="read"/>
<!--
Uuid:
The UUID of the connection, provided as a convenience so that clients do
not have to retrieve all connection details.
-->
<property name="Uuid" type="s" access="read"/>
<!--
Type:
The type of the connection, provided as a convenience so that clients do
not have to retrieve all connection details.
-->
<property name="Type" type="s" access="read"/>
<!--
Devices:
Array of object paths representing devices which are part of this active
connection.
-->
<property name="Devices" type="ao" access="read"/>
<!--
State:
The state of this active connection.
Returns: <link linkend="NMActiveConnectionState">NMActiveConnectionState</link>
-->
<property name="State" type="u" access="read"/>
<!--
StateFlags:
The state flags of this active connection.
Returns: <link linkend="NMActivationStateFlags">NMActivationStateFlags</link>
-->
<property name="StateFlags" type="u" access="read"/>
<!--
StateChanged:
@state: (<link linkend="NMActiveConnectionState">NMActiveConnectionState</link>) The new state of the active connection.
@reason: (<link linkend="NMActiveConnectionStateReason">NMActiveConnectionStateReason</link>) Reason code describing the change to the new state.
@since: 1.8
Emitted when the state of the active connection has changed.
-->
<signal name="StateChanged">
<arg name="state" type="u"/>
<arg name="reason" type="u"/>
</signal>
<!--
Default:
Whether this active connection is the default IPv4 connection, i.e.
whether it currently owns the default IPv4 route.
-->
<property name="Default" type="b" access="read"/>
<!--
Ip4Config:
Object path of the Ip4Config object describing the configuration of the
connection. Only valid when the connection is in the
NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
-->
<property name="Ip4Config" type="o" access="read"/>
<!--
Dhcp4Config:
Object path of the Dhcp4Config object describing the DHCP options returned
by the DHCP server (assuming the connection used DHCP). Only valid when
the connection is in the NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
-->
<property name="Dhcp4Config" type="o" access="read"/>
<!--
Default6:
Whether this active connection is the default IPv6 connection, i.e.
whether it currently owns the default IPv6 route.
-->
<property name="Default6" type="b" access="read"/>
<!--
Ip6Config:
Object path of the Ip6Config object describing the configuration of the
connection. Only valid when the connection is in the
NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
-->
<property name="Ip6Config" type="o" access="read"/>
<!--
Dhcp6Config:
Object path of the Dhcp6Config object describing the DHCP options returned
by the DHCP server (assuming the connection used DHCP). Only valid when
the connection is in the NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
-->
<property name="Dhcp6Config" type="o" access="read"/>
<!--
Vpn:
Whether this active connection is also a VPN connection.
-->
<property name="Vpn" type="b" access="read"/>
<!--
Controller:
@Since: 1.44, 1.42.2
The path to the controller device if the connection is a port. This
property replaces the deprecated 'Master' property.
-->
<property name="Controller" type="o" access="read"/>
<!--
Master:
The path to the controller device if the connection is a port.
This property is deprecated in favor of the 'Controller'
property since 1.44 and 1.42.2.
-->
<property name="Master" type="o" access="read"/>
</interface>
</node>
@@ -0,0 +1,191 @@
#include <cstring>
#include <memory>
#include <random>
#include <systemd/sd-id128.h>
#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<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
std::map<std::string, std::map<std::string, sdbus::Variant>>
connection_settings{
{
"connection",
std::map<std::string, sdbus::Variant>{
{"uuid", SD_ID128_TO_UUID_STRING(id)},
{"id", "Google Nearby Hotspot"},
{"type", "802-11-wireless"},
{"zone", "Public"}},
},
{"802-11-wireless",
std::map<std::string, sdbus::Variant>{
{"assigned-mac-address", "random"},
{"mode", "ap"},
{"ssid", ssid_bytes},
{"security", "802-11-wireless-security"}}},
{"802-11-wireless-security",
std::map<std::string, sdbus::Variant>{
{"group", std::vector<std::string>{"ccmp"}},
{"key-mgmt", "wpa-psk"},
{
"pairwise",
std::vector<std::string>{"ccmp"},
},
{"proto", std::vector<std::string>{"rsn"}},
{"psk", password}}},
{"ipv4", std::map<std::string, sdbus::Variant>{"method", "shared"}},
{"ipv6", std::map<std::string, sdbus::Variant>{
{"addr-gen-mode", static_cast<std::int32_t>(1)},
{"method", "shared"},
}}};
std::unique_ptr<NetworkManagerActiveConnection> 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<NetworkManagerActiveConnection>(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
@@ -0,0 +1,50 @@
#ifndef PLATFORM_IMPL_LINUX_WIFI_HOTSPOT_H_
#define PLATFORM_IMPL_LINUX_WIFI_HOTSPOT_H_
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Types.h>
#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<NetworkManager> network_manager,
const sdbus::ObjectPath &wireless_device_object_path)
: system_bus_(system_bus),
wireless_device_(std::make_unique<NetworkManagerWifiMedium>(
network_manager, system_bus, wireless_device_object_path)),
network_manager_(network_manager) {}
~NetworkManagerWifiHotspotMedium() {}
bool IsInterfaceValid() const override { return true; }
std::unique_ptr<api::WifiHotspotSocket>
ConnectToService(absl::string_view ip_address, int port,
CancellationFlag *cancellation_flag) override;
std::unique_ptr<api::WifiHotspotServerSocket>
ListenForService(int port) override;
bool StartWifiHotspot(HotspotCredentials *hotspot_credentials) override;
bool StopWifiHotspot() override;
bool ConnectWifiHotspot(HotspotCredentials *hotspot_credentials) override;
bool DisconnectWifiHotspot() override;
absl::optional<std::pair<std::int32_t, std::int32_t>>
GetDynamicPortRange() override {return absl::nullopt;}
private:
bool WifiHotspotActive();
sdbus::IConnection &system_bus_;
std::unique_ptr<NetworkManagerWifiMedium> wireless_device_;
std::shared_ptr<NetworkManager> network_manager_;
};
}
}
#endif
@@ -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<linux::NetworkManager>(system_bus)),
avahi_(std::make_shared<avahi::Server>(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<api::WifiLanSocket>
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<WifiLanSocket>(std::move(fd));
}
std::unique_ptr<api::WifiLanServerSocket> ListenForService(int port = 0) {
std::unique_ptr<api::WifiLanServerSocket> 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<api::WifiLanServerSocket> 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<api::WifiLanServerSocket> ListenForService(int port = 0) {
return nullptr;
}
return std::make_unique<WifiLanServerSocket>(sdbus::UnixFd(sock));
return std::make_unique<WifiLanServerSocket>(sock, network_manager_);
}
absl::optional<std::pair<std::int32_t, std::int32_t>>
GetDynamicPortRange() {
absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange() {
return absl::nullopt;
}
@@ -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<NetworkManager> network_manager_;
std::shared_ptr<avahi::Server> avahi_;
std::unique_ptr<avahi::EntryGroup> entry_group_;
@@ -1,3 +1,4 @@
#include <arpa/inet.h>
#include <cerrno>
#include <cstring>
#include <ifaddrs.h>
@@ -5,35 +6,54 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sdbus-c++/Types.h>
#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<sdbus::ObjectPath> 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<NetworkManagerActiveConnection>(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<struct sockaddr_in *>(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<std::map<std::string, sdbus::Variant>> 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();
}
@@ -3,30 +3,34 @@
#include <netinet/in.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Types.h>
#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<NetworkManager> 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<api::WifiLanSocket> Accept() override;
Exception Close() override;
private:
sdbus::UnixFd fd_;
std::shared_ptr<NetworkManager> network_manager_;
sdbus::IConnection &system_bus_;
};
} // namespace linux
} // namespace nearby
@@ -7,9 +7,11 @@
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include <systemd/sd-id128.h>
#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<NetworkManagerIP4Config>
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<NetworkManagerIP4Config>(
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<NetworkManagerAccessPoint> active_access_point;
try {
auto ap_path = ActiveAccessPoint();
if (ap_path.empty()) {
information_ = api::WifiInformation{false};
return information_;
}
active_access_point = std::make_unique<NetworkManagerAccessPoint>(
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<NetworkManagerAccessPoint>
NetworkManagerWifiMedium::SearchBySSIDNoScan(
std::vector<std::uint8_t> &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<NetworkManagerAccessPoint>
NetworkManagerWifiMedium::SearchBySSID(absl::string_view ssid,
absl::Duration scan_timeout) {
std::vector<std::uint8_t> 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<std::vector<std::uint8_t>>{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<std::string, std::string>
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<std::uint8_t> 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<std::string, std::map<std::string, sdbus::Variant>>
connection_settings{
{"connection",
std::map<std::string, sdbus::Variant>{
{"uuid", connection_id},
{"autoconnect", true},
{"id", ssid},
{"type", "802-11-wireless"},
{"zone", "Public"},
}},
{"802-11-wireless",
std::map<std::string, sdbus::Variant>{
{"ssid", ssid_bytes},
{"mode", "infrastructure"},
{"security", "802-11-wireless-security"},
{"assigned-mac-address", "random"},
}},
{"802-11-wireless-security",
std::map<std::string, sdbus::Variant>{{"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;
}
}
@@ -3,20 +3,25 @@
#include <atomic>
#include <functional>
#include <list>
#include <memory>
#include <optional>
#include <ostream>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#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<ActiveConnectionState>(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<ActiveConnectionState>(state);
}
if (reason >= kStateReasonUnknown && reason <= kStateReasonDeviceRemoved) {
reason_ = static_cast<ActiveConnectionStateReason>(reason);
}
}
public:
std::pair<std::optional<ActiveConnectionStateReason>, 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<NetworkManager> 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<NetworkManagerAccessPoint>
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<std::string, sdbus::Variant> &changedProperties,
const std::vector<std::string> &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<NetworkManagerAccessPoint>
SearchBySSIDNoScan(std::vector<std::uint8_t> &ssid)
ABSL_LOCKS_EXCLUDED(known_access_points_lock_);
std::shared_ptr<NetworkManager> network_manager_;
api::WifiCapability capability_;
api::WifiInformation information_{false};
absl::Mutex active_access_point_lock_;
std::optional<NetworkManagerAccessPoint> active_access_point_;
absl::Mutex known_access_points_lock_;
std::map<sdbus::ObjectPath, std::shared_ptr<NetworkManagerAccessPoint>>
known_access_points_ ABSL_GUARDED_BY(known_access_points_lock_);
absl::Mutex scan_result_callback_lock_;
std::optional<
std::reference_wrapper<const api::WifiMedium::ScanResultCallback>>
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