/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2004 - 2017 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
#include "nm-default.h"
#include "nm-wifi-ap.h"
#include <stdlib.h>
#include <linux/if_ether.h>
#include "NetworkManagerUtils.h"
#include "devices/nm-device.h"
#include "nm-core-internal.h"
#include "nm-dbus-manager.h"
#include "nm-glib-aux/nm-ref-string.h"
#include "nm-setting-wireless.h"
#include "nm-utils.h"
#include "nm-wifi-utils.h"
#include "platform/nm-platform.h"
#include "supplicant/nm-supplicant-interface.h"
#define PROTO_WPA "wpa"
#define PROTO_RSN "rsn"
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMWifiAP,
PROP_FLAGS,
PROP_WPA_FLAGS,
PROP_RSN_FLAGS,
PROP_SSID,
PROP_FREQUENCY,
PROP_HW_ADDRESS,
PROP_MODE,
PROP_MAX_BITRATE,
PROP_STRENGTH,
PROP_LAST_SEEN, );
struct _NMWifiAPPrivate {
/* Scanned or cached values */
GBytes * ssid;
char * address;
NM80211Mode mode;
guint8 strength;
guint32 freq; /* Frequency in MHz; ie 2412 (== 2.412 GHz) */
guint32 max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
gint64
last_seen_msec; /* Timestamp when the AP was seen lastly (in nm_utils_get_monotonic_timestamp_*() scale).
* Note that this value might be negative! */
NM80211ApFlags flags; /* General flags */
NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */
NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */
bool metered : 1;
/* Non-scanned attributes */
bool fake : 1; /* Whether or not the AP is from a scan */
bool hotspot : 1; /* Whether the AP is a local device's hotspot network */
};
typedef struct _NMWifiAPPrivate NMWifiAPPrivate;
struct _NMWifiAPClass {
NMDBusObjectClass parent;
};
G_DEFINE_TYPE(NMWifiAP, nm_wifi_ap, NM_TYPE_DBUS_OBJECT)
#define NM_WIFI_AP_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMWifiAP, NM_IS_WIFI_AP)
/*****************************************************************************/
GBytes *
nm_wifi_ap_get_ssid(const NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), NULL);
return NM_WIFI_AP_GET_PRIVATE(ap)->ssid;
}
gboolean
nm_wifi_ap_set_ssid(NMWifiAP *ap, GBytes *ssid)
{
NMWifiAPPrivate *priv;
gsize l;
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
if (!ssid) {
/* we don't clear the SSID, once we have it. We can only update
* it by a better value. */
return FALSE;
}
l = g_bytes_get_size(ssid);
if (l == 0 || l > 32)
g_return_val_if_reached(FALSE);
priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (ssid == priv->ssid)
return FALSE;
if (priv->ssid && g_bytes_equal(ssid, priv->ssid))
return FALSE;
g_bytes_ref(ssid);
nm_clear_pointer(&priv->ssid, g_bytes_unref);
priv->ssid = ssid;
_notify(ap, PROP_SSID);
return TRUE;
}
static gboolean
nm_wifi_ap_set_flags(NMWifiAP *ap, NM80211ApFlags flags)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->flags != flags) {
priv->flags = flags;
_notify(ap, PROP_FLAGS);
return TRUE;
}
return FALSE;
}
static gboolean
nm_wifi_ap_set_wpa_flags(NMWifiAP *ap, NM80211ApSecurityFlags flags)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->wpa_flags != flags) {
priv->wpa_flags = flags;
_notify(ap, PROP_WPA_FLAGS);
return TRUE;
}
return FALSE;
}
static gboolean
nm_wifi_ap_set_rsn_flags(NMWifiAP *ap, NM80211ApSecurityFlags flags)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->rsn_flags != flags) {
priv->rsn_flags = flags;
_notify(ap, PROP_RSN_FLAGS);
return TRUE;
}
return FALSE;
}
const char *
nm_wifi_ap_get_address(const NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), NULL);
return NM_WIFI_AP_GET_PRIVATE(ap)->address;
}
gboolean
nm_wifi_ap_set_address_bin(NMWifiAP *ap, const NMEtherAddr *addr)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
nm_assert(addr);
if (!priv->address || !nm_utils_hwaddr_matches(addr, ETH_ALEN, priv->address, -1)) {
g_free(priv->address);
priv->address = nm_utils_hwaddr_ntoa(addr, ETH_ALEN);
_notify(ap, PROP_HW_ADDRESS);
return TRUE;
}
return FALSE;
}
gboolean
nm_wifi_ap_set_address(NMWifiAP *ap, const char *addr)
{
NMEtherAddr addr_buf;
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
if (!addr || !nm_utils_hwaddr_aton(addr, &addr_buf, sizeof(addr_buf)))
g_return_val_if_reached(FALSE);
return nm_wifi_ap_set_address_bin(ap, &addr_buf);
}
NM80211Mode
nm_wifi_ap_get_mode(NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), NM_802_11_MODE_UNKNOWN);
return NM_WIFI_AP_GET_PRIVATE(ap)->mode;
}
static gboolean
nm_wifi_ap_set_mode(NMWifiAP *ap, NM80211Mode mode)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
nm_assert(NM_IN_SET(mode,
NM_802_11_MODE_UNKNOWN,
NM_802_11_MODE_ADHOC,
NM_802_11_MODE_INFRA,
NM_802_11_MODE_MESH));
if (priv->mode != mode) {
priv->mode = mode;
_notify(ap, PROP_MODE);
return TRUE;
}
return FALSE;
}
gboolean
nm_wifi_ap_is_hotspot(NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
return NM_WIFI_AP_GET_PRIVATE(ap)->hotspot;
}
gint8
nm_wifi_ap_get_strength(NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), 0);
return NM_WIFI_AP_GET_PRIVATE(ap)->strength;
}
gboolean
nm_wifi_ap_set_strength(NMWifiAP *ap, gint8 strength)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->strength != strength) {
priv->strength = strength;
_notify(ap, PROP_STRENGTH);
return TRUE;
}
return FALSE;
}
guint32
nm_wifi_ap_get_freq(NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), 0);
return NM_WIFI_AP_GET_PRIVATE(ap)->freq;
}
gboolean
nm_wifi_ap_set_freq(NMWifiAP *ap, guint32 freq)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->freq != freq) {
priv->freq = freq;
_notify(ap, PROP_FREQUENCY);
return TRUE;
}
return FALSE;
}
guint32
nm_wifi_ap_get_max_bitrate(NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), 0);
g_return_val_if_fail(nm_dbus_object_is_exported(NM_DBUS_OBJECT(ap)), 0);
return NM_WIFI_AP_GET_PRIVATE(ap)->max_bitrate;
}
gboolean
nm_wifi_ap_set_max_bitrate(NMWifiAP *ap, guint32 bitrate)
{
NMWifiAPPrivate *priv;
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->max_bitrate != bitrate) {
priv->max_bitrate = bitrate;
_notify(ap, PROP_MAX_BITRATE);
return TRUE;
}
return FALSE;
}
gboolean
nm_wifi_ap_get_fake(const NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
return NM_WIFI_AP_GET_PRIVATE(ap)->fake;
}
gboolean
nm_wifi_ap_set_fake(NMWifiAP *ap, gboolean fake)
{
NMWifiAPPrivate *priv;
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->fake != !!fake) {
priv->fake = fake;
return TRUE;
}
return FALSE;
}
NM80211ApFlags
nm_wifi_ap_get_flags(const NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), NM_802_11_AP_FLAGS_NONE);
return NM_WIFI_AP_GET_PRIVATE(ap)->flags;
}
static gboolean
nm_wifi_ap_set_last_seen(NMWifiAP *ap, gint32 last_seen_msec)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->last_seen_msec != last_seen_msec) {
priv->last_seen_msec = last_seen_msec;
_notify(ap, PROP_LAST_SEEN);
return TRUE;
}
return FALSE;
}
gboolean
nm_wifi_ap_get_metered(const NMWifiAP *self)
{
return NM_WIFI_AP_GET_PRIVATE(self)->metered;
}
NM80211ApSecurityFlags
nm_wifi_ap_get_wpa_flags(const NMWifiAP *self)
{
return NM_WIFI_AP_GET_PRIVATE(self)->wpa_flags;
}
NM80211ApSecurityFlags
nm_wifi_ap_get_rsn_flags(const NMWifiAP *self)
{
return NM_WIFI_AP_GET_PRIVATE(self)->rsn_flags;
}
/*****************************************************************************/
gboolean
nm_wifi_ap_update_from_properties(NMWifiAP *ap, const NMSupplicantBssInfo *bss_info)
{
NMWifiAPPrivate *priv;
gboolean changed = FALSE;
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
g_return_val_if_fail(bss_info, FALSE);
nm_assert(NM_IS_REF_STRING(bss_info->bss_path));
priv = NM_WIFI_AP_GET_PRIVATE(ap);
nm_assert(!ap->_supplicant_path || ap->_supplicant_path == bss_info->bss_path);
g_object_freeze_notify(G_OBJECT(ap));
if (!ap->_supplicant_path) {
ap->_supplicant_path = nm_ref_string_ref(bss_info->bss_path);
changed = TRUE;
}
changed |= nm_wifi_ap_set_flags(ap, bss_info->ap_flags);
changed |= nm_wifi_ap_set_mode(ap, bss_info->mode);
changed |= nm_wifi_ap_set_strength(ap, bss_info->signal_percent);
changed |= nm_wifi_ap_set_freq(ap, bss_info->frequency);
changed |= nm_wifi_ap_set_ssid(ap, bss_info->ssid);
if (bss_info->bssid_valid)
changed |= nm_wifi_ap_set_address_bin(ap, &bss_info->bssid);
else {
/* we don't actually clear the value. */
}
changed |= nm_wifi_ap_set_max_bitrate(ap, bss_info->max_rate);
if (priv->metered != bss_info->metered) {
priv->metered = bss_info->metered;
changed = TRUE;
}
changed |= nm_wifi_ap_set_wpa_flags(ap, bss_info->wpa_flags);
changed |= nm_wifi_ap_set_rsn_flags(ap, bss_info->rsn_flags);
changed |= nm_wifi_ap_set_last_seen(ap, bss_info->last_seen_msec);
changed |= nm_wifi_ap_set_fake(ap, FALSE);
g_object_thaw_notify(G_OBJECT(ap));
return changed;
}
static gboolean
has_proto(NMSettingWirelessSecurity *sec, const char *proto)
{
guint32 num_protos = nm_setting_wireless_security_get_num_protos(sec);
guint32 i;
if (num_protos == 0)
return TRUE; /* interpret no protos as "all" */
for (i = 0; i < num_protos; i++) {
if (!strcmp(nm_setting_wireless_security_get_proto(sec, i), proto))
return TRUE;
}
return FALSE;
}
static void
add_pair_ciphers(NMWifiAP *ap, NMSettingWirelessSecurity *sec)
{
NMWifiAPPrivate * priv = NM_WIFI_AP_GET_PRIVATE(ap);
guint32 num = nm_setting_wireless_security_get_num_pairwise(sec);
NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
guint32 i;
/* If no ciphers are specified, that means "all" WPA ciphers */
if (num == 0) {
flags |= NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP;
} else {
for (i = 0; i < num; i++) {
const char *cipher = nm_setting_wireless_security_get_pairwise(sec, i);
if (!strcmp(cipher, "tkip"))
flags |= NM_802_11_AP_SEC_PAIR_TKIP;
else if (!strcmp(cipher, "ccmp"))
flags |= NM_802_11_AP_SEC_PAIR_CCMP;
}
}
if (has_proto(sec, PROTO_WPA))
nm_wifi_ap_set_wpa_flags(ap, priv->wpa_flags | flags);
if (has_proto(sec, PROTO_RSN))
nm_wifi_ap_set_rsn_flags(ap, priv->rsn_flags | flags);
}
static void
add_group_ciphers(NMWifiAP *ap, NMSettingWirelessSecurity *sec)
{
NMWifiAPPrivate * priv = NM_WIFI_AP_GET_PRIVATE(ap);
guint32 num = nm_setting_wireless_security_get_num_groups(sec);
NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
guint32 i;
/* If no ciphers are specified, that means "all" WPA ciphers */
if (num == 0) {
flags |= NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP;
} else {
for (i = 0; i < num; i++) {
const char *cipher = nm_setting_wireless_security_get_group(sec, i);
if (!strcmp(cipher, "wep40"))
flags |= NM_802_11_AP_SEC_GROUP_WEP40;
else if (!strcmp(cipher, "wep104"))
flags |= NM_802_11_AP_SEC_GROUP_WEP104;
else if (!strcmp(cipher, "tkip"))
flags |= NM_802_11_AP_SEC_GROUP_TKIP;
else if (!strcmp(cipher, "ccmp"))
flags |= NM_802_11_AP_SEC_GROUP_CCMP;
}
}
if (has_proto(sec, PROTO_WPA))
nm_wifi_ap_set_wpa_flags(ap, priv->wpa_flags | flags);
if (has_proto(sec, PROTO_RSN))
nm_wifi_ap_set_rsn_flags(ap, priv->rsn_flags | flags);
}
const char *
nm_wifi_ap_to_string(const NMWifiAP *self, char *str_buf, gulong buf_len, gint64 now_msec)
{
const NMWifiAPPrivate *priv;
const char * supplicant_id = "-";
const char * export_path;
guint32 chan;
gs_free char * ssid_to_free = NULL;
char str_buf_ts[100];
g_return_val_if_fail(NM_IS_WIFI_AP(self), NULL);
priv = NM_WIFI_AP_GET_PRIVATE(self);
chan = nm_utils_wifi_freq_to_channel(priv->freq);
if (self->_supplicant_path)
supplicant_id = strrchr(self->_supplicant_path->str, '/') ?: supplicant_id;
export_path = nm_dbus_object_get_path(NM_DBUS_OBJECT(self));
if (export_path)
export_path = strrchr(export_path, '/') ?: export_path;
else
export_path = "/";
nm_utils_get_monotonic_timestamp_msec_cached(&now_msec);
g_snprintf(str_buf,
buf_len,
"%17s %-35s [ %c %3u %3u%% %c%c %c%c W:%04X R:%04X ] %s sup:%s [nm:%s]",
priv->address ?: "(none)",
(ssid_to_free = _nm_utils_ssid_to_string(priv->ssid)),
(priv->mode == NM_802_11_MODE_ADHOC
? '*'
: (priv->hotspot
? '#'
: (priv->fake ? 'f' : (priv->mode == NM_802_11_MODE_MESH ? 'm' : 'a')))),
chan,
priv->strength,
priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_',
priv->metered ? 'M' : '_',
priv->flags & NM_802_11_AP_FLAGS_WPS ? 'W' : '_',
priv->flags & NM_802_11_AP_FLAGS_WPS_PIN
? 'p'
: (priv->flags & NM_802_11_AP_FLAGS_WPS_PBC ? '#' : '_'),
priv->wpa_flags & 0xFFFF,
priv->rsn_flags & 0xFFFF,
priv->last_seen_msec != G_MININT64
? nm_sprintf_buf(str_buf_ts,
"%3u.%03us",
(guint)((now_msec - priv->last_seen_msec) / 1000),
(guint)((now_msec - priv->last_seen_msec) % 1000))
: " ",
supplicant_id,
export_path);
return str_buf;
}
static guint
freq_to_band(guint32 freq)
{
if (freq >= 4915 && freq <= 5825)
return 5;
else if (freq >= 2412 && freq <= 2484)
return 2;
return 0;
}
gboolean
nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection)
{
NMWifiAPPrivate * priv;
NMSettingWireless * s_wireless;
NMSettingWirelessSecurity *s_wireless_sec;
GBytes * ssid;
const char * mode;
const char * band;
const char * bssid;
guint32 channel;
g_return_val_if_fail(NM_IS_WIFI_AP(self), FALSE);
g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE);
priv = NM_WIFI_AP_GET_PRIVATE(self);
s_wireless = nm_connection_get_setting_wireless(connection);
if (s_wireless == NULL)
return FALSE;
ssid = nm_setting_wireless_get_ssid(s_wireless);
if (ssid != priv->ssid) {
if (!ssid || !priv->ssid)
return FALSE;
if (!g_bytes_equal(ssid, priv->ssid))
return FALSE;
}
bssid = nm_setting_wireless_get_bssid(s_wireless);
if (bssid && (!priv->address || !nm_utils_hwaddr_matches(bssid, -1, priv->address, -1)))
return FALSE;
mode = nm_setting_wireless_get_mode(s_wireless);
if (mode) {
if (!strcmp(mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA))
return FALSE;
if (!strcmp(mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC))
return FALSE;
if (!strcmp(mode, "ap") && (priv->mode != NM_802_11_MODE_INFRA || priv->hotspot != TRUE))
return FALSE;
if (!strcmp(mode, "mesh") && (priv->mode != NM_802_11_MODE_MESH))
return FALSE;
}
band = nm_setting_wireless_get_band(s_wireless);
if (band) {
guint ap_band = freq_to_band(priv->freq);
if (!strcmp(band, "a") && ap_band != 5)
return FALSE;
else if (!strcmp(band, "bg") && ap_band != 2)
return FALSE;
}
channel = nm_setting_wireless_get_channel(s_wireless);
if (channel) {
guint32 ap_chan = nm_utils_wifi_freq_to_channel(priv->freq);
if (channel != ap_chan)
return FALSE;
}
s_wireless_sec = nm_connection_get_setting_wireless_security(connection);
return nm_setting_wireless_ap_security_compatible(s_wireless,
s_wireless_sec,
priv->flags,
priv->wpa_flags,
priv->rsn_flags,
priv->mode);
}
gboolean
nm_wifi_ap_complete_connection(NMWifiAP * self,
NMConnection *connection,
gboolean lock_bssid,
GError ** error)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(self);
g_return_val_if_fail(connection != NULL, FALSE);
return nm_wifi_utils_complete_connection(priv->ssid,
priv->address,
priv->mode,
priv->freq,
priv->flags,
priv->wpa_flags,
priv->rsn_flags,
connection,
lock_bssid,
error);
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMWifiAP * self = NM_WIFI_AP(object);
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(self);
switch (prop_id) {
case PROP_FLAGS:
g_value_set_uint(value, priv->flags);
break;
case PROP_WPA_FLAGS:
g_value_set_uint(value, priv->wpa_flags);
break;
case PROP_RSN_FLAGS:
g_value_set_uint(value, priv->rsn_flags);
break;
case PROP_SSID:
g_value_take_variant(value, nm_utils_gbytes_to_variant_ay(priv->ssid));
break;
case PROP_FREQUENCY:
g_value_set_uint(value, priv->freq);
break;
case PROP_HW_ADDRESS:
g_value_set_string(value, priv->address);
break;
case PROP_MODE:
g_value_set_uint(value, priv->mode);
break;
case PROP_MAX_BITRATE:
g_value_set_uint(value, priv->max_bitrate);
break;
case PROP_STRENGTH:
g_value_set_uchar(value, priv->strength);
break;
case PROP_LAST_SEEN:
g_value_set_int(value,
priv->last_seen_msec != G_MININT64 ? (int) NM_MAX(
nm_utils_monotonic_timestamp_as_boottime(priv->last_seen_msec,
NM_UTILS_NSEC_PER_MSEC)
/ 1000,
1)
: -1);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_wifi_ap_init(NMWifiAP *self)
{
NMWifiAPPrivate *priv;
priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_WIFI_AP, NMWifiAPPrivate);
self->_priv = priv;
c_list_init(&self->aps_lst);
priv->mode = NM_802_11_MODE_INFRA;
priv->flags = NM_802_11_AP_FLAGS_NONE;
priv->wpa_flags = NM_802_11_AP_SEC_NONE;
priv->rsn_flags = NM_802_11_AP_SEC_NONE;
priv->last_seen_msec = G_MININT64;
}
NMWifiAP *
nm_wifi_ap_new_from_properties(const NMSupplicantBssInfo *bss_info)
{
NMWifiAP *ap;
ap = g_object_new(NM_TYPE_WIFI_AP, NULL);
nm_wifi_ap_update_from_properties(ap, bss_info);
return ap;
}
NMWifiAP *
nm_wifi_ap_new_fake_from_connection(NMConnection *connection)
{
NMWifiAP * ap;
NMWifiAPPrivate * priv;
NMSettingWireless * s_wireless;
NMSettingWirelessSecurity *s_wireless_sec;
const char * mode, *band, *key_mgmt;
guint32 channel;
NM80211ApSecurityFlags flags;
gboolean psk = FALSE, eap = FALSE, adhoc = FALSE;
g_return_val_if_fail(connection != NULL, NULL);
s_wireless = nm_connection_get_setting_wireless(connection);
g_return_val_if_fail(s_wireless != NULL, NULL);
ap = g_object_new(NM_TYPE_WIFI_AP, NULL);
priv = NM_WIFI_AP_GET_PRIVATE(ap);
priv->fake = TRUE;
nm_wifi_ap_set_ssid(ap, nm_setting_wireless_get_ssid(s_wireless));
// FIXME: bssid too?
mode = nm_setting_wireless_get_mode(s_wireless);
if (mode) {
if (!strcmp(mode, "infrastructure"))
nm_wifi_ap_set_mode(ap, NM_802_11_MODE_INFRA);
else if (!strcmp(mode, "adhoc")) {
nm_wifi_ap_set_mode(ap, NM_802_11_MODE_ADHOC);
adhoc = TRUE;
} else if (!strcmp(mode, "mesh"))
nm_wifi_ap_set_mode(ap, NM_802_11_MODE_MESH);
else if (!strcmp(mode, "ap")) {
nm_wifi_ap_set_mode(ap, NM_802_11_MODE_INFRA);
NM_WIFI_AP_GET_PRIVATE(ap)->hotspot = TRUE;
} else
goto error;
} else {
nm_wifi_ap_set_mode(ap, NM_802_11_MODE_INFRA);
}
band = nm_setting_wireless_get_band(s_wireless);
channel = nm_setting_wireless_get_channel(s_wireless);
if (band && channel) {
guint32 freq = nm_utils_wifi_channel_to_freq(channel, band);
if (freq == 0)
goto error;
nm_wifi_ap_set_freq(ap, freq);
}
s_wireless_sec = nm_connection_get_setting_wireless_security(connection);
/* Assume presence of a security setting means the AP is encrypted */
if (!s_wireless_sec)
goto done;
key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wireless_sec);
/* Everything below here uses encryption */
nm_wifi_ap_set_flags(ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
/* Static & Dynamic WEP */
if (!strcmp(key_mgmt, "none") || !strcmp(key_mgmt, "ieee8021x"))
goto done;
psk = nm_streq(key_mgmt, "wpa-psk");
eap = nm_streq(key_mgmt, "wpa-eap") || nm_streq(key_mgmt, "wpa-eap-suite-b-192");
if (!adhoc && (psk || eap)) {
if (has_proto(s_wireless_sec, PROTO_WPA)) {
flags = priv->wpa_flags
| (eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK);
nm_wifi_ap_set_wpa_flags(ap, flags);
}
if (has_proto(s_wireless_sec, PROTO_RSN)) {
flags = priv->rsn_flags
| (eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK);
nm_wifi_ap_set_rsn_flags(ap, flags);
}
add_pair_ciphers(ap, s_wireless_sec);
add_group_ciphers(ap, s_wireless_sec);
} else if (adhoc && psk) {
/* Ad-Hoc has special requirements: proto=RSN, pairwise=CCMP and
* group=CCMP.
*/
flags = priv->wpa_flags | NM_802_11_AP_SEC_KEY_MGMT_PSK;
/* Clear ciphers; only CCMP is supported */
flags &= ~(NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104
| NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_WEP40
| NM_802_11_AP_SEC_GROUP_WEP104 | NM_802_11_AP_SEC_GROUP_TKIP);
flags |= NM_802_11_AP_SEC_PAIR_CCMP;
flags |= NM_802_11_AP_SEC_GROUP_CCMP;
nm_wifi_ap_set_rsn_flags(ap, flags);
/* Don't use Ad-Hoc WPA (WPA-none) anymore */
nm_wifi_ap_set_wpa_flags(ap, NM_802_11_AP_SEC_NONE);
}
done:
return ap;
error:
g_object_unref(ap);
return NULL;
}
static void
finalize(GObject *object)
{
NMWifiAP * self = NM_WIFI_AP(object);
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(self);
nm_assert(!self->wifi_device);
nm_assert(c_list_is_empty(&self->aps_lst));
nm_ref_string_unref(self->_supplicant_path);
if (priv->ssid)
g_bytes_unref(priv->ssid);
g_free(priv->address);
G_OBJECT_CLASS(nm_wifi_ap_parent_class)->finalize(object);
}
static const NMDBusInterfaceInfoExtended interface_info_access_point = {
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(
NM_DBUS_INTERFACE_ACCESS_POINT,
.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, ),
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Flags", "u", NM_WIFI_AP_FLAGS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WpaFlags", "u", NM_WIFI_AP_WPA_FLAGS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("RsnFlags", "u", NM_WIFI_AP_RSN_FLAGS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Ssid", "ay", NM_WIFI_AP_SSID),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Frequency",
"u",
NM_WIFI_AP_FREQUENCY),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("HwAddress",
"s",
NM_WIFI_AP_HW_ADDRESS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Mode", "u", NM_WIFI_AP_MODE),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("MaxBitrate",
"u",
NM_WIFI_AP_MAX_BITRATE),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Strength", "y", NM_WIFI_AP_STRENGTH),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("LastSeen",
"i",
NM_WIFI_AP_LAST_SEEN), ), ),
.legacy_property_changed = TRUE,
};
static void
nm_wifi_ap_class_init(NMWifiAPClass *ap_class)
{
#define ALL_SEC_FLAGS \
(NM_802_11_AP_SEC_NONE | NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104 \
| NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_WEP40 \
| NM_802_11_AP_SEC_GROUP_WEP104 | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP \
| NM_802_11_AP_SEC_KEY_MGMT_PSK | NM_802_11_AP_SEC_KEY_MGMT_802_1X \
| NM_802_11_AP_SEC_KEY_MGMT_SAE | NM_802_11_AP_SEC_KEY_MGMT_OWE \
| NM_802_11_AP_SEC_KEY_MGMT_OWE_TM | NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192)
GObjectClass * object_class = G_OBJECT_CLASS(ap_class);
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(ap_class);
g_type_class_add_private(object_class, sizeof(NMWifiAPPrivate));
dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED(NM_DBUS_PATH_ACCESS_POINT);
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_access_point);
object_class->get_property = get_property;
object_class->finalize = finalize;
obj_properties[PROP_FLAGS] = g_param_spec_uint(NM_WIFI_AP_FLAGS,
"",
"",
NM_802_11_AP_FLAGS_NONE,
NM_802_11_AP_FLAGS_PRIVACY,
NM_802_11_AP_FLAGS_NONE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_WPA_FLAGS] = g_param_spec_uint(NM_WIFI_AP_WPA_FLAGS,
"",
"",
NM_802_11_AP_SEC_NONE,
ALL_SEC_FLAGS,
NM_802_11_AP_SEC_NONE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_RSN_FLAGS] = g_param_spec_uint(NM_WIFI_AP_RSN_FLAGS,
"",
"",
NM_802_11_AP_SEC_NONE,
ALL_SEC_FLAGS,
NM_802_11_AP_SEC_NONE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_SSID] = g_param_spec_variant(NM_WIFI_AP_SSID,
"",
"",
G_VARIANT_TYPE("ay"),
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_FREQUENCY] = g_param_spec_uint(NM_WIFI_AP_FREQUENCY,
"",
"",
0,
10000,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_HW_ADDRESS] =
g_param_spec_string(NM_WIFI_AP_HW_ADDRESS,
"",
"",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_MODE] = g_param_spec_uint(NM_WIFI_AP_MODE,
"",
"",
NM_802_11_MODE_ADHOC,
NM_802_11_MODE_INFRA,
NM_802_11_MODE_INFRA,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_MAX_BITRATE] = g_param_spec_uint(NM_WIFI_AP_MAX_BITRATE,
"",
"",
0,
G_MAXUINT16,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_STRENGTH] = g_param_spec_uchar(NM_WIFI_AP_STRENGTH,
"",
"",
0,
G_MAXINT8,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_LAST_SEEN] = g_param_spec_int(NM_WIFI_AP_LAST_SEEN,
"",
"",
-1,
G_MAXINT,
-1,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}
/*****************************************************************************/
const char **
nm_wifi_aps_get_paths(const CList *aps_lst_head, gboolean include_without_ssid)
{
NMWifiAP * ap;
gsize i, n;
const char **list;
const char * path;
n = c_list_length(aps_lst_head);
list = g_new(const char *, n + 1);
i = 0;
if (n > 0) {
c_list_for_each_entry (ap, aps_lst_head, aps_lst) {
nm_assert(i < n);
if (!include_without_ssid && !nm_wifi_ap_get_ssid(ap))
continue;
path = nm_dbus_object_get_path(NM_DBUS_OBJECT(ap));
nm_assert(path);
list[i++] = path;
}
nm_assert(i <= n);
nm_assert(!include_without_ssid || i == n);
}
list[i] = NULL;
return list;
}
NMWifiAP *
nm_wifi_aps_find_first_compatible(const CList *aps_lst_head, NMConnection *connection)
{
NMWifiAP *ap;
g_return_val_if_fail(connection, NULL);
c_list_for_each_entry (ap, aps_lst_head, aps_lst) {
if (nm_wifi_ap_check_compatible(ap, connection))
return ap;
}
return NULL;
}
/*****************************************************************************/
NMWifiAP *
nm_wifi_ap_lookup_for_device(NMDevice *device, const char *exported_path)
{
NMWifiAP *ap;
g_return_val_if_fail(NM_IS_DEVICE(device), NULL);
ap = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
exported_path);
if (!ap || !NM_IS_WIFI_AP(ap) || ap->wifi_device != device)
return NULL;
return ap;
}