# include "selfdrive/ui/qt/offroad/wifiManager.h"
# include <stdlib.h>
# include <algorithm>
# include <set>
# include "selfdrive/common/params.h"
# include "selfdrive/common/swaglog.h"
/**
* We are using a NetworkManager DBUS API : https : //developer.gnome.org/NetworkManager/1.26/spec.html
* */
// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApFlags
const int NM_802_11_AP_FLAGS_PRIVACY = 0x00000001 ;
// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApSecurityFlags
const int NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001 ;
const int NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002 ;
const int NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010 ;
const int NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020 ;
const int NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100 ;
const int NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200 ;
const QString nm_path = " /org/freedesktop/NetworkManager " ;
const QString nm_settings_path = " /org/freedesktop/NetworkManager/Settings " ;
const QString nm_iface = " org.freedesktop.NetworkManager " ;
const QString props_iface = " org.freedesktop.DBus.Properties " ;
const QString nm_settings_iface = " org.freedesktop.NetworkManager.Settings " ;
const QString nm_settings_conn_iface = " org.freedesktop.NetworkManager.Settings.Connection " ;
const QString device_iface = " org.freedesktop.NetworkManager.Device " ;
const QString wireless_device_iface = " org.freedesktop.NetworkManager.Device.Wireless " ;
const QString ap_iface = " org.freedesktop.NetworkManager.AccessPoint " ;
const QString connection_iface = " org.freedesktop.NetworkManager.Connection.Active " ;
const QString ipv4config_iface = " org.freedesktop.NetworkManager.IP4Config " ;
const QString nm_service = " org.freedesktop.NetworkManager " ;
const int state_connected = 100 ;
const int state_need_auth = 60 ;
const int reason_wrong_password = 8 ;
const int dbus_timeout = 100 ;
template < typename T >
T get_response ( QDBusMessage response ) {
QVariant first = response . arguments ( ) . at ( 0 ) ;
QDBusVariant dbvFirst = first . value < QDBusVariant > ( ) ;
QVariant vFirst = dbvFirst . variant ( ) ;
if ( vFirst . canConvert < T > ( ) ) {
return vFirst . value < T > ( ) ;
} else {
LOGE ( " Variant unpacking failure " ) ;
return T ( ) ;
}
}
bool compare_by_strength ( const Network & a , const Network & b ) {
if ( a . connected = = ConnectedType : : CONNECTED ) return true ;
if ( b . connected = = ConnectedType : : CONNECTED ) return false ;
if ( a . connected = = ConnectedType : : CONNECTING ) return true ;
if ( b . connected = = ConnectedType : : CONNECTING ) return false ;
return a . strength > b . strength ;
}
WifiManager : : WifiManager ( QWidget * parent ) {
qDBusRegisterMetaType < Connection > ( ) ;
qDBusRegisterMetaType < IpConfig > ( ) ;
connecting_to_network = " " ;
adapter = get_adapter ( ) ;
bool has_adapter = adapter ! = " " ;
if ( ! has_adapter ) {
throw std : : runtime_error ( " Error connecting to NetworkManager " ) ;
}
QDBusInterface nm ( nm_service , adapter , device_iface , bus ) ;
bus . connect ( nm_service , adapter , device_iface , " StateChanged " , this , SLOT ( change ( unsigned int , unsigned int , unsigned int ) ) ) ;
QDBusInterface device_props ( nm_service , adapter , props_iface , bus ) ;
device_props . setTimeout ( dbus_timeout ) ;
QDBusMessage response = device_props . call ( " Get " , device_iface , " State " ) ;
raw_adapter_state = get_response < uint > ( response ) ;
change ( raw_adapter_state , 0 , 0 ) ;
// Set tethering ssid as "weedle" + first 4 characters of a dongle id
tethering_ssid = " weedle " ;
std : : string bytes = Params ( ) . get ( " DongleId " ) ;
if ( bytes . length ( ) > = 4 ) {
tethering_ssid + = " - " + QString : : fromStdString ( bytes . substr ( 0 , 4 ) ) ;
}
// Create dbus interface for tethering button. This populates the introspection cache,
// making sure all future creations are non-blocking
// https://bugreports.qt.io/browse/QTBUG-14485
QDBusInterface ( nm_service , nm_settings_path , nm_settings_iface , bus ) ;
}
void WifiManager : : refreshNetworks ( ) {
seen_networks . clear ( ) ;
seen_ssids . clear ( ) ;
ipv4_address = get_ipv4_address ( ) ;
for ( Network & network : get_networks ( ) ) {
if ( seen_ssids . count ( network . ssid ) ) {
continue ;
}
seen_ssids . push_back ( network . ssid ) ;
seen_networks . push_back ( network ) ;
}
}
QString WifiManager : : get_ipv4_address ( ) {
if ( raw_adapter_state ! = state_connected ) {
return " " ;
}
QVector < QDBusObjectPath > conns = get_active_connections ( ) ;
for ( auto & p : conns ) {
QString active_connection = p . path ( ) ;
QDBusInterface nm ( nm_service , active_connection , props_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
QDBusObjectPath pth = get_response < QDBusObjectPath > ( nm . call ( " Get " , connection_iface , " Ip4Config " ) ) ;
QString ip4config = pth . path ( ) ;
QString type = get_response < QString > ( nm . call ( " Get " , connection_iface , " Type " ) ) ;
if ( type = = " 802-11-wireless " ) {
QDBusInterface nm2 ( nm_service , ip4config , props_iface , bus ) ;
nm2 . setTimeout ( dbus_timeout ) ;
const QDBusArgument & arr = get_response < QDBusArgument > ( nm2 . call ( " Get " , ipv4config_iface , " AddressData " ) ) ;
QMap < QString , QVariant > pth2 ;
arr . beginArray ( ) ;
while ( ! arr . atEnd ( ) ) {
arr > > pth2 ;
QString ipv4 = pth2 . value ( " address " ) . value < QString > ( ) ;
arr . endArray ( ) ;
return ipv4 ;
}
arr . endArray ( ) ;
}
}
return " " ;
}
QList < Network > WifiManager : : get_networks ( ) {
QList < Network > r ;
QDBusInterface nm ( nm_service , adapter , wireless_device_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm . call ( " GetAllAccessPoints " ) ;
QVariant first = response . arguments ( ) . at ( 0 ) ;
QString active_ap = get_active_ap ( ) ;
const QDBusArgument & args = first . value < QDBusArgument > ( ) ;
args . beginArray ( ) ;
while ( ! args . atEnd ( ) ) {
QDBusObjectPath path ;
args > > path ;
QByteArray ssid = get_property ( path . path ( ) , " Ssid " ) ;
unsigned int strength = get_ap_strength ( path . path ( ) ) ;
SecurityType security = getSecurityType ( path . path ( ) ) ;
ConnectedType ctype ;
if ( path . path ( ) ! = active_ap ) {
ctype = ConnectedType : : DISCONNECTED ;
} else {
if ( ssid = = connecting_to_network ) {
ctype = ConnectedType : : CONNECTING ;
} else {
ctype = ConnectedType : : CONNECTED ;
}
}
Network network = { path . path ( ) , ssid , strength , ctype , security } ;
if ( ssid . length ( ) ) {
r . push_back ( network ) ;
}
}
args . endArray ( ) ;
std : : sort ( r . begin ( ) , r . end ( ) , compare_by_strength ) ;
return r ;
}
SecurityType WifiManager : : getSecurityType ( QString path ) {
int sflag = get_property ( path , " Flags " ) . toInt ( ) ;
int wpaflag = get_property ( path , " WpaFlags " ) . toInt ( ) ;
int rsnflag = get_property ( path , " RsnFlags " ) . toInt ( ) ;
int wpa_props = wpaflag | rsnflag ;
// obtained by looking at flags of networks in the office as reported by an Android phone
const int supports_wpa = NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104 | NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104 | NM_802_11_AP_SEC_KEY_MGMT_PSK ;
if ( sflag = = 0 ) {
return SecurityType : : OPEN ;
} else if ( ( sflag & NM_802_11_AP_FLAGS_PRIVACY ) & & ( wpa_props & supports_wpa ) & & ! ( wpa_props & NM_802_11_AP_SEC_KEY_MGMT_802_1X ) ) {
return SecurityType : : WPA ;
} else {
return SecurityType : : UNSUPPORTED ;
}
}
void WifiManager : : connect ( Network n ) {
return connect ( n , " " , " " ) ;
}
void WifiManager : : connect ( Network n , QString password ) {
return connect ( n , " " , password ) ;
}
void WifiManager : : connect ( Network n , QString username , QString password ) {
connecting_to_network = n . ssid ;
// disconnect();
clear_connections ( n . ssid ) ; //Clear all connections that may already exist to the network we are connecting
connect ( n . ssid , username , password , n . security_type ) ;
}
void WifiManager : : connect ( QByteArray ssid , QString username , QString password , SecurityType security_type ) {
Connection connection ;
connection [ " connection " ] [ " type " ] = " 802-11-wireless " ;
connection [ " connection " ] [ " uuid " ] = QUuid : : createUuid ( ) . toString ( ) . remove ( ' { ' ) . remove ( ' } ' ) ;
connection [ " connection " ] [ " id " ] = " openpilot connection " + QString : : fromStdString ( ssid . toStdString ( ) ) ;
connection [ " connection " ] [ " autoconnect-retries " ] = 0 ;
connection [ " 802-11-wireless " ] [ " ssid " ] = ssid ;
connection [ " 802-11-wireless " ] [ " mode " ] = " infrastructure " ;
if ( security_type = = SecurityType : : WPA ) {
connection [ " 802-11-wireless-security " ] [ " key-mgmt " ] = " wpa-psk " ;
connection [ " 802-11-wireless-security " ] [ " auth-alg " ] = " open " ;
connection [ " 802-11-wireless-security " ] [ " psk " ] = password ;
}
connection [ " ipv4 " ] [ " method " ] = " auto " ;
connection [ " ipv6 " ] [ " method " ] = " ignore " ;
QDBusInterface nm_settings ( nm_service , nm_settings_path , nm_settings_iface , bus ) ;
nm_settings . setTimeout ( dbus_timeout ) ;
nm_settings . call ( " AddConnection " , QVariant : : fromValue ( connection ) ) ;
activate_wifi_connection ( QString ( ssid ) ) ;
}
void WifiManager : : deactivate_connections ( QString ssid ) {
for ( QDBusObjectPath active_connection_raw : get_active_connections ( ) ) {
QString active_connection = active_connection_raw . path ( ) ;
QDBusInterface nm ( nm_service , active_connection , props_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
QDBusObjectPath pth = get_response < QDBusObjectPath > ( nm . call ( " Get " , connection_iface , " SpecificObject " ) ) ;
if ( pth . path ( ) ! = " " & & pth . path ( ) ! = " / " ) {
QString Ssid = get_property ( pth . path ( ) , " Ssid " ) ;
if ( Ssid = = ssid ) {
QDBusInterface nm2 ( nm_service , nm_path , nm_iface , bus ) ;
nm2 . setTimeout ( dbus_timeout ) ;
nm2 . call ( " DeactivateConnection " , QVariant : : fromValue ( active_connection_raw ) ) ; // TODO change to disconnect
}
}
}
}
QVector < QDBusObjectPath > WifiManager : : get_active_connections ( ) {
QDBusInterface nm ( nm_service , nm_path , props_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm . call ( " Get " , nm_iface , " ActiveConnections " ) ;
const QDBusArgument & arr = get_response < QDBusArgument > ( response ) ;
QVector < QDBusObjectPath > conns ;
QDBusObjectPath path ;
arr . beginArray ( ) ;
while ( ! arr . atEnd ( ) ) {
arr > > path ;
conns . push_back ( path ) ;
}
arr . endArray ( ) ;
return conns ;
}
void WifiManager : : clear_connections ( QString ssid ) {
for ( QDBusObjectPath path : list_connections ( ) ) {
QDBusInterface nm2 ( nm_service , path . path ( ) , nm_settings_conn_iface , bus ) ;
nm2 . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm2 . call ( " GetSettings " ) ;
const QDBusArgument & dbusArg = response . arguments ( ) . at ( 0 ) . value < QDBusArgument > ( ) ;
QMap < QString , QMap < QString , QVariant > > map ;
dbusArg > > map ;
for ( auto & inner : map ) {
for ( auto & val : inner ) {
QString key = inner . key ( val ) ;
if ( key = = " ssid " ) {
if ( val = = ssid ) {
nm2 . call ( " Delete " ) ;
}
}
}
}
}
}
void WifiManager : : request_scan ( ) {
QDBusInterface nm ( nm_service , adapter , wireless_device_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
nm . call ( " RequestScan " , QVariantMap ( ) ) ;
}
uint WifiManager : : get_wifi_device_state ( ) {
QDBusInterface device_props ( nm_service , adapter , props_iface , bus ) ;
device_props . setTimeout ( dbus_timeout ) ;
QDBusMessage response = device_props . call ( " Get " , device_iface , " State " ) ;
uint resp = get_response < uint > ( response ) ;
return resp ;
}
QString WifiManager : : get_active_ap ( ) {
QDBusInterface device_props ( nm_service , adapter , props_iface , bus ) ;
device_props . setTimeout ( dbus_timeout ) ;
QDBusMessage response = device_props . call ( " Get " , wireless_device_iface , " ActiveAccessPoint " ) ;
QDBusObjectPath r = get_response < QDBusObjectPath > ( response ) ;
return r . path ( ) ;
}
QByteArray WifiManager : : get_property ( QString network_path , QString property ) {
QDBusInterface device_props ( nm_service , network_path , props_iface , bus ) ;
device_props . setTimeout ( dbus_timeout ) ;
QDBusMessage response = device_props . call ( " Get " , ap_iface , property ) ;
return get_response < QByteArray > ( response ) ;
}
unsigned int WifiManager : : get_ap_strength ( QString network_path ) {
QDBusInterface device_props ( nm_service , network_path , props_iface , bus ) ;
device_props . setTimeout ( dbus_timeout ) ;
QDBusMessage response = device_props . call ( " Get " , ap_iface , " Strength " ) ;
return get_response < unsigned int > ( response ) ;
}
QString WifiManager : : get_adapter ( ) {
QDBusInterface nm ( nm_service , nm_path , nm_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm . call ( " GetDevices " ) ;
QVariant first = response . arguments ( ) . at ( 0 ) ;
QString adapter_path = " " ;
const QDBusArgument & args = first . value < QDBusArgument > ( ) ;
args . beginArray ( ) ;
while ( ! args . atEnd ( ) ) {
QDBusObjectPath path ;
args > > path ;
// Get device type
QDBusInterface device_props ( nm_service , path . path ( ) , props_iface , bus ) ;
device_props . setTimeout ( dbus_timeout ) ;
QDBusMessage response = device_props . call ( " Get " , device_iface , " DeviceType " ) ;
uint device_type = get_response < uint > ( response ) ;
if ( device_type = = 2 ) { // Wireless
adapter_path = path . path ( ) ;
break ;
}
}
args . endArray ( ) ;
return adapter_path ;
}
void WifiManager : : change ( unsigned int new_state , unsigned int previous_state , unsigned int change_reason ) {
raw_adapter_state = new_state ;
if ( new_state = = state_need_auth & & change_reason = = reason_wrong_password ) {
emit wrongPassword ( connecting_to_network ) ;
} else if ( new_state = = state_connected ) {
emit successfulConnection ( connecting_to_network ) ;
connecting_to_network = " " ;
}
}
void WifiManager : : disconnect ( ) {
QString active_ap = get_active_ap ( ) ;
if ( active_ap ! = " " & & active_ap ! = " / " ) {
deactivate_connections ( get_property ( active_ap , " Ssid " ) ) ;
}
}
QVector < QDBusObjectPath > WifiManager : : list_connections ( ) {
QVector < QDBusObjectPath > connections ;
QDBusInterface nm ( nm_service , nm_settings_path , nm_settings_iface , bus ) ;
nm . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm . call ( " ListConnections " ) ;
QVariant first = response . arguments ( ) . at ( 0 ) ;
const QDBusArgument & args = first . value < QDBusArgument > ( ) ;
args . beginArray ( ) ;
while ( ! args . atEnd ( ) ) {
QDBusObjectPath path ;
args > > path ;
connections . push_back ( path ) ;
}
return connections ;
}
bool WifiManager : : activate_wifi_connection ( QString ssid ) {
QString devicePath = get_adapter ( ) ;
for ( QDBusObjectPath path : list_connections ( ) ) {
QDBusInterface nm2 ( nm_service , path . path ( ) , nm_settings_conn_iface , bus ) ;
nm2 . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm2 . call ( " GetSettings " ) ;
const QDBusArgument & dbusArg = response . arguments ( ) . at ( 0 ) . value < QDBusArgument > ( ) ;
QMap < QString , QMap < QString , QVariant > > map ;
dbusArg > > map ;
for ( auto & inner : map ) {
for ( auto & val : inner ) {
QString key = inner . key ( val ) ;
if ( key = = " ssid " ) {
if ( val = = ssid ) {
QDBusInterface nm3 ( nm_service , nm_path , nm_iface , bus ) ;
nm3 . setTimeout ( dbus_timeout ) ;
nm3 . call ( " ActivateConnection " , QVariant : : fromValue ( path ) , QVariant : : fromValue ( QDBusObjectPath ( devicePath ) ) , QVariant : : fromValue ( QDBusObjectPath ( " / " ) ) ) ;
return true ;
}
}
}
}
}
return false ;
}
//Functions for tethering
bool WifiManager : : activate_tethering_connection ( ) {
QString devicePath = get_adapter ( ) ;
for ( QDBusObjectPath path : list_connections ( ) ) {
QDBusInterface nm2 ( nm_service , path . path ( ) , nm_settings_conn_iface , bus ) ;
nm2 . setTimeout ( dbus_timeout ) ;
QDBusMessage response = nm2 . call ( " GetSettings " ) ;
const QDBusArgument & dbusArg = response . arguments ( ) . at ( 0 ) . value < QDBusArgument > ( ) ;
QMap < QString , QMap < QString , QVariant > > map ;
dbusArg > > map ;
for ( auto & inner : map ) {
for ( auto & val : inner ) {
QString key = inner . key ( val ) ;
if ( key = = " ssid " ) {
if ( val = = tethering_ssid . toUtf8 ( ) ) {
QDBusInterface nm3 ( nm_service , nm_path , nm_iface , bus ) ;
nm3 . setTimeout ( dbus_timeout ) ;
nm3 . call ( " ActivateConnection " , QVariant : : fromValue ( path ) , QVariant : : fromValue ( QDBusObjectPath ( devicePath ) ) , QVariant : : fromValue ( QDBusObjectPath ( " / " ) ) ) ;
return true ;
}
}
}
}
}
return false ;
}
void WifiManager : : addTetheringConnection ( ) {
Connection connection ;
connection [ " connection " ] [ " id " ] = " Hotspot " ;
connection [ " connection " ] [ " uuid " ] = QUuid : : createUuid ( ) . toString ( ) . remove ( ' { ' ) . remove ( ' } ' ) ;
connection [ " connection " ] [ " type " ] = " 802-11-wireless " ;
connection [ " connection " ] [ " interface-name " ] = " wlan0 " ;
connection [ " connection " ] [ " autoconnect " ] = false ;
connection [ " 802-11-wireless " ] [ " band " ] = " bg " ;
connection [ " 802-11-wireless " ] [ " mode " ] = " ap " ;
connection [ " 802-11-wireless " ] [ " ssid " ] = tethering_ssid . toUtf8 ( ) ;
connection [ " 802-11-wireless-security " ] [ " group " ] = QStringList ( " ccmp " ) ;
connection [ " 802-11-wireless-security " ] [ " key-mgmt " ] = " wpa-psk " ;
connection [ " 802-11-wireless-security " ] [ " pairwise " ] = QStringList ( " ccmp " ) ;
connection [ " 802-11-wireless-security " ] [ " proto " ] = QStringList ( " rsn " ) ;
connection [ " 802-11-wireless-security " ] [ " psk " ] = tetheringPassword ;
connection [ " ipv4 " ] [ " method " ] = " shared " ;
QMap < QString , QVariant > address ;
address [ " address " ] = " 192.168.43.1 " ;
address [ " prefix " ] = 24u ;
connection [ " ipv4 " ] [ " address-data " ] = QVariant : : fromValue ( IpConfig ( ) < < address ) ;
connection [ " ipv4 " ] [ " gateway " ] = " 192.168.43.1 " ;
connection [ " ipv6 " ] [ " method " ] = " ignore " ;
QDBusInterface nm_settings ( nm_service , nm_settings_path , nm_settings_iface , bus ) ;
nm_settings . setTimeout ( dbus_timeout ) ;
nm_settings . call ( " AddConnection " , QVariant : : fromValue ( connection ) ) ;
}
void WifiManager : : enableTethering ( ) {
if ( activate_tethering_connection ( ) ) {
return ;
}
addTetheringConnection ( ) ;
activate_tethering_connection ( ) ;
}
void WifiManager : : disableTethering ( ) {
deactivate_connections ( tethering_ssid . toUtf8 ( ) ) ;
}
bool WifiManager : : tetheringEnabled ( ) {
QString active_ap = get_active_ap ( ) ;
return get_property ( active_ap , " Ssid " ) = = tethering_ssid ;
}
void WifiManager : : changeTetheringPassword ( QString newPassword ) {
tetheringPassword = newPassword ;
clear_connections ( tethering_ssid . toUtf8 ( ) ) ;
addTetheringConnection ( ) ;
}