522697b228
git-svn-id: http://svn.sabayonlinux.org/overlay@1163 d7aec97c-591d-0410-af39-a8856400b30a
4012 lines
114 KiB
Diff
4012 lines
114 KiB
Diff
diff -Nurp NetworkManager-0.6.5.orig/src/nm-device-802-11-wireless.c NetworkManager-0.6.5/src/nm-device-802-11-wireless.c
|
|
--- NetworkManager-0.6.5.orig/src/nm-device-802-11-wireless.c 2007-01-26 18:56:29.000000000 +0100
|
|
+++ NetworkManager-0.6.5/src/nm-device-802-11-wireless.c 2007-04-11 16:30:03.000000000 +0200
|
|
@@ -119,7 +119,8 @@ static gboolean process_scan_results (NM
|
|
guint32 res_buf_len);
|
|
static void schedule_scan (NMDevice80211Wireless *self);
|
|
|
|
-static int wireless_qual_to_percent (const struct iw_quality *qual,
|
|
+static int wireless_qual_to_percent (NMDevice *dev,
|
|
+ const struct iw_quality *qual,
|
|
const struct iw_quality *max_qual,
|
|
const struct iw_quality *avg_qual);
|
|
|
|
@@ -193,8 +194,9 @@ nm_device_802_11_wireless_update_signal_
|
|
#endif
|
|
if (iw_get_stats (nm_dev_sock_get_fd (sk), iface, &stats, &range, has_range) == 0)
|
|
{
|
|
- percent = wireless_qual_to_percent (&stats.qual, (const iwqual *)(&self->priv->max_qual),
|
|
- (const iwqual *)(&self->priv->avg_qual));
|
|
+ percent = wireless_qual_to_percent (NM_DEVICE (self), &stats.qual,
|
|
+ (const iwqual *)(&self->priv->max_qual),
|
|
+ (const iwqual *)(&self->priv->avg_qual));
|
|
}
|
|
nm_dev_sock_close (sk);
|
|
}
|
|
@@ -353,23 +355,15 @@ get_wireless_capabilities (NMDevice80211
|
|
|
|
if ((data_len >= minlen) && range->we_version_compiled >= 18)
|
|
{
|
|
- if (range->enc_capa & IW_ENC_CAPA_WPA)
|
|
- {
|
|
- caps |= (NM_802_11_CAP_PROTO_WPA
|
|
- | NM_802_11_CAP_KEY_MGMT_PSK
|
|
- | NM_802_11_CAP_KEY_MGMT_802_1X);
|
|
- }
|
|
- if (range->enc_capa & IW_ENC_CAPA_WPA2)
|
|
- {
|
|
- caps |= (NM_802_11_CAP_PROTO_WPA2
|
|
- | NM_802_11_CAP_KEY_MGMT_PSK
|
|
- | NM_802_11_CAP_KEY_MGMT_802_1X);
|
|
- }
|
|
-
|
|
- if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
|
|
- caps |= NM_802_11_CAP_CIPHER_TKIP;
|
|
- if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
|
|
- caps |= NM_802_11_CAP_CIPHER_CCMP;
|
|
+ /* FIXME: Until all drivers support enc_capa ... */
|
|
+ caps |= (NM_802_11_CAP_PROTO_WPA
|
|
+ | NM_802_11_CAP_KEY_MGMT_PSK
|
|
+ | NM_802_11_CAP_KEY_MGMT_802_1X);
|
|
+ caps |= (NM_802_11_CAP_PROTO_WPA2
|
|
+ | NM_802_11_CAP_KEY_MGMT_PSK
|
|
+ | NM_802_11_CAP_KEY_MGMT_802_1X);
|
|
+ caps |= NM_802_11_CAP_CIPHER_TKIP;
|
|
+ caps |= NM_802_11_CAP_CIPHER_CCMP;
|
|
}
|
|
|
|
return caps;
|
|
@@ -1285,7 +1279,8 @@ nm_device_802_11_wireless_get_signal_str
|
|
*
|
|
*/
|
|
static int
|
|
-wireless_qual_to_percent (const struct iw_quality *qual,
|
|
+wireless_qual_to_percent (NMDevice *dev,
|
|
+ const struct iw_quality *qual,
|
|
const struct iw_quality *max_qual,
|
|
const struct iw_quality *avg_qual)
|
|
{
|
|
@@ -1314,7 +1309,7 @@ max_qual->updated);
|
|
* are free to use whatever they want to calculate "Link Quality".
|
|
*/
|
|
if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID))
|
|
- percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual));
|
|
+ percent = (int) (100 * ((double) qual->qual / (double) max_qual->qual));
|
|
|
|
/* If the driver doesn't specify a complete and valid quality, we have two options:
|
|
*
|
|
@@ -1380,7 +1375,7 @@ max_qual->updated);
|
|
}
|
|
|
|
/* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
|
|
- if ((percent < 1) && (level_percent >= 0))
|
|
+ if ((percent < 1 || !strcmp (nm_device_get_driver (dev), "ath_pci")) && (level_percent >= 0))
|
|
percent = level_percent;
|
|
|
|
#ifdef IW_QUAL_DEBUG
|
|
@@ -2693,13 +2688,11 @@ supplicant_status_cb (GIOChannel *source
|
|
}
|
|
|
|
|
|
-#define NM_SUPPLICANT_TIMEOUT 20 /* how long we wait for wpa_supplicant to associate (in seconds) */
|
|
+#define NM_SUPPLICANT_TIMEOUT 60 /* how long we wait for wpa_supplicant to associate (in seconds) */
|
|
|
|
static unsigned int
|
|
get_supplicant_timeout (NMDevice80211Wireless *self)
|
|
{
|
|
- if (self->priv->num_freqs > 14)
|
|
- return NM_SUPPLICANT_TIMEOUT * 2;
|
|
return NM_SUPPLICANT_TIMEOUT;
|
|
}
|
|
|
|
@@ -2923,6 +2916,8 @@ supplicant_interface_init (NMDevice80211
|
|
struct wpa_ctrl * ctrl = NULL;
|
|
char * socket_path;
|
|
const char * iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ const char * wpa_driver;
|
|
+ const char * kernel_driver;
|
|
gboolean success = FALSE;
|
|
int tries = 0;
|
|
|
|
@@ -2939,9 +2934,21 @@ supplicant_interface_init (NMDevice80211
|
|
goto exit;
|
|
}
|
|
|
|
+ /*
|
|
+ * We want to work with the generic "wext" wpa_supplicant driver, but some kernel drivers
|
|
+ * are just utter junk. For those losers, we use a specific wpa_supplicant driver.
|
|
+ */
|
|
+ kernel_driver = nm_device_get_driver (NM_DEVICE (self));
|
|
+ if (!strcmp (kernel_driver, "ath_pci"))
|
|
+ wpa_driver = "madwifi";
|
|
+ else if (!strcmp (kernel_driver, "prism54"))
|
|
+ wpa_driver = "prism54";
|
|
+ else
|
|
+ wpa_driver = "wext";
|
|
+
|
|
/* wpa_cli -g/var/run/wpa_supplicant-global interface_add eth1 "" wext /var/run/wpa_supplicant */
|
|
if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL,
|
|
- "INTERFACE_ADD %s\t\twext\t" WPA_SUPPLICANT_CONTROL_SOCKET "\t", iface))
|
|
+ "INTERFACE_ADD %s\t\t%s\t" WPA_SUPPLICANT_CONTROL_SOCKET "\t", iface, wpa_driver))
|
|
goto exit;
|
|
wpa_ctrl_close (ctrl);
|
|
|
|
@@ -2978,6 +2985,7 @@ supplicant_send_network_config (NMDevice
|
|
gboolean is_adhoc;
|
|
char * hex_essid = NULL;
|
|
const char * ap_scan = "AP_SCAN 1";
|
|
+ const char * kernel_driver;
|
|
guint32 caps;
|
|
gboolean supports_wpa;
|
|
|
|
@@ -2999,12 +3007,39 @@ supplicant_send_network_config (NMDevice
|
|
|| (caps & NM_802_11_CAP_PROTO_WPA2);
|
|
|
|
/* Use "AP_SCAN 2" if:
|
|
+ *
|
|
+ * - The wireless driver does not support "AP_SCAN 1"
|
|
+ * (orinoco, prism54, airo, and airo_cs)
|
|
+ * - The wireless network is hidden and the driver does not support
|
|
+ * "AP_SCAN 1" with hidden networks (ipw2100, ipw2200, and ndiswrapper)
|
|
* - The wireless network is non-broadcast or Ad-Hoc
|
|
* - The wireless driver does not support WPA (stupid drivers...)
|
|
+ *
|
|
+ * Otherwise, we prefer "AP_SCAN 1".
|
|
*/
|
|
is_adhoc = (nm_ap_get_mode(ap) == IW_MODE_ADHOC);
|
|
- if (!nm_ap_get_broadcast (ap) || is_adhoc || !supports_wpa)
|
|
+ kernel_driver = nm_device_get_driver (NM_DEVICE (self));
|
|
+ if (!strcmp (kernel_driver, "orinoco_cs"))
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+ else if (!strcmp (kernel_driver, "prism54"))
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+ else if (!strncmp (kernel_driver, "airo", 4))
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+ else if (!strcmp (kernel_driver, "ndiswrapper") && !nm_ap_get_broadcast (ap))
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+ else if (!strcmp (kernel_driver, "ipw2100") || !strcmp (kernel_driver, "ipw2200"))
|
|
+ {
|
|
+ if (nm_ap_get_broadcast (ap))
|
|
+ ap_scan = "AP_SCAN 1";
|
|
+ else
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+ }
|
|
+ else if (is_adhoc)
|
|
ap_scan = "AP_SCAN 2";
|
|
+ else if (!supports_wpa)
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+ else
|
|
+ ap_scan = "AP_SCAN 1";
|
|
|
|
/* Tell wpa_supplicant that we'll do the scanning */
|
|
if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL, ap_scan))
|
|
@@ -3700,7 +3735,8 @@ process_scan_results (NMDevice80211Wirel
|
|
nm_ap_set_freq (ap, iw_freq2float(&(iwe->u.freq)));
|
|
break;
|
|
case IWEVQUAL:
|
|
- nm_ap_set_strength (ap, wireless_qual_to_percent (&(iwe->u.qual),
|
|
+ nm_ap_set_strength (ap, wireless_qual_to_percent (&dev->parent,
|
|
+ &(iwe->u.qual),
|
|
(const iwqual *)(&dev->priv->max_qual),
|
|
(const iwqual *)(&dev->priv->avg_qual)));
|
|
break;
|
|
diff -Nurp NetworkManager-0.6.5.orig/src/nm-device-802-11-wireless.c.orig NetworkManager-0.6.5/src/nm-device-802-11-wireless.c.orig
|
|
--- NetworkManager-0.6.5.orig/src/nm-device-802-11-wireless.c.orig 1970-01-01 01:00:00.000000000 +0100
|
|
+++ NetworkManager-0.6.5/src/nm-device-802-11-wireless.c.orig 2007-04-11 16:28:46.000000000 +0200
|
|
@@ -0,0 +1,3815 @@
|
|
+/* NetworkManager -- Network link manager
|
|
+ *
|
|
+ * Dan Williams <dcbw@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
+ *
|
|
+ * (C) Copyright 2005 Red Hat, Inc.
|
|
+ */
|
|
+
|
|
+#include <glib.h>
|
|
+#include <glib/gi18n.h>
|
|
+#include <dbus/dbus.h>
|
|
+#include <netinet/in.h>
|
|
+#include <string.h>
|
|
+#include <net/ethernet.h>
|
|
+#include <iwlib.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/wait.h>
|
|
+#include <signal.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include "nm-device.h"
|
|
+#include "nm-device-802-11-wireless.h"
|
|
+#include "nm-device-private.h"
|
|
+#include "NetworkManagerAPList.h"
|
|
+#include "NetworkManagerDbus.h"
|
|
+#include "nm-utils.h"
|
|
+#include "NetworkManagerUtils.h"
|
|
+#include "NetworkManagerPolicy.h"
|
|
+#include "nm-activation-request.h"
|
|
+#include "nm-dbus-nmi.h"
|
|
+#include "wpa_ctrl.h"
|
|
+#include "cipher.h"
|
|
+
|
|
+/* #define IW_QUAL_DEBUG */
|
|
+
|
|
+#define NM_DEVICE_802_11_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_802_11_WIRELESS, NMDevice80211WirelessPrivate))
|
|
+
|
|
+struct _Supplicant
|
|
+{
|
|
+ GPid pid;
|
|
+ GSource * watch;
|
|
+ GSource * status;
|
|
+ struct wpa_ctrl * ctrl;
|
|
+ GSource * timeout;
|
|
+
|
|
+ GSource * stdout;
|
|
+};
|
|
+
|
|
+struct _NMDevice80211WirelessPrivate
|
|
+{
|
|
+ gboolean dispose_has_run;
|
|
+ gboolean is_initialized;
|
|
+
|
|
+ struct ether_addr hw_addr;
|
|
+
|
|
+ char * cur_essid;
|
|
+ gint8 strength;
|
|
+ gint8 invalid_strength_counter;
|
|
+ iwqual max_qual;
|
|
+ iwqual avg_qual;
|
|
+
|
|
+ gint8 num_freqs;
|
|
+ double freqs[IW_MAX_FREQUENCIES];
|
|
+
|
|
+ gboolean scanning;
|
|
+ NMAccessPointList * ap_list;
|
|
+ guint8 scan_interval; /* seconds */
|
|
+ guint32 last_scan;
|
|
+ GSource * scan_timeout;
|
|
+ GSource * pending_scan;
|
|
+
|
|
+ struct _Supplicant supplicant;
|
|
+
|
|
+ guint32 failed_link_count;
|
|
+ GSource * link_timeout;
|
|
+ gulong wireless_event_id;
|
|
+
|
|
+ /* Static options from driver */
|
|
+ guint8 we_version;
|
|
+ guint32 capabilities;
|
|
+};
|
|
+
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ NMDevice80211Wireless * dev;
|
|
+ guint8 * results;
|
|
+ guint32 results_len;
|
|
+} NMWirelessScanResults;
|
|
+
|
|
+
|
|
+static void nm_device_802_11_wireless_ap_list_clear (NMDevice80211Wireless *self);
|
|
+
|
|
+static void nm_device_802_11_wireless_scan_done (gpointer user_data);
|
|
+
|
|
+static gboolean nm_device_802_11_wireless_scan (gpointer user_data);
|
|
+
|
|
+static void cancel_scan_results_timeout (NMDevice80211Wireless *self);
|
|
+
|
|
+static void cancel_pending_scan (NMDevice80211Wireless *self);
|
|
+
|
|
+static void request_and_convert_scan_results (NMDevice80211Wireless *self);
|
|
+
|
|
+static gboolean process_scan_results (NMDevice80211Wireless *dev,
|
|
+ const guint8 *res_buf,
|
|
+ guint32 res_buf_len);
|
|
+static void schedule_scan (NMDevice80211Wireless *self);
|
|
+
|
|
+static int wireless_qual_to_percent (NMDevice *dev,
|
|
+ const struct iw_quality *qual,
|
|
+ const struct iw_quality *max_qual,
|
|
+ const struct iw_quality *avg_qual);
|
|
+
|
|
+static gboolean is_associated (NMDevice80211Wireless *self);
|
|
+
|
|
+static gboolean link_to_specific_ap (NMDevice80211Wireless *self,
|
|
+ NMAccessPoint *ap,
|
|
+ gboolean default_link);
|
|
+
|
|
+static void supplicant_cleanup (NMDevice80211Wireless *self);
|
|
+
|
|
+static void remove_link_timeout (NMDevice80211Wireless *self);
|
|
+
|
|
+static void nm_device_802_11_wireless_set_wep_enc_key (NMDevice80211Wireless *self,
|
|
+ const char *key,
|
|
+ int auth_method);
|
|
+
|
|
+static void nm_device_802_11_wireless_event (NmNetlinkMonitor *monitor,
|
|
+ GObject *obj,
|
|
+ char *data,
|
|
+ int data_len,
|
|
+ NMDevice80211Wireless *self);
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_update_signal_strength
|
|
+ *
|
|
+ * Update the device's idea of the strength of its connection to the
|
|
+ * current access point.
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+nm_device_802_11_wireless_update_signal_strength (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMData * app_data;
|
|
+ gboolean has_range = FALSE;
|
|
+ NMSock * sk;
|
|
+ iwrange range;
|
|
+ iwstats stats;
|
|
+ int percent = -1;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ /* Signal strength is pretty meaningless during a scan */
|
|
+ if (self->priv->scanning)
|
|
+ return;
|
|
+
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ g_assert (app_data);
|
|
+
|
|
+ /* If we aren't the active device, we don't really have a signal strength
|
|
+ * that would mean anything.
|
|
+ */
|
|
+ if (!nm_device_get_act_request (NM_DEVICE (self)))
|
|
+ {
|
|
+ self->priv->strength = -1;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ const char *iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+
|
|
+ memset (&range, 0, sizeof (iwrange));
|
|
+ memset (&stats, 0, sizeof (iwstats));
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET 'iwrange'.", iface);
|
|
+#endif
|
|
+ has_range = (iw_get_range_info (nm_dev_sock_get_fd (sk), iface, &range) >= 0);
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET 'iwstats'.", iface);
|
|
+#endif
|
|
+ if (iw_get_stats (nm_dev_sock_get_fd (sk), iface, &stats, &range, has_range) == 0)
|
|
+ {
|
|
+ percent = wireless_qual_to_percent (NM_DEVICE (self), &stats.qual,
|
|
+ (const iwqual *)(&self->priv->max_qual),
|
|
+ (const iwqual *)(&self->priv->avg_qual));
|
|
+ }
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ /* Try to smooth out the strength. Atmel cards, for example, will give no strength
|
|
+ * one second and normal strength the next.
|
|
+ */
|
|
+ if ((percent == -1) && (++self->priv->invalid_strength_counter <= 3))
|
|
+ percent = self->priv->strength;
|
|
+ else
|
|
+ self->priv->invalid_strength_counter = 0;
|
|
+
|
|
+ if (percent != self->priv->strength)
|
|
+ nm_dbus_signal_device_strength_change (app_data->dbus_connection, self, percent);
|
|
+
|
|
+ self->priv->strength = percent;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_update_bssid
|
|
+ *
|
|
+ * Update the current wireless network's BSSID, presumably in response to
|
|
+ * roaming.
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+nm_device_802_11_wireless_update_bssid (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMAccessPoint * ap;
|
|
+ NMActRequest * req;
|
|
+ struct ether_addr new_bssid;
|
|
+ const struct ether_addr *old_bssid;
|
|
+ const char * new_essid;
|
|
+ const char * old_essid;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ /* If we aren't the active device with an active AP, there is no meaningful BSSID value */
|
|
+ req = nm_device_get_act_request (NM_DEVICE (self));
|
|
+ if (!req)
|
|
+ return;
|
|
+
|
|
+ ap = nm_act_request_get_ap (req);
|
|
+ if (!ap)
|
|
+ return;
|
|
+
|
|
+ /* Get the current BSSID. If it is valid but does not match the stored value,
|
|
+ * and the ESSID is the same as what we think its suposed to be, update it. */
|
|
+ nm_device_802_11_wireless_get_bssid (self, &new_bssid);
|
|
+ old_bssid = nm_ap_get_address (ap);
|
|
+ new_essid = nm_device_802_11_wireless_get_essid(self);
|
|
+ old_essid = nm_ap_get_essid(ap);
|
|
+ if ( nm_ethernet_address_is_valid (&new_bssid)
|
|
+ && !nm_ethernet_addresses_are_equal (&new_bssid, old_bssid)
|
|
+ && !nm_null_safe_strcmp (old_essid, new_essid))
|
|
+ {
|
|
+ NMData * app_data;
|
|
+ gboolean automatic;
|
|
+ gchar new_addr[20];
|
|
+ gchar old_addr[20];
|
|
+
|
|
+ memset (new_addr, '\0', sizeof (new_addr));
|
|
+ memset (old_addr, '\0', sizeof (old_addr));
|
|
+ iw_ether_ntop (&new_bssid, new_addr);
|
|
+ iw_ether_ntop (old_bssid, old_addr);
|
|
+ nm_debug ("Roamed from BSSID %s to %s on wireless network '%s'", old_addr, new_addr, nm_ap_get_essid (ap));
|
|
+
|
|
+ nm_ap_set_address (ap, &new_bssid);
|
|
+
|
|
+ automatic = !nm_act_request_get_user_requested (req);
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ g_assert (app_data);
|
|
+ nm_dbus_update_network_info (app_data->dbus_connection, ap, automatic);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static guint nm_wireless_scan_interval_to_seconds (NMWirelessScanInterval interval)
|
|
+{
|
|
+ guint seconds;
|
|
+
|
|
+ switch (interval)
|
|
+ {
|
|
+ case NM_WIRELESS_SCAN_INTERVAL_INIT:
|
|
+ seconds = 15;
|
|
+ break;
|
|
+
|
|
+ case NM_WIRELESS_SCAN_INTERVAL_INACTIVE:
|
|
+ seconds = 120;
|
|
+ break;
|
|
+
|
|
+ case NM_WIRELESS_SCAN_INTERVAL_ACTIVE:
|
|
+ default:
|
|
+ seconds = 20;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return seconds;
|
|
+}
|
|
+
|
|
+
|
|
+static guint32
|
|
+real_get_generic_capabilities (NMDevice *dev)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ int err;
|
|
+ guint32 caps = NM_DEVICE_CAP_NONE;
|
|
+ iwrange range;
|
|
+ struct iwreq wrq;
|
|
+
|
|
+ /* Check for Wireless Extensions support >= 16 for wireless devices */
|
|
+
|
|
+ if (!(sk = nm_dev_sock_open (dev, DEV_WIRELESS, __func__, NULL)))
|
|
+ goto out;
|
|
+
|
|
+ if (iw_get_range_info (nm_dev_sock_get_fd (sk), nm_device_get_iface (dev), &range) < 0)
|
|
+ goto out;
|
|
+
|
|
+ if (range.we_version_compiled < 16)
|
|
+ {
|
|
+ nm_warning ("%s: driver's Wireless Extensions version (%d) is too old. Can't use device.",
|
|
+ nm_device_get_iface (dev), range.we_version_compiled);
|
|
+ }
|
|
+ else
|
|
+ caps |= NM_DEVICE_CAP_NM_SUPPORTED;
|
|
+
|
|
+ memset (&wrq, 0, sizeof (struct iwreq));
|
|
+ err = iw_set_ext (nm_dev_sock_get_fd (sk), nm_device_get_iface (dev), SIOCSIWSCAN, &wrq);
|
|
+ if (!((err == -1) && (errno == EOPNOTSUPP)))
|
|
+ caps |= NM_DEVICE_CAP_WIRELESS_SCAN;
|
|
+
|
|
+out:
|
|
+ if (sk)
|
|
+ nm_dev_sock_close (sk);
|
|
+ return caps;
|
|
+}
|
|
+
|
|
+static guint32
|
|
+get_wireless_capabilities (NMDevice80211Wireless *self,
|
|
+ iwrange * range,
|
|
+ guint32 data_len)
|
|
+{
|
|
+ guint32 minlen;
|
|
+ guint32 caps = NM_802_11_CAP_NONE;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, NM_802_11_CAP_NONE);
|
|
+ g_return_val_if_fail (range != NULL, NM_802_11_CAP_NONE);
|
|
+
|
|
+ minlen = ((char *) &range->enc_capa) - (char *) range + sizeof (range->enc_capa);
|
|
+
|
|
+ /* All drivers should support WEP by default */
|
|
+ caps |= (NM_802_11_CAP_CIPHER_WEP40 | NM_802_11_CAP_CIPHER_WEP104);
|
|
+ /* All drivers should support no encryption by default */
|
|
+ caps |= (NM_802_11_CAP_PROTO_NONE | NM_802_11_CAP_PROTO_WEP);
|
|
+
|
|
+ if ((data_len >= minlen) && range->we_version_compiled >= 18)
|
|
+ {
|
|
+ if (range->enc_capa & IW_ENC_CAPA_WPA)
|
|
+ {
|
|
+ caps |= (NM_802_11_CAP_PROTO_WPA
|
|
+ | NM_802_11_CAP_KEY_MGMT_PSK
|
|
+ | NM_802_11_CAP_KEY_MGMT_802_1X);
|
|
+ }
|
|
+ if (range->enc_capa & IW_ENC_CAPA_WPA2)
|
|
+ {
|
|
+ caps |= (NM_802_11_CAP_PROTO_WPA2
|
|
+ | NM_802_11_CAP_KEY_MGMT_PSK
|
|
+ | NM_802_11_CAP_KEY_MGMT_802_1X);
|
|
+ }
|
|
+
|
|
+ if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
|
|
+ caps |= NM_802_11_CAP_CIPHER_TKIP;
|
|
+ if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
|
|
+ caps |= NM_802_11_CAP_CIPHER_CCMP;
|
|
+ }
|
|
+
|
|
+ return caps;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+nm_device_802_11_wireless_init (NMDevice80211Wireless * self)
|
|
+{
|
|
+ self->priv = NM_DEVICE_802_11_WIRELESS_GET_PRIVATE (self);
|
|
+ self->priv->dispose_has_run = FALSE;
|
|
+ self->priv->is_initialized = FALSE;
|
|
+
|
|
+ memset (&(self->priv->hw_addr), 0, sizeof (struct ether_addr));
|
|
+ self->priv->supplicant.pid = -1;
|
|
+}
|
|
+
|
|
+static void
|
|
+real_init (NMDevice *dev)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMData * app_data;
|
|
+ guint32 caps;
|
|
+ NMSock * sk;
|
|
+ NmNetlinkMonitor * monitor;
|
|
+
|
|
+ self->priv->is_initialized = TRUE;
|
|
+ self->priv->scanning = FALSE;
|
|
+
|
|
+ self->priv->ap_list = nm_ap_list_new (NETWORK_TYPE_DEVICE);
|
|
+
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ nm_device_802_11_wireless_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_ACTIVE);
|
|
+
|
|
+ nm_device_802_11_wireless_set_mode (self, IW_MODE_INFRA);
|
|
+
|
|
+ /* Non-scanning devices show the entire allowed AP list as their
|
|
+ * available networks.
|
|
+ */
|
|
+ caps = nm_device_get_capabilities (NM_DEVICE (self));
|
|
+ if (!(caps & NM_DEVICE_CAP_WIRELESS_SCAN))
|
|
+ nm_device_802_11_wireless_copy_allowed_to_dev_list (self, app_data->allowed_ap_list);
|
|
+
|
|
+ self->priv->we_version = 0;
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ struct iw_range range;
|
|
+ struct iwreq wrq;
|
|
+
|
|
+ memset (&wrq, 0, sizeof (wrq));
|
|
+ strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ);
|
|
+ wrq.u.data.pointer = (caddr_t) ⦥
|
|
+ wrq.u.data.length = sizeof (struct iw_range);
|
|
+
|
|
+ if (ioctl (nm_dev_sock_get_fd (sk), SIOCGIWRANGE, &wrq) >= 0)
|
|
+ {
|
|
+ int i;
|
|
+
|
|
+ self->priv->max_qual.qual = range.max_qual.qual;
|
|
+ self->priv->max_qual.level = range.max_qual.level;
|
|
+ self->priv->max_qual.noise = range.max_qual.noise;
|
|
+ self->priv->max_qual.updated = range.max_qual.updated;
|
|
+
|
|
+ self->priv->avg_qual.qual = range.avg_qual.qual;
|
|
+ self->priv->avg_qual.level = range.avg_qual.level;
|
|
+ self->priv->avg_qual.noise = range.avg_qual.noise;
|
|
+ self->priv->avg_qual.updated = range.avg_qual.updated;
|
|
+
|
|
+ self->priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
|
|
+ for (i = 0; i < self->priv->num_freqs; i++)
|
|
+ self->priv->freqs[i] = iw_freq2float (&(range.freq[i]));
|
|
+
|
|
+ self->priv->we_version = range.we_version_compiled;
|
|
+
|
|
+ /* 802.11 wireless-specific capabilities */
|
|
+ self->priv->capabilities = get_wireless_capabilities (self, &range, wrq.u.data.length);
|
|
+ }
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ monitor = app_data->netlink_monitor;
|
|
+ self->priv->wireless_event_id =
|
|
+ g_signal_connect (G_OBJECT (monitor), "wireless-event",
|
|
+ G_CALLBACK (nm_device_802_11_wireless_event), self);
|
|
+}
|
|
+
|
|
+
|
|
+typedef struct WirelessEventCBData
|
|
+{
|
|
+ NMDevice80211Wireless * dev;
|
|
+ char * data;
|
|
+ int len;
|
|
+} WirelessEventCBData;
|
|
+
|
|
+static void
|
|
+wireless_event_cb_data_free (WirelessEventCBData *data)
|
|
+{
|
|
+ if (data) {
|
|
+ g_object_unref (data->dev);
|
|
+ g_free (data->data);
|
|
+ g_free (data);
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+wireless_event_helper (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless * self;
|
|
+ WirelessEventCBData * cb_data;
|
|
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
|
|
+ char *pos, *end, *custom;
|
|
+
|
|
+ cb_data = (WirelessEventCBData *) user_data;
|
|
+ g_return_val_if_fail (cb_data != NULL, FALSE);
|
|
+
|
|
+ self = NM_DEVICE_802_11_WIRELESS (cb_data->dev);
|
|
+ g_return_val_if_fail (self != NULL, FALSE);
|
|
+
|
|
+ g_return_val_if_fail (cb_data->data != NULL, FALSE);
|
|
+ g_return_val_if_fail (cb_data->len >= 0, FALSE);
|
|
+
|
|
+ pos = cb_data->data;
|
|
+ end = cb_data->data + cb_data->len;
|
|
+
|
|
+ while (pos + IW_EV_LCP_LEN <= end)
|
|
+ {
|
|
+ /* Event data may be unaligned, so make a local, aligned copy
|
|
+ * before processing. */
|
|
+ memcpy (&iwe_buf, pos, IW_EV_LCP_LEN);
|
|
+ if (iwe->len <= IW_EV_LCP_LEN)
|
|
+ break;
|
|
+
|
|
+ custom = pos + IW_EV_POINT_LEN;
|
|
+ if (self->priv->we_version > 18 &&
|
|
+ (iwe->cmd == IWEVMICHAELMICFAILURE ||
|
|
+ iwe->cmd == IWEVCUSTOM ||
|
|
+ iwe->cmd == IWEVASSOCREQIE ||
|
|
+ iwe->cmd == IWEVASSOCRESPIE ||
|
|
+ iwe->cmd == IWEVPMKIDCAND))
|
|
+ {
|
|
+ /* WE-19 removed the pointer from struct iw_point */
|
|
+ char *dpos = (char *) &iwe_buf.u.data.length;
|
|
+ int dlen = dpos - (char *) &iwe_buf;
|
|
+ memcpy (dpos, pos + IW_EV_LCP_LEN,
|
|
+ sizeof (struct iw_event) - dlen);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ memcpy (&iwe_buf, pos, sizeof (struct iw_event));
|
|
+ custom += IW_EV_POINT_OFF;
|
|
+ }
|
|
+
|
|
+ switch (iwe->cmd)
|
|
+ {
|
|
+ case SIOCGIWAP:
|
|
+ if ( memcmp(iwe->u.ap_addr.sa_data,
|
|
+ "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0
|
|
+ || memcmp(iwe->u.ap_addr.sa_data,
|
|
+ "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0
|
|
+ || memcmp(iwe->u.ap_addr.sa_data,
|
|
+ "\xFF\xFF\xFF\xFF\xFF\xFF", ETH_ALEN) == 0)
|
|
+ {
|
|
+ /* disassociated */
|
|
+ } else {
|
|
+ /* associated */
|
|
+ }
|
|
+ break;
|
|
+ case SIOCGIWSCAN:
|
|
+ cancel_scan_results_timeout (self);
|
|
+ request_and_convert_scan_results (self);
|
|
+ schedule_scan (self);
|
|
+ break;
|
|
+ }
|
|
+ pos += iwe->len;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void
|
|
+nm_device_802_11_wireless_event (NmNetlinkMonitor *monitor,
|
|
+ GObject *obj,
|
|
+ char *data,
|
|
+ int data_len,
|
|
+ NMDevice80211Wireless *self)
|
|
+{
|
|
+ GSource * source;
|
|
+ WirelessEventCBData * cb_data;
|
|
+
|
|
+ /* Make sure signal is for us */
|
|
+ if (NM_DEVICE (self) != NM_DEVICE (obj))
|
|
+ return;
|
|
+
|
|
+ cb_data = g_malloc0 (sizeof (WirelessEventCBData));
|
|
+ cb_data->dev = g_object_ref (G_OBJECT (self));
|
|
+ cb_data->data = g_malloc (data_len);
|
|
+ memcpy (cb_data->data, data, data_len);
|
|
+ cb_data->len = data_len;
|
|
+
|
|
+ source = g_idle_source_new ();
|
|
+ g_source_set_callback (source, (GSourceFunc) wireless_event_helper,
|
|
+ cb_data, (GDestroyNotify) wireless_event_cb_data_free);
|
|
+ g_source_attach (source, nm_device_get_main_context (NM_DEVICE (self)));
|
|
+ g_source_unref (source);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+real_update_link (NMDevice *dev)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+
|
|
+ /* If the supplicant isn't running, we can't possibly have a link */
|
|
+ if (!self->priv->supplicant.pid)
|
|
+ nm_device_set_active_link (NM_DEVICE (self), FALSE);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_periodic_update
|
|
+ *
|
|
+ * Periodically update device statistics and link state.
|
|
+ *
|
|
+ */
|
|
+static gboolean
|
|
+nm_device_802_11_periodic_update (gpointer data)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (data);
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, TRUE);
|
|
+
|
|
+ nm_device_802_11_wireless_update_signal_strength (self);
|
|
+ nm_device_802_11_wireless_update_bssid (self);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+real_start (NMDevice *dev)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ GSource * source;
|
|
+ guint source_id;
|
|
+
|
|
+ /* Start the scanning timeout for devices that can do scanning */
|
|
+ if (nm_device_get_capabilities (dev) & NM_DEVICE_CAP_WIRELESS_SCAN)
|
|
+ {
|
|
+ self->priv->pending_scan = g_idle_source_new ();
|
|
+ g_source_set_callback (self->priv->pending_scan,
|
|
+ nm_device_802_11_wireless_scan,
|
|
+ self,
|
|
+ nm_device_802_11_wireless_scan_done);
|
|
+ source_id = g_source_attach (self->priv->pending_scan,
|
|
+ nm_device_get_main_context (dev));
|
|
+ g_source_unref (self->priv->pending_scan);
|
|
+ }
|
|
+
|
|
+ /* Peridoically update link status and signal strength */
|
|
+ source = g_timeout_source_new (2000);
|
|
+ g_source_set_callback (source, nm_device_802_11_periodic_update, self, NULL);
|
|
+ source_id = g_source_attach (source, nm_device_get_main_context (dev));
|
|
+ g_source_unref (source);
|
|
+}
|
|
+
|
|
+static void
|
|
+real_deactivate_quickly (NMDevice *dev)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+
|
|
+ supplicant_cleanup (self);
|
|
+ remove_link_timeout (self);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+real_deactivate (NMDevice *dev)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMData * app_data;
|
|
+
|
|
+ app_data = nm_device_get_app_data (dev);
|
|
+ g_assert (app_data);
|
|
+
|
|
+ /* Clean up stuff, don't leave the card associated */
|
|
+ nm_device_802_11_wireless_set_essid (self, "");
|
|
+ nm_device_802_11_wireless_set_wep_enc_key (self, NULL, 0);
|
|
+ nm_device_802_11_wireless_set_mode (self, IW_MODE_INFRA);
|
|
+ nm_device_802_11_wireless_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_ACTIVE);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_copy_allowed_to_dev_list
|
|
+ *
|
|
+ * For devices that don't support wireless scanning, copy
|
|
+ * the allowed AP list to the device's ap list.
|
|
+ *
|
|
+ */
|
|
+void
|
|
+nm_device_802_11_wireless_copy_allowed_to_dev_list (NMDevice80211Wireless *self,
|
|
+ NMAccessPointList *allowed_list)
|
|
+{
|
|
+ NMAPListIter *iter;
|
|
+ NMAccessPoint *src_ap;
|
|
+ NMAccessPointList *dev_list;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (allowed_list == NULL)
|
|
+ return;
|
|
+
|
|
+ nm_device_802_11_wireless_ap_list_clear (self);
|
|
+ self->priv->ap_list = nm_ap_list_new (NETWORK_TYPE_ALLOWED);
|
|
+
|
|
+ if (!(iter = nm_ap_list_iter_new (allowed_list)))
|
|
+ return;
|
|
+
|
|
+ dev_list = nm_device_802_11_wireless_ap_list_get (self);
|
|
+ while ((src_ap = nm_ap_list_iter_next (iter)))
|
|
+ {
|
|
+ NMAccessPoint * dst_ap = nm_ap_new_from_ap (src_ap);
|
|
+
|
|
+ nm_ap_list_append_ap (dev_list, dst_ap);
|
|
+ nm_ap_unref (dst_ap);
|
|
+ }
|
|
+ nm_ap_list_iter_free (iter);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_get_address
|
|
+ *
|
|
+ * Get a device's hardware address
|
|
+ *
|
|
+ */
|
|
+void
|
|
+nm_device_802_11_wireless_get_address (NMDevice80211Wireless *self,
|
|
+ struct ether_addr *addr)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+ g_return_if_fail (addr != NULL);
|
|
+
|
|
+ memcpy (addr, &(self->priv->hw_addr), sizeof (struct ether_addr));
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_set_address
|
|
+ *
|
|
+ * Set a device's hardware address
|
|
+ *
|
|
+ */
|
|
+void
|
|
+nm_device_802_11_wireless_set_address (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMDevice *dev = NM_DEVICE (self);
|
|
+ struct ifreq req;
|
|
+ NMSock *sk;
|
|
+ int ret;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ sk = nm_dev_sock_open (dev, DEV_GENERAL, __FUNCTION__, NULL);
|
|
+ if (!sk)
|
|
+ return;
|
|
+ memset (&req, 0, sizeof (struct ifreq));
|
|
+ strncpy (req.ifr_name, nm_device_get_iface (dev), sizeof (req.ifr_name) - 1);
|
|
+
|
|
+ ret = ioctl (nm_dev_sock_get_fd (sk), SIOCGIFHWADDR, &req);
|
|
+ if (ret)
|
|
+ goto out;
|
|
+
|
|
+ memcpy (&(self->priv->hw_addr), &(req.ifr_hwaddr.sa_data), sizeof (struct ether_addr));
|
|
+
|
|
+out:
|
|
+ nm_dev_sock_close (sk);
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+link_to_specific_ap (NMDevice80211Wireless *self,
|
|
+ NMAccessPoint *ap,
|
|
+ gboolean default_link)
|
|
+{
|
|
+ gboolean have_link = FALSE;
|
|
+
|
|
+ /* Fake a link if we're scanning, we'll drop it later
|
|
+ * if it's really dead.
|
|
+ */
|
|
+ if (self->priv->scanning)
|
|
+ return TRUE;
|
|
+
|
|
+ if (is_associated (self))
|
|
+ {
|
|
+ const char * dev_essid = nm_device_802_11_wireless_get_essid (self);
|
|
+ const char * ap_essid = nm_ap_get_essid (ap);
|
|
+
|
|
+ if (dev_essid && ap_essid && !strcmp (dev_essid, ap_essid))
|
|
+ {
|
|
+ self->priv->failed_link_count = 0;
|
|
+ have_link = TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!have_link)
|
|
+ {
|
|
+ self->priv->failed_link_count++;
|
|
+ if (self->priv->failed_link_count <= 6)
|
|
+ have_link = default_link;
|
|
+ }
|
|
+
|
|
+ return have_link;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_update_best_ap
|
|
+ *
|
|
+ * Recalculate the "best" access point we should be associating with.
|
|
+ *
|
|
+ */
|
|
+NMAccessPoint *
|
|
+nm_device_802_11_wireless_get_best_ap (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMAccessPointList * ap_list;
|
|
+ NMAPListIter * iter;
|
|
+ NMAccessPoint * scan_ap = NULL;
|
|
+ NMAccessPoint * best_ap = NULL;
|
|
+ NMAccessPoint * cur_ap = NULL;
|
|
+ NMActRequest * req = NULL;
|
|
+ NMAccessPoint * trusted_best_ap = NULL;
|
|
+ NMAccessPoint * untrusted_best_ap = NULL;
|
|
+ GTimeVal trusted_latest_timestamp = {0, 0};
|
|
+ GTimeVal untrusted_latest_timestamp = {0, 0};
|
|
+ NMData * app_data;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ g_assert (app_data);
|
|
+
|
|
+ /* Devices that can't scan don't do anything automatic.
|
|
+ * The user must choose the access point from the menu.
|
|
+ */
|
|
+ if ( !(nm_device_get_capabilities (NM_DEVICE (self)) & NM_DEVICE_CAP_WIRELESS_SCAN)
|
|
+ && !nm_device_has_active_link (NM_DEVICE (self)))
|
|
+ return NULL;
|
|
+
|
|
+ if (!(ap_list = nm_device_802_11_wireless_ap_list_get (self)))
|
|
+ return NULL;
|
|
+
|
|
+ /* We prefer the currently selected access point if its user-chosen or if there
|
|
+ * is still a hardware link to it.
|
|
+ */
|
|
+ if ((req = nm_device_get_act_request (NM_DEVICE (self))))
|
|
+ {
|
|
+ if ((cur_ap = nm_act_request_get_ap (req)))
|
|
+ {
|
|
+ const char * essid = nm_ap_get_essid (cur_ap);
|
|
+ gboolean keep = FALSE;
|
|
+
|
|
+ if (nm_ap_get_user_created (cur_ap))
|
|
+ keep = TRUE;
|
|
+ else if (nm_act_request_get_user_requested (req))
|
|
+ keep = TRUE;
|
|
+ else if (link_to_specific_ap (self, cur_ap, TRUE))
|
|
+ keep = TRUE;
|
|
+
|
|
+ /* Only keep if its not in the invalid list and it _is_ in our scanned list */
|
|
+ if ( keep
|
|
+ && !nm_ap_list_get_ap_by_essid (app_data->invalid_ap_list, essid)
|
|
+ && nm_device_802_11_wireless_ap_list_get_ap_by_essid (self, essid))
|
|
+ {
|
|
+ nm_ap_ref (cur_ap);
|
|
+ return cur_ap;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!(iter = nm_ap_list_iter_new (ap_list)))
|
|
+ return NULL;
|
|
+ while ((scan_ap = nm_ap_list_iter_next (iter)))
|
|
+ {
|
|
+ NMAccessPoint *tmp_ap;
|
|
+ const char * ap_essid = nm_ap_get_essid (scan_ap);
|
|
+
|
|
+ /* Access points in the "invalid" list cannot be used */
|
|
+ if (nm_ap_list_get_ap_by_essid (app_data->invalid_ap_list, ap_essid))
|
|
+ continue;
|
|
+
|
|
+ if ((tmp_ap = nm_ap_list_get_ap_by_essid (app_data->allowed_ap_list, ap_essid)))
|
|
+ {
|
|
+ const GTimeVal *curtime = nm_ap_get_timestamp (tmp_ap);
|
|
+
|
|
+ /* Only connect to a blacklisted AP if the user has connected
|
|
+ * to this specific AP before.
|
|
+ */
|
|
+ gboolean blacklisted = nm_ap_has_manufacturer_default_essid (scan_ap);
|
|
+ if (blacklisted)
|
|
+ {
|
|
+ GSList *elt, *user_addrs;
|
|
+ const struct ether_addr *ap_addr;
|
|
+ char char_addr[20];
|
|
+
|
|
+ ap_addr = nm_ap_get_address (scan_ap);
|
|
+ user_addrs = nm_ap_get_user_addresses (tmp_ap);
|
|
+
|
|
+ memset (&char_addr[0], 0, 20);
|
|
+ iw_ether_ntop (ap_addr, &char_addr[0]);
|
|
+
|
|
+ for (elt = user_addrs; elt; elt = g_slist_next (elt))
|
|
+ {
|
|
+ if (elt->data && !strcmp (elt->data, &char_addr[0]))
|
|
+ {
|
|
+ blacklisted = FALSE;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_slist_foreach (user_addrs, (GFunc)g_free, NULL);
|
|
+ g_slist_free (user_addrs);
|
|
+ }
|
|
+
|
|
+ if (!blacklisted && nm_ap_get_trusted (tmp_ap) && (curtime->tv_sec > trusted_latest_timestamp.tv_sec))
|
|
+ {
|
|
+ trusted_latest_timestamp = *nm_ap_get_timestamp (tmp_ap);
|
|
+ trusted_best_ap = scan_ap;
|
|
+ nm_ap_set_security (trusted_best_ap, nm_ap_get_security (tmp_ap));
|
|
+ }
|
|
+ else if (!blacklisted && !nm_ap_get_trusted (tmp_ap) && (curtime->tv_sec > untrusted_latest_timestamp.tv_sec))
|
|
+ {
|
|
+ untrusted_latest_timestamp = *nm_ap_get_timestamp (tmp_ap);
|
|
+ untrusted_best_ap = scan_ap;
|
|
+ nm_ap_set_security (untrusted_best_ap, nm_ap_get_security (tmp_ap));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ best_ap = trusted_best_ap ? trusted_best_ap : untrusted_best_ap;
|
|
+ nm_ap_list_iter_free (iter);
|
|
+
|
|
+ if (best_ap)
|
|
+ nm_ap_ref (best_ap);
|
|
+
|
|
+ return best_ap;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_get_activation_ap
|
|
+ *
|
|
+ * Return an access point suitable for use in the device activation
|
|
+ * request.
|
|
+ *
|
|
+ */
|
|
+NMAccessPoint *
|
|
+nm_device_802_11_wireless_get_activation_ap (NMDevice80211Wireless *self,
|
|
+ const char *essid,
|
|
+ NMAPSecurity *security)
|
|
+{
|
|
+ NMAccessPoint *ap = NULL;
|
|
+ NMData * app_data;
|
|
+ NMAccessPointList * dev_ap_list;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+ g_return_val_if_fail (essid != NULL, NULL);
|
|
+
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ g_assert (app_data);
|
|
+
|
|
+ nm_debug ("Forcing AP '%s'", essid);
|
|
+
|
|
+ /* Find the AP in our card's scan list first.
|
|
+ * If its not there, create an entirely new AP.
|
|
+ */
|
|
+ dev_ap_list = nm_device_802_11_wireless_ap_list_get (self);
|
|
+ if (!(ap = nm_ap_list_get_ap_by_essid (dev_ap_list, essid)))
|
|
+ {
|
|
+ /* We need security information from the user if the network they
|
|
+ * request isn't in our scan list.
|
|
+ */
|
|
+ if (!security)
|
|
+ {
|
|
+ nm_warning ("%s: tried to manually connect to network '%s' without "
|
|
+ "providing security information!", __func__, essid);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* The 'else' block will create a new security and we'll going to unref the
|
|
+ security at the end of this function. So make sure we don't free the original. */
|
|
+ g_object_ref (security);
|
|
+
|
|
+ /* User chose a network we haven't seen in a scan, so create a
|
|
+ * "fake" access point and add it to the scan list.
|
|
+ */
|
|
+ ap = nm_ap_new ();
|
|
+ nm_ap_set_essid (ap, essid);
|
|
+ nm_ap_set_artificial (ap, TRUE);
|
|
+ nm_ap_set_broadcast (ap, FALSE);
|
|
+ /* Ensure the AP has some capabilities. They will get overwritten
|
|
+ * with the correct ones next time the AP is seen in a scan.
|
|
+ */
|
|
+ nm_ap_set_capabilities (ap, nm_ap_security_get_default_capabilities (security));
|
|
+ nm_ap_list_append_ap (dev_ap_list, ap);
|
|
+ nm_ap_unref (ap);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* If the AP is in the ignore list, we have to remove it since
|
|
+ * the User Knows What's Best.
|
|
+ */
|
|
+ nm_ap_list_remove_ap_by_essid (app_data->invalid_ap_list, nm_ap_get_essid (ap));
|
|
+
|
|
+ /* If we didn't get any security info, make some up. */
|
|
+ if (!security)
|
|
+ security = nm_ap_security_new_from_ap (ap);
|
|
+ }
|
|
+ g_assert (security);
|
|
+ nm_ap_set_security (ap, security);
|
|
+ nm_ap_add_capabilities_from_security (ap, security);
|
|
+
|
|
+ g_object_unref (security);
|
|
+
|
|
+ return ap;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_ap_list_clear
|
|
+ *
|
|
+ * Clears out the device's internal list of available access points.
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+nm_device_802_11_wireless_ap_list_clear (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (!self->priv->ap_list)
|
|
+ return;
|
|
+
|
|
+ nm_ap_list_unref (self->priv->ap_list);
|
|
+ self->priv->ap_list = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_ap_list_get_ap_by_essid
|
|
+ *
|
|
+ * Get the access point for a specific essid
|
|
+ *
|
|
+ */
|
|
+NMAccessPoint *
|
|
+nm_device_802_11_wireless_ap_list_get_ap_by_essid (NMDevice80211Wireless *self,
|
|
+ const char *essid)
|
|
+{
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+ g_return_val_if_fail (essid != NULL, NULL);
|
|
+
|
|
+ if (!self->priv->ap_list)
|
|
+ return NULL;
|
|
+
|
|
+ return nm_ap_list_get_ap_by_essid (self->priv->ap_list, essid);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_ap_list_get_ap_by_bssid
|
|
+ *
|
|
+ * Get the access point for a specific BSSID
|
|
+ *
|
|
+ */
|
|
+NMAccessPoint *
|
|
+nm_device_802_11_wireless_ap_list_get_ap_by_bssid (NMDevice80211Wireless *self,
|
|
+ const struct ether_addr *bssid)
|
|
+{
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+ g_return_val_if_fail (bssid != NULL, NULL);
|
|
+
|
|
+ if (!self->priv->ap_list)
|
|
+ return NULL;
|
|
+
|
|
+ return nm_ap_list_get_ap_by_address (self->priv->ap_list, bssid);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_ap_list_get_ap_by_obj_path
|
|
+ *
|
|
+ * Get the access point for a dbus object path. Requires an _unescaped_
|
|
+ * object path.
|
|
+ *
|
|
+ */
|
|
+NMAccessPoint *
|
|
+nm_device_802_11_wireless_ap_list_get_ap_by_obj_path (NMDevice80211Wireless *self,
|
|
+ const char *obj_path)
|
|
+{
|
|
+ NMAccessPoint * ret_ap = NULL;
|
|
+ char * built_path;
|
|
+ char * dev_path;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+ g_return_val_if_fail (obj_path != NULL, NULL);
|
|
+
|
|
+ if (!self->priv->ap_list)
|
|
+ return NULL;
|
|
+
|
|
+ dev_path = nm_dbus_get_object_path_for_device (NM_DEVICE (self));
|
|
+ dev_path = nm_dbus_unescape_object_path (dev_path);
|
|
+ built_path = g_strdup_printf ("%s/Networks/", dev_path);
|
|
+ g_free (dev_path);
|
|
+
|
|
+ if (strncmp (built_path, obj_path, strlen (built_path)) == 0)
|
|
+ {
|
|
+ char *essid = g_strdup (obj_path + strlen (built_path));
|
|
+
|
|
+ ret_ap = nm_ap_list_get_ap_by_essid (self->priv->ap_list, essid);
|
|
+ g_free (essid);
|
|
+ }
|
|
+ g_free (built_path);
|
|
+
|
|
+ return ret_ap;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_ap_list_get
|
|
+ *
|
|
+ * Return a pointer to the AP list
|
|
+ *
|
|
+ */
|
|
+NMAccessPointList *
|
|
+nm_device_802_11_wireless_ap_list_get (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+
|
|
+ return self->priv->ap_list;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+set_scan_interval_cb (gpointer user_data)
|
|
+{
|
|
+ NMData *data = (NMData*) user_data;
|
|
+
|
|
+ nm_device_802_11_wireless_set_scan_interval (data, NULL, NM_WIRELESS_SCAN_INTERVAL_INACTIVE);
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+void
|
|
+nm_device_802_11_wireless_set_scan_interval (NMData *data,
|
|
+ NMDevice80211Wireless *self,
|
|
+ NMWirelessScanInterval interval)
|
|
+{
|
|
+ static guint source_id = 0;
|
|
+ GSource * source = NULL;
|
|
+ GSList * elt;
|
|
+ gboolean found = FALSE;
|
|
+ guint8 seconds = nm_wireless_scan_interval_to_seconds (interval);
|
|
+
|
|
+ g_return_if_fail (data != NULL);
|
|
+
|
|
+ if (source_id != 0)
|
|
+ g_source_remove (source_id);
|
|
+
|
|
+ for (elt = data->dev_list; elt; elt = g_slist_next (elt))
|
|
+ {
|
|
+ NMDevice *d = (NMDevice *)(elt->data);
|
|
+ if (self && (NM_DEVICE (self) != d))
|
|
+ continue;
|
|
+
|
|
+ if (d && nm_device_is_802_11_wireless (d))
|
|
+ {
|
|
+ NM_DEVICE_802_11_WIRELESS (d)->priv->scan_interval = seconds;
|
|
+ if (self && (NM_DEVICE (self) == d))
|
|
+ found = TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* In case the scan interval didn't get set (which can happen during card
|
|
+ * initialization where the device gets set up before being added to the
|
|
+ * device list), set interval here
|
|
+ */
|
|
+ if (self && !found)
|
|
+ self->priv->scan_interval = seconds;
|
|
+
|
|
+ if (interval != NM_WIRELESS_SCAN_INTERVAL_INACTIVE)
|
|
+ {
|
|
+ source = g_timeout_source_new (120000);
|
|
+ g_source_set_callback (source, set_scan_interval_cb, (gpointer) data, NULL);
|
|
+ source_id = g_source_attach (source, data->main_context);
|
|
+ g_source_unref (source);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_get_mode
|
|
+ *
|
|
+ * Get managed/infrastructure/adhoc mode on a device
|
|
+ *
|
|
+ */
|
|
+int
|
|
+nm_device_802_11_wireless_get_mode (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ int mode = IW_MODE_AUTO;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, -1);
|
|
+
|
|
+ /* Force the card into Managed/Infrastructure mode */
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ struct iwreq wrq;
|
|
+
|
|
+ memset (&wrq, 0, sizeof (struct iwreq));
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET IWMODE.", nm_device_get_iface (NM_DEVICE (self)));
|
|
+#endif
|
|
+ if (iw_get_ext (nm_dev_sock_get_fd (sk), nm_device_get_iface (NM_DEVICE (self)), SIOCGIWMODE, &wrq) == 0)
|
|
+ {
|
|
+ if ((mode == IW_MODE_ADHOC) || (mode == IW_MODE_INFRA))
|
|
+ mode = wrq.u.mode;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_warning ("error getting card mode on %s: %s",
|
|
+ nm_device_get_iface (NM_DEVICE (self)), strerror (errno));
|
|
+ }
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ return mode;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_set_mode
|
|
+ *
|
|
+ * Set managed/infrastructure/adhoc mode on a device
|
|
+ *
|
|
+ */
|
|
+gboolean
|
|
+nm_device_802_11_wireless_set_mode (NMDevice80211Wireless *self,
|
|
+ const int mode)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ gboolean success = FALSE;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, FALSE);
|
|
+ g_return_val_if_fail ((mode == IW_MODE_INFRA) || (mode == IW_MODE_ADHOC) || (mode == IW_MODE_AUTO), FALSE);
|
|
+
|
|
+ if (nm_device_802_11_wireless_get_mode (self) == mode)
|
|
+ return TRUE;
|
|
+
|
|
+ /* Force the card into Managed/Infrastructure mode */
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ const char * iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ struct iwreq wreq;
|
|
+
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to SET IWMODE.", iface);
|
|
+#endif
|
|
+ wreq.u.mode = mode;
|
|
+ if (iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWMODE, &wreq) == 0)
|
|
+ success = TRUE;
|
|
+ else
|
|
+ {
|
|
+ if (errno != ENODEV)
|
|
+ {
|
|
+ nm_warning ("error setting card %s to %s mode: %s",
|
|
+ iface,
|
|
+ mode == IW_MODE_INFRA ? "Infrastructure" : \
|
|
+ (mode == IW_MODE_ADHOC ? "Ad-Hoc" : \
|
|
+ (mode == IW_MODE_AUTO ? "Auto" : "unknown")),
|
|
+ strerror (errno));
|
|
+ }
|
|
+ }
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ return success;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_get_signal_strength
|
|
+ *
|
|
+ * Get the current signal strength of a wireless device. This only works when
|
|
+ * the card is associated with an access point, so will only work for the
|
|
+ * active device.
|
|
+ *
|
|
+ * Returns: -1 on error
|
|
+ * 0 - 100 strength percentage of the connection to the current access point
|
|
+ *
|
|
+ */
|
|
+gint8
|
|
+nm_device_802_11_wireless_get_signal_strength (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_val_if_fail (self != NULL, -1);
|
|
+
|
|
+ return (self->priv->strength);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * wireless_stats_to_percent
|
|
+ *
|
|
+ * Convert an iw_stats structure from a scan or the card into
|
|
+ * a magical signal strength percentage.
|
|
+ *
|
|
+ */
|
|
+static int
|
|
+wireless_qual_to_percent (NMDevice *dev,
|
|
+ const struct iw_quality *qual,
|
|
+ const struct iw_quality *max_qual,
|
|
+ const struct iw_quality *avg_qual)
|
|
+{
|
|
+ int percent = -1;
|
|
+ int level_percent = -1;
|
|
+
|
|
+ g_return_val_if_fail (qual != NULL, -1);
|
|
+ g_return_val_if_fail (max_qual != NULL, -1);
|
|
+ g_return_val_if_fail (avg_qual != NULL, -1);
|
|
+
|
|
+#ifdef IW_QUAL_DEBUG
|
|
+nm_debug ("QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
|
|
+(__s8)qual->qual, qual->qual, qual->qual,
|
|
+(__s8)qual->level, qual->level, qual->level,
|
|
+(__s8)qual->noise, qual->noise, qual->noise,
|
|
+qual->updated,
|
|
+(__s8)max_qual->qual, max_qual->qual, max_qual->qual,
|
|
+(__s8)max_qual->level, max_qual->level, max_qual->level,
|
|
+(__s8)max_qual->noise, max_qual->noise, max_qual->noise,
|
|
+max_qual->updated);
|
|
+#endif
|
|
+
|
|
+ /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is.
|
|
+ * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be
|
|
+ * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers
|
|
+ * are free to use whatever they want to calculate "Link Quality".
|
|
+ */
|
|
+ if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID))
|
|
+ percent = (int) (100 * ((double) qual->qual / (double) max_qual->qual));
|
|
+
|
|
+ /* If the driver doesn't specify a complete and valid quality, we have two options:
|
|
+ *
|
|
+ * 1) dBm: driver must specify max_qual->level = 0, and have valid values for
|
|
+ * qual->level and (qual->noise OR max_qual->noise)
|
|
+ * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for
|
|
+ * qual->level and max_qual->level
|
|
+ *
|
|
+ * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise,
|
|
+ * If drivers don't conform to it, they are wrong and need to be fixed.
|
|
+ */
|
|
+
|
|
+ if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */
|
|
+ && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */
|
|
+ && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */
|
|
+ || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */
|
|
+ )
|
|
+ {
|
|
+ /* Absolute power values (dBm) */
|
|
+
|
|
+ /* Reasonable fallbacks for dumb drivers that don't specify either level. */
|
|
+ #define FALLBACK_NOISE_FLOOR_DBM -90
|
|
+ #define FALLBACK_SIGNAL_MAX_DBM -20
|
|
+ int max_level = FALLBACK_SIGNAL_MAX_DBM;
|
|
+ int noise = FALLBACK_NOISE_FLOOR_DBM;
|
|
+ int level = qual->level - 0x100;
|
|
+
|
|
+ level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
|
|
+
|
|
+ if ((qual->noise > 0) && (!qual->updated & IW_QUAL_NOISE_INVALID))
|
|
+ noise = qual->noise - 0x100;
|
|
+ else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID))
|
|
+ noise = max_qual->noise - 0x100;
|
|
+ noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
|
|
+
|
|
+ /* A sort of signal-to-noise ratio calculation */
|
|
+ level_percent = (int)(100 - 70 *(
|
|
+ ((double)max_level - (double)level) /
|
|
+ ((double)max_level - (double)noise)));
|
|
+#ifdef IW_QUAL_DEBUG
|
|
+ nm_debug ("QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", level_percent, max_level, level, noise);
|
|
+#endif
|
|
+ }
|
|
+ else if ((max_qual->level != 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */
|
|
+ && !(qual->updated & IW_QUAL_LEVEL_INVALID))
|
|
+ {
|
|
+ /* Relative power values (RSSI) */
|
|
+
|
|
+ int level = qual->level;
|
|
+
|
|
+ /* Signal level is relavtive (0 -> max_qual->level) */
|
|
+ level = CLAMP (level, 0, max_qual->level);
|
|
+ level_percent = (int)(100 * ((double)level / (double)max_qual->level));
|
|
+#ifdef IW_QUAL_DEBUG
|
|
+ nm_debug ("QL2: level_percent is %d. max_level %d, level %d.", level_percent, max_qual->level, level);
|
|
+#endif
|
|
+ }
|
|
+ else if (percent == -1)
|
|
+ {
|
|
+#ifdef IW_QUAL_DEBUG
|
|
+ nm_debug ("QL: Could not get quality %% value from driver. Driver is probably buggy.");
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
|
|
+ if ((percent < 1 || !strcmp (nm_device_get_driver (dev), "ath_pci")) && (level_percent >= 0))
|
|
+ percent = level_percent;
|
|
+
|
|
+#ifdef IW_QUAL_DEBUG
|
|
+ nm_debug ("QL: Final quality percent is %d (%d).", percent, CLAMP (percent, 0, 100));
|
|
+#endif
|
|
+ return (CLAMP (percent, 0, 100));
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_get_essid
|
|
+ *
|
|
+ * If a device is wireless, return the essid that it is attempting
|
|
+ * to use.
|
|
+ *
|
|
+ * Returns: allocated string containing essid. Must be freed by caller.
|
|
+ *
|
|
+ */
|
|
+const char *
|
|
+nm_device_802_11_wireless_get_essid (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ int err;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ wireless_config info;
|
|
+
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET 'basic config' for ESSID.", iface);
|
|
+#endif
|
|
+ err = iw_get_basic_config (nm_dev_sock_get_fd (sk), iface, &info);
|
|
+ if (err >= 0)
|
|
+ {
|
|
+ if (self->priv->cur_essid)
|
|
+ g_free (self->priv->cur_essid);
|
|
+ self->priv->cur_essid = g_strdup (info.essid);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_warning ("error getting ESSID for device %s: %s",
|
|
+ iface, strerror (errno));
|
|
+ }
|
|
+
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ return self->priv->cur_essid;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_set_essid
|
|
+ *
|
|
+ * If a device is wireless, set the essid that it should use.
|
|
+ */
|
|
+void
|
|
+nm_device_802_11_wireless_set_essid (NMDevice80211Wireless *self,
|
|
+ const char *essid)
|
|
+{
|
|
+ NMSock* sk;
|
|
+ int err;
|
|
+ struct iwreq wreq;
|
|
+ unsigned char safe_essid[IW_ESSID_MAX_SIZE + 1] = "\0";
|
|
+ const char * iface;
|
|
+ const char * driver;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ /* Make sure the essid we get passed is a valid size */
|
|
+ if (!essid)
|
|
+ safe_essid[0] = '\0';
|
|
+ else
|
|
+ {
|
|
+ strncpy ((char *) safe_essid, essid, IW_ESSID_MAX_SIZE);
|
|
+ safe_essid[IW_ESSID_MAX_SIZE] = '\0';
|
|
+ }
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ wreq.u.essid.pointer = (caddr_t) safe_essid;
|
|
+ wreq.u.essid.length = strlen ((char *) safe_essid) + 1;
|
|
+ wreq.u.essid.flags = 1; /* Enable essid on card */
|
|
+
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to SET IWESSID.", iface);
|
|
+#endif
|
|
+ if ((err = iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWESSID, &wreq)) == -1)
|
|
+ {
|
|
+ if (errno != ENODEV)
|
|
+ {
|
|
+ nm_warning ("error setting ESSID to '%s' for device %s: %s",
|
|
+ safe_essid, iface, strerror (errno));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nm_dev_sock_close (sk);
|
|
+
|
|
+ /* Orinoco cards seem to need extra time here to not screw
|
|
+ * up the firmware, which reboots when you set the ESSID.
|
|
+ * Unfortunately, there's no way to know when the card is back up
|
|
+ * again. Sigh...
|
|
+ */
|
|
+ driver = nm_device_get_driver (NM_DEVICE (self));
|
|
+ if (!driver || !strcmp (driver, "orinoco"))
|
|
+ sleep (2);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+#if 0
|
|
+/*
|
|
+ * nm_device_get_frequency
|
|
+ *
|
|
+ * For wireless devices, get the frequency we broadcast/receive on.
|
|
+ *
|
|
+ */
|
|
+static double
|
|
+nm_device_802_11_wireless_get_frequency (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ int err;
|
|
+ double freq = 0;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, 0);
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ struct iwreq wrq;
|
|
+
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET IWFREQ.", iface);
|
|
+#endif
|
|
+ err = iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWFREQ, &wrq);
|
|
+ if (err >= 0)
|
|
+ freq = iw_freq2float (&wrq.u.freq);
|
|
+ if (err == -1)
|
|
+ {
|
|
+ nm_warning ("error getting frequency for device %s: %s",
|
|
+ iface, strerror (errno));
|
|
+ }
|
|
+
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ return freq;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * nm_device_set_frequency
|
|
+ *
|
|
+ * For wireless devices, set the frequency to broadcast/receive on.
|
|
+ * A frequency <= 0 means "auto".
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+nm_device_802_11_wireless_set_frequency (NMDevice80211Wireless *self,
|
|
+ const double freq)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ int err;
|
|
+ const char * iface;
|
|
+
|
|
+ /* HACK FOR NOW */
|
|
+ if (freq <= 0)
|
|
+ return;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (fabs (nm_device_802_11_wireless_get_frequency (self) - freq) <= DBL_EPSILON)
|
|
+ return;
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ struct iwreq wrq;
|
|
+
|
|
+ if (freq <= 0)
|
|
+ {
|
|
+ /* Auto */
|
|
+ /* People like to make things hard for us. Even though iwlib/iwconfig say
|
|
+ * that wrq.u.freq.m should be -1 for "auto" mode, nobody actually supports
|
|
+ * that. Madwifi actually uses "0" to mean "auto". So, we'll try 0 first
|
|
+ * and if that doesn't work, fall back to the iwconfig method and use -1.
|
|
+ *
|
|
+ * As a further note, it appears that Atheros/Madwifi cards can't go back to
|
|
+ * any-channel operation once you force set the channel on them. For example,
|
|
+ * if you set a prism54 card to a specific channel, but then set the ESSID to
|
|
+ * something else later, it will scan for the ESSID and switch channels just fine.
|
|
+ * Atheros cards, however, just stay at the channel you previously set and don't
|
|
+ * budge, no matter what you do to them, until you tell them to go back to
|
|
+ * any-channel operation.
|
|
+ */
|
|
+ wrq.u.freq.m = 0;
|
|
+ wrq.u.freq.e = 0;
|
|
+ wrq.u.freq.flags = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Fixed */
|
|
+ wrq.u.freq.flags = IW_FREQ_FIXED;
|
|
+ iw_float2freq (freq, &wrq.u.freq);
|
|
+ }
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to SET IWFREQ.", iface);
|
|
+#endif
|
|
+ if ((err = iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWFREQ, &wrq)) == -1)
|
|
+ {
|
|
+ gboolean success = FALSE;
|
|
+ if ((freq <= 0) && ((errno == EINVAL) || (errno == EOPNOTSUPP)))
|
|
+ {
|
|
+ /* Ok, try "auto" the iwconfig way if the Atheros way didn't work */
|
|
+ wrq.u.freq.m = -1;
|
|
+ wrq.u.freq.e = 0;
|
|
+ wrq.u.freq.flags = 0;
|
|
+ if (iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWFREQ, &wrq) != -1)
|
|
+ success = TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * nm_device_get_bitrate
|
|
+ *
|
|
+ * For wireless devices, get the bitrate to broadcast/receive at.
|
|
+ * Returned value is rate in Mb/s.
|
|
+ *
|
|
+ */
|
|
+int
|
|
+nm_device_802_11_wireless_get_bitrate (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ int err = -1;
|
|
+ struct iwreq wrq;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, 0);
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET IWRATE.", iface);
|
|
+#endif
|
|
+ err = iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWRATE, &wrq);
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+
|
|
+ return ((err >= 0) ? wrq.u.bitrate.value / 1000000 : 0);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+/*
|
|
+ * nm_device_set_bitrate
|
|
+ *
|
|
+ * For wireless devices, set the bitrate to broadcast/receive at.
|
|
+ * Rate argument should be in Mbps (mega-bits per second), or 0 for automatic.
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+nm_device_802_11_wireless_set_bitrate (NMDevice80211Wireless *self,
|
|
+ const int Mbps)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (nm_device_802_11_wireless_get_bitrate (self) == Mbps)
|
|
+ return;
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ struct iwreq wrq;
|
|
+
|
|
+ if (Mbps != 0)
|
|
+ {
|
|
+ wrq.u.bitrate.value = Mbps * 1000;
|
|
+ wrq.u.bitrate.fixed = 1;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Auto bitrate */
|
|
+ wrq.u.bitrate.value = -1;
|
|
+ wrq.u.bitrate.fixed = 0;
|
|
+ }
|
|
+ /* Silently fail as not all drivers support setting bitrate yet (ipw2x00 for example) */
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to SET IWRATE.", iface);
|
|
+#endif
|
|
+ iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWRATE, &wrq);
|
|
+
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_get_bssid
|
|
+ *
|
|
+ * If a device is wireless, get the access point's ethernet address
|
|
+ * that the card is associated with.
|
|
+ */
|
|
+void
|
|
+nm_device_802_11_wireless_get_bssid (NMDevice80211Wireless *self,
|
|
+ struct ether_addr *bssid)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ struct iwreq wrq;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+ g_return_if_fail (bssid != NULL);
|
|
+
|
|
+ memset (bssid, 0, sizeof (struct ether_addr));
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET IWAP.", iface);
|
|
+#endif
|
|
+ if (iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWAP, &wrq) >= 0)
|
|
+ memcpy (bssid, &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr));
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_set_wep_enc_key
|
|
+ *
|
|
+ * If a device is wireless, set the encryption key that it should use.
|
|
+ *
|
|
+ * key: encryption key to use, or NULL or "" to disable encryption.
|
|
+ */
|
|
+static void
|
|
+nm_device_802_11_wireless_set_wep_enc_key (NMDevice80211Wireless *self,
|
|
+ const char *key,
|
|
+ int auth_method)
|
|
+{
|
|
+ NMSock * sk;
|
|
+ struct iwreq wreq;
|
|
+ int keylen;
|
|
+ unsigned char safe_key[IW_ENCODING_TOKEN_MAX + 1];
|
|
+ gboolean set_key = FALSE;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ /* Make sure the essid we get passed is a valid size */
|
|
+ if (!key)
|
|
+ safe_key[0] = '\0';
|
|
+ else
|
|
+ {
|
|
+ strncpy ((char *) safe_key, key, IW_ENCODING_TOKEN_MAX);
|
|
+ safe_key[IW_ENCODING_TOKEN_MAX] = '\0';
|
|
+ }
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ wreq.u.data.pointer = (caddr_t) NULL;
|
|
+ wreq.u.data.length = 0;
|
|
+ wreq.u.data.flags = IW_ENCODE_ENABLED;
|
|
+
|
|
+ /* Unfortunately, some drivers (Cisco) don't make a distinction between
|
|
+ * Open System authentication mode and whether or not to use WEP. You
|
|
+ * DON'T have to use WEP when using Open System, but these cards force
|
|
+ * it. Therefore, we have to set Open System mode when using WEP.
|
|
+ */
|
|
+
|
|
+ if (strlen ((char *) safe_key) == 0)
|
|
+ {
|
|
+ wreq.u.data.flags |= IW_ENCODE_DISABLED | IW_ENCODE_NOKEY;
|
|
+ set_key = TRUE;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ unsigned char parsed_key[IW_ENCODING_TOKEN_MAX + 1];
|
|
+
|
|
+ keylen = iw_in_key_full (nm_dev_sock_get_fd (sk), iface,
|
|
+ (char *) safe_key, &parsed_key[0], &wreq.u.data.flags);
|
|
+ if (keylen > 0)
|
|
+ {
|
|
+ switch (auth_method)
|
|
+ {
|
|
+ case IW_AUTH_ALG_OPEN_SYSTEM:
|
|
+ wreq.u.data.flags |= IW_ENCODE_OPEN;
|
|
+ break;
|
|
+ case IW_AUTH_ALG_SHARED_KEY:
|
|
+ wreq.u.data.flags |= IW_ENCODE_RESTRICTED;
|
|
+ break;
|
|
+ default:
|
|
+ wreq.u.data.flags |= IW_ENCODE_RESTRICTED;
|
|
+ break;
|
|
+ }
|
|
+ wreq.u.data.pointer = (caddr_t) &parsed_key;
|
|
+ wreq.u.data.length = keylen;
|
|
+ set_key = TRUE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (set_key)
|
|
+ {
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to SET IWENCODE.", iface);
|
|
+#endif
|
|
+ if (iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWENCODE, &wreq) == -1)
|
|
+ {
|
|
+ if (errno != ENODEV)
|
|
+ {
|
|
+ nm_warning ("error setting key for device %s: %s",
|
|
+ iface, strerror (errno));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nm_dev_sock_close (sk);
|
|
+ } else nm_warning ("could not get wireless control socket for device %s", iface);
|
|
+}
|
|
+
|
|
+static void
|
|
+free_process_scan_cb_data (NMWirelessScanResults *cb_data)
|
|
+{
|
|
+ if (!cb_data)
|
|
+ return;
|
|
+
|
|
+ if (cb_data->results)
|
|
+ g_free (cb_data->results);
|
|
+ g_object_unref (G_OBJECT (cb_data->dev));
|
|
+ memset (cb_data, 0, sizeof (NMWirelessScanResults));
|
|
+ g_free (cb_data);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * convert_scan_results
|
|
+ *
|
|
+ * Process results of an iwscan() into our own AP lists. We're an idle function,
|
|
+ * but we never reschedule ourselves.
|
|
+ *
|
|
+ */
|
|
+static gboolean
|
|
+convert_scan_results (gpointer user_data)
|
|
+{
|
|
+ NMWirelessScanResults * cb_data = (NMWirelessScanResults *) user_data;
|
|
+ NMDevice80211Wireless * self;
|
|
+ GTimeVal cur_time;
|
|
+ NMAPListIter * iter = NULL;
|
|
+ const char * iface;
|
|
+ NMData * app_data;
|
|
+ NMAccessPointList * ap_list;
|
|
+
|
|
+ g_return_val_if_fail (cb_data != NULL, FALSE);
|
|
+
|
|
+ self = NM_DEVICE_802_11_WIRELESS (cb_data->dev);
|
|
+ if (!self || !cb_data->results)
|
|
+ return FALSE;
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ if (cb_data->results_len > 0)
|
|
+ {
|
|
+ if (!process_scan_results (self, cb_data->results, cb_data->results_len))
|
|
+ nm_warning ("process_scan_results() on device %s returned an error.", iface);
|
|
+
|
|
+ /* Once we have the list, copy in any relevant information from our Allowed list. */
|
|
+ nm_ap_list_copy_properties (nm_device_802_11_wireless_ap_list_get (self), app_data->allowed_ap_list);
|
|
+ }
|
|
+
|
|
+ /* Walk the access point list and remove any access points older than thrice the inactive scan interval */
|
|
+ g_get_current_time (&cur_time);
|
|
+ ap_list = nm_device_802_11_wireless_ap_list_get (self);
|
|
+ if (ap_list && (iter = nm_ap_list_iter_new (ap_list)))
|
|
+ {
|
|
+ NMAccessPoint *outdated_ap;
|
|
+ GSList * outdated_list = NULL;
|
|
+ GSList * elt;
|
|
+ NMActRequest * req = nm_device_get_act_request (NM_DEVICE (self));
|
|
+ NMAccessPoint *cur_ap = NULL;
|
|
+
|
|
+ if (req)
|
|
+ {
|
|
+ cur_ap = nm_act_request_get_ap (req);
|
|
+ g_assert (cur_ap);
|
|
+ }
|
|
+
|
|
+ while ((outdated_ap = nm_ap_list_iter_next (iter)))
|
|
+ {
|
|
+ const GTimeVal *ap_time = nm_ap_get_last_seen (outdated_ap);
|
|
+ gboolean keep_around = FALSE;
|
|
+ guint inactive_interval_s;
|
|
+ guint prune_interval_s;
|
|
+
|
|
+ /* Don't ever prune the AP we're currently associated with */
|
|
+ if ( nm_ap_get_essid (outdated_ap)
|
|
+ && (cur_ap && (nm_null_safe_strcmp (nm_ap_get_essid (cur_ap), nm_ap_get_essid (outdated_ap))) == 0))
|
|
+ keep_around = TRUE;
|
|
+
|
|
+ inactive_interval_s = nm_wireless_scan_interval_to_seconds (NM_WIRELESS_SCAN_INTERVAL_INACTIVE);
|
|
+ prune_interval_s = inactive_interval_s * 3;
|
|
+
|
|
+ if (!keep_around && (ap_time->tv_sec + prune_interval_s < cur_time.tv_sec))
|
|
+ outdated_list = g_slist_append (outdated_list, outdated_ap);
|
|
+ }
|
|
+ nm_ap_list_iter_free (iter);
|
|
+
|
|
+ /* Ok, now remove outdated ones. We have to do it after the lock
|
|
+ * because nm_ap_list_remove_ap() locks the list too.
|
|
+ */
|
|
+ for (elt = outdated_list; elt; elt = g_slist_next (elt))
|
|
+ {
|
|
+ if ((outdated_ap = (NMAccessPoint *)(elt->data)))
|
|
+ {
|
|
+ nm_dbus_signal_wireless_network_change (app_data->dbus_connection, self, outdated_ap, NETWORK_STATUS_DISAPPEARED, -1);
|
|
+ nm_ap_list_remove_ap (nm_device_802_11_wireless_ap_list_get (self), outdated_ap);
|
|
+ }
|
|
+ }
|
|
+ g_slist_free (outdated_list);
|
|
+ }
|
|
+
|
|
+ nm_policy_schedule_device_change_check (app_data);
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+
|
|
+#define SCAN_SLEEP_CENTISECONDS 10 /* sleep 1/10 of a second, waiting for data */
|
|
+static void
|
|
+request_and_convert_scan_results (NMDevice80211Wireless *self)
|
|
+{
|
|
+ NMSock *sk;
|
|
+ NMWirelessScanResults *scan_results = NULL;
|
|
+ const char *iface;
|
|
+ struct iwreq iwr;
|
|
+ guint8 tries = 0;
|
|
+ gboolean success = FALSE;
|
|
+ guint8 *buf = NULL;
|
|
+ size_t buflen = IW_SCAN_MAX_DATA;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (!(sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ return;
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ for (;;)
|
|
+ {
|
|
+ if (!(buf = g_malloc0 (buflen)))
|
|
+ break;
|
|
+ iwr.u.data.pointer = buf;
|
|
+ iwr.u.data.flags = 0;
|
|
+ iwr.u.data.length = buflen;
|
|
+
|
|
+ if (iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWSCAN, &iwr) == 0)
|
|
+ {
|
|
+ /* success */
|
|
+ buflen = iwr.u.data.length;
|
|
+ success = TRUE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ g_free (buf);
|
|
+ buf = NULL;
|
|
+
|
|
+ if ((errno == E2BIG) && (buflen < 100000)) /* Buffer not big enough */
|
|
+ {
|
|
+ buflen *= 2;
|
|
+ }
|
|
+ else if (errno == EAGAIN) /* Card doesn't have results yet */
|
|
+ {
|
|
+ /* We've already waited for the scan data, so don't give
|
|
+ * drivers too much slack here.
|
|
+ */
|
|
+ if (tries > 4 * SCAN_SLEEP_CENTISECONDS)
|
|
+ {
|
|
+ nm_warning ("card took too much time scanning. Get a better one.");
|
|
+ break;
|
|
+ }
|
|
+ g_usleep (G_USEC_PER_SEC / SCAN_SLEEP_CENTISECONDS);
|
|
+ tries++;
|
|
+ }
|
|
+ else if (errno == ENODATA) /* No scan results */
|
|
+ {
|
|
+ buflen = 0;
|
|
+ success = TRUE;
|
|
+ break;
|
|
+ }
|
|
+ else /* Random errors */
|
|
+ {
|
|
+ nm_warning ("unknown error, or the card returned too much scan info: %s",
|
|
+ strerror (errno));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ nm_dev_sock_close (sk);
|
|
+
|
|
+ if (success)
|
|
+ {
|
|
+ NMData * app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ GSource * convert_source = g_idle_source_new ();
|
|
+ GTimeVal cur_time;
|
|
+
|
|
+ /* We run the scan processing function from the main thread, since it must deliver
|
|
+ * messages over DBUS. Plus, that way the main thread is the only thread that has
|
|
+ * to modify the device's access point list.
|
|
+ */
|
|
+ scan_results = g_malloc0 (sizeof (NMWirelessScanResults));
|
|
+ g_object_ref (G_OBJECT (self));
|
|
+ scan_results->dev = self;
|
|
+ scan_results->results = buf;
|
|
+ scan_results->results_len = buflen;
|
|
+
|
|
+ g_source_set_callback (convert_source, convert_scan_results, scan_results,
|
|
+ (GDestroyNotify) free_process_scan_cb_data);
|
|
+ g_source_attach (convert_source, app_data->main_context);
|
|
+ g_source_unref (convert_source);
|
|
+ g_get_current_time (&cur_time);
|
|
+ self->priv->last_scan = cur_time.tv_sec;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+scan_results_timeout_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->scan_timeout = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * scan_results_timeout
|
|
+ *
|
|
+ * Request scan results from the card if it has taken more time than
|
|
+ * we allow. Also works around drivers that don't send notifications of
|
|
+ * completed scans to userspace.
|
|
+ */
|
|
+static gboolean
|
|
+scan_results_timeout (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_val_if_fail (self != NULL, FALSE);
|
|
+
|
|
+ request_and_convert_scan_results (self);
|
|
+ schedule_scan (self);
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * schedule_scan_results_timeout
|
|
+ *
|
|
+ * For cards that don't send a wireless event for scan results,
|
|
+ * we hit the card after the timeout and explicitly ask for them.
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+schedule_scan_results_timeout (NMDevice80211Wireless *self)
|
|
+{
|
|
+ GMainContext * context;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ cancel_scan_results_timeout (self);
|
|
+
|
|
+ /* Wait 10 seconds for scan results */
|
|
+ self->priv->scan_timeout = g_timeout_source_new (10000);
|
|
+ g_source_set_callback (self->priv->scan_timeout,
|
|
+ (GSourceFunc) scan_results_timeout,
|
|
+ self,
|
|
+ scan_results_timeout_done);
|
|
+ context = nm_device_get_main_context (NM_DEVICE (self));
|
|
+ g_source_attach (self->priv->scan_timeout, context);
|
|
+ g_source_unref (self->priv->scan_timeout);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * cancel_scan_results_timeout
|
|
+ *
|
|
+ * Cancel an existing scan results timeout
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+cancel_scan_results_timeout (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (self->priv->scan_timeout)
|
|
+ {
|
|
+ g_source_destroy (self->priv->scan_timeout); /* Balance g_timeout_source_new() */
|
|
+ self->priv->scan_timeout = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+nm_device_802_11_wireless_scan_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->pending_scan = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_802_11_wireless_scan
|
|
+ *
|
|
+ * Trigger a scan request
|
|
+ *
|
|
+ */
|
|
+static gboolean
|
|
+nm_device_802_11_wireless_scan (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+ guint32 caps;
|
|
+ NMData * app_data;
|
|
+ gboolean success = FALSE;
|
|
+ const char * iface;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, FALSE);
|
|
+
|
|
+ if (!(app_data = nm_device_get_app_data (NM_DEVICE (self))))
|
|
+ goto out;
|
|
+
|
|
+ caps = nm_device_get_capabilities (NM_DEVICE (self));
|
|
+ if (!(caps & NM_DEVICE_CAP_NM_SUPPORTED) || !(caps & NM_DEVICE_CAP_WIRELESS_SCAN))
|
|
+ goto out;
|
|
+
|
|
+ self->priv->pending_scan = NULL;
|
|
+
|
|
+ /* Reschedule ourselves if all wireless is disabled, we're asleep,
|
|
+ * or we are currently activating.
|
|
+ */
|
|
+ if ( (app_data->wireless_enabled == FALSE)
|
|
+ || (app_data->asleep == TRUE)
|
|
+ || (nm_device_is_activating (NM_DEVICE (self)) == TRUE))
|
|
+ {
|
|
+ nm_device_802_11_wireless_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_INIT);
|
|
+ schedule_scan (self);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * A/B/G cards should only scan if they are disconnected. Set the timeout to active
|
|
+ * for the case we lose this connection shortly, it will reach this point and then
|
|
+ * nm_device_is_activated will return FALSE, letting the scan proceed.
|
|
+ */
|
|
+ if ((self->priv->num_freqs > 14) && nm_device_is_activated (NM_DEVICE (self)) == TRUE)
|
|
+ {
|
|
+ nm_device_802_11_wireless_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_ACTIVE);
|
|
+ schedule_scan (self);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ self->priv->scanning = TRUE;
|
|
+
|
|
+ /* Device must be up before we can scan */
|
|
+ if (nm_device_bring_up_wait (NM_DEVICE (self), 1))
|
|
+ {
|
|
+ schedule_scan (self);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* If we're currently connected to an AP, let wpa_supplicant initiate
|
|
+ * the scan request rather than doing it ourselves.
|
|
+ */
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ if (self->priv->supplicant.ctrl)
|
|
+ {
|
|
+ if (nm_utils_supplicant_request_with_check (self->priv->supplicant.ctrl,
|
|
+ "OK", __func__, NULL, "SCAN"))
|
|
+ success = TRUE;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ NMSock * sk;
|
|
+
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)))
|
|
+ {
|
|
+ struct iwreq wrq;
|
|
+
|
|
+ wrq.u.data.pointer = NULL;
|
|
+ wrq.u.data.flags = 0;
|
|
+ wrq.u.data.length = 0;
|
|
+ if (iw_set_ext (nm_dev_sock_get_fd (sk), iface, SIOCSIWSCAN, &wrq) == 0)
|
|
+ success = TRUE;
|
|
+ nm_dev_sock_close (sk);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (success)
|
|
+ {
|
|
+ schedule_scan_results_timeout (self);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_warning ("could not trigger wireless scan on device %s: %s",
|
|
+ iface, strerror (errno));
|
|
+ schedule_scan (self);
|
|
+ }
|
|
+
|
|
+out:
|
|
+ return FALSE; /* Balance g_source_attach(), destroyed on return */
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * nm_device_wireless_schedule_scan
|
|
+ *
|
|
+ * Schedule a wireless scan in the /device's/ thread.
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+schedule_scan (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ cancel_pending_scan (self);
|
|
+
|
|
+ self->priv->pending_scan = g_timeout_source_new (self->priv->scan_interval * 1000);
|
|
+ g_source_set_callback (self->priv->pending_scan,
|
|
+ nm_device_802_11_wireless_scan,
|
|
+ self,
|
|
+ nm_device_802_11_wireless_scan_done);
|
|
+ g_source_attach (self->priv->pending_scan, nm_device_get_main_context (NM_DEVICE (self)));
|
|
+ g_source_unref (self->priv->pending_scan);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+cancel_pending_scan (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ self->priv->scanning = FALSE;
|
|
+ if (self->priv->pending_scan)
|
|
+ {
|
|
+ g_source_destroy (self->priv->pending_scan);
|
|
+ self->priv->pending_scan = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * is_associated
|
|
+ *
|
|
+ * Figure out whether or not we're associated to an access point
|
|
+ */
|
|
+static gboolean
|
|
+is_associated (NMDevice80211Wireless *self)
|
|
+{
|
|
+ struct iwreq wrq;
|
|
+ NMSock * sk;
|
|
+ gboolean associated = FALSE;
|
|
+ NMData * app_data;
|
|
+ const char * iface;
|
|
+
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+ g_assert (app_data);
|
|
+
|
|
+ if ((sk = nm_dev_sock_open (NM_DEVICE (self), DEV_WIRELESS, __FUNCTION__, NULL)) == NULL)
|
|
+ return FALSE;
|
|
+
|
|
+ /* Some cards, for example ipw2x00 cards, can short-circuit the MAC
|
|
+ * address check using this check on IWNAME. Its faster.
|
|
+ */
|
|
+ memset (&wrq, 0, sizeof (struct iwreq));
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET IWNAME.", iface);
|
|
+#endif
|
|
+ if (iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWNAME, &wrq) >= 0)
|
|
+ {
|
|
+ if (!strcmp (wrq.u.name, "unassociated"))
|
|
+ {
|
|
+ associated = FALSE;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!associated)
|
|
+ {
|
|
+ /*
|
|
+ * For all other wireless cards, the best indicator of a "link" at this time
|
|
+ * seems to be whether the card has a valid access point MAC address.
|
|
+ * Is there a better way? Some cards don't work too well with this check, ie
|
|
+ * Lucent WaveLAN.
|
|
+ */
|
|
+#ifdef IOCTL_DEBUG
|
|
+ nm_info ("%s: About to GET IWAP.", iface);
|
|
+#endif
|
|
+ if (iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWAP, &wrq) >= 0)
|
|
+ if (nm_ethernet_address_is_valid ((struct ether_addr *)(&(wrq.u.ap_addr.sa_data))))
|
|
+ associated = TRUE;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ nm_dev_sock_close (sk);
|
|
+
|
|
+ return associated;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+ap_need_key (NMDevice80211Wireless *self,
|
|
+ NMAccessPoint *ap,
|
|
+ gboolean *ask_user)
|
|
+{
|
|
+ const char * essid;
|
|
+ gboolean need_key = FALSE;
|
|
+ NMAPSecurity * security;
|
|
+ const char * iface;
|
|
+ int we_cipher;
|
|
+
|
|
+ g_return_val_if_fail (ap != NULL, FALSE);
|
|
+ g_return_val_if_fail (ask_user != NULL, FALSE);
|
|
+
|
|
+ essid = nm_ap_get_essid (ap);
|
|
+ security = nm_ap_get_security (ap);
|
|
+ g_assert (security);
|
|
+ we_cipher = nm_ap_security_get_we_cipher (security);
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+
|
|
+ if (!nm_ap_get_encrypted (ap))
|
|
+ {
|
|
+ nm_info ("Activation (%s/wireless): access point '%s' is unencrypted, no key needed.",
|
|
+ iface, essid ? essid : "(null)");
|
|
+
|
|
+ /* If the user-specified security info doesn't overlap the
|
|
+ * scanned access point's info, create new info from the scanned
|
|
+ * characteristics of the access point. Can happen if the AP's
|
|
+ * settings were changed.
|
|
+ */
|
|
+ if (we_cipher != IW_AUTH_CIPHER_NONE)
|
|
+ nm_ap_set_security (ap, nm_ap_security_new_from_ap (ap));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if ( !nm_ap_security_get_key (security)
|
|
+ || (we_cipher == IW_AUTH_CIPHER_NONE))
|
|
+ {
|
|
+ nm_info ("Activation (%s/wireless): access point '%s' "
|
|
+ "is encrypted, but NO valid key exists. New key needed.",
|
|
+ iface, essid ? essid : "(null)");
|
|
+ need_key = TRUE;
|
|
+
|
|
+ /* If the user-specified security info doesn't overlap the
|
|
+ * scanned access point's info, ask the user for a completely
|
|
+ * new key.
|
|
+ */
|
|
+ if (we_cipher == IW_AUTH_CIPHER_NONE)
|
|
+ *ask_user = TRUE;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_info ("Activation (%s/wireless): access point '%s' "
|
|
+ "is encrypted, and a key exists. No new key needed.",
|
|
+ iface, essid ? essid : "(null)");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return need_key;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * ap_is_auth_required
|
|
+ *
|
|
+ * Checks whether or not there is an encryption key present for
|
|
+ * this connection, and whether or not the authentication method
|
|
+ * in use will result in an authentication rejection if the key
|
|
+ * is wrong. For example, Ad Hoc mode networks don't have a
|
|
+ * master node and therefore nothing exists to reject the station.
|
|
+ * Similarly, Open System WEP access points don't reject a station
|
|
+ * when the key is wrong. Shared Key WEP access points will.
|
|
+ *
|
|
+ * Theory of operation here is that if:
|
|
+ * (a) the NMAPSecurity object specifies that authentication is
|
|
+ * required, and the AP rejects our authentication attempt during
|
|
+ * connection (which shows up as a wpa_supplicant disconnection
|
|
+ * event); or
|
|
+ * (b) the NMAPSecurity object specifies that no authentiation is
|
|
+ * required, and either DHCP times out or wpa_supplicant times out;
|
|
+ *
|
|
+ * then we need a new key from the user because our currenty key
|
|
+ * and/or authentication method is likely wrong.
|
|
+ *
|
|
+ */
|
|
+static gboolean
|
|
+ap_is_auth_required (NMAccessPoint *ap, gboolean *has_key)
|
|
+{
|
|
+ NMAPSecurity *security;
|
|
+ int we_cipher;
|
|
+ gboolean auth_required = FALSE;
|
|
+
|
|
+ g_return_val_if_fail (ap != NULL, FALSE);
|
|
+ g_return_val_if_fail (has_key != NULL, FALSE);
|
|
+
|
|
+ *has_key = FALSE;
|
|
+
|
|
+ /* Ad Hoc mode doesn't have any master station to validate
|
|
+ * security credentials, so no auth can possibly be required.
|
|
+ */
|
|
+ if (nm_ap_get_mode(ap) == IW_MODE_ADHOC)
|
|
+ return FALSE;
|
|
+
|
|
+ /* No encryption obviously means no possiblity of auth
|
|
+ * rejection due to a wrong encryption key.
|
|
+ */
|
|
+ security = nm_ap_get_security (ap);
|
|
+ we_cipher = nm_ap_security_get_we_cipher (security);
|
|
+ if (we_cipher == IW_AUTH_CIPHER_NONE)
|
|
+ return FALSE;
|
|
+
|
|
+ auth_required = nm_ap_security_get_authentication_required (security);
|
|
+ *has_key = TRUE;
|
|
+
|
|
+ return auth_required;
|
|
+}
|
|
+
|
|
+
|
|
+/****************************************************************************/
|
|
+/* WPA Supplicant control stuff
|
|
+ *
|
|
+ * Originally from:
|
|
+ *
|
|
+ * wpa_supplicant wrapper
|
|
+ *
|
|
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation version 2 of the License.
|
|
+ */
|
|
+
|
|
+#define WPA_SUPPLICANT_GLOBAL_SOCKET LOCALSTATEDIR"/run/wpa_supplicant-global"
|
|
+#define WPA_SUPPLICANT_CONTROL_SOCKET LOCALSTATEDIR"/run/wpa_supplicant"
|
|
+#define WPA_SUPPLICANT_NUM_RETRIES 20
|
|
+#define WPA_SUPPLICANT_RETRY_TIME_US 100*1000
|
|
+
|
|
+
|
|
+static void
|
|
+remove_link_timeout (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (self->priv->link_timeout != NULL)
|
|
+ {
|
|
+ g_source_destroy (self->priv->link_timeout);
|
|
+ self->priv->link_timeout = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+supplicant_remove_timeout (NMDevice80211Wireless *self)
|
|
+{
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ /* Remove any pending timeouts on the request */
|
|
+ if (self->priv->supplicant.timeout != NULL)
|
|
+ {
|
|
+ g_source_destroy (self->priv->supplicant.timeout);
|
|
+ self->priv->supplicant.timeout = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static char *
|
|
+supplicant_get_device_socket_path (NMDevice80211Wireless *self)
|
|
+{
|
|
+ const char *iface;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, NULL);
|
|
+
|
|
+ iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ return g_strdup_printf (WPA_SUPPLICANT_CONTROL_SOCKET "/%s", iface);
|
|
+}
|
|
+
|
|
+static void
|
|
+supplicant_cleanup (NMDevice80211Wireless *self)
|
|
+{
|
|
+ char * sock_path;
|
|
+
|
|
+ g_return_if_fail (self != NULL);
|
|
+
|
|
+ if (self->priv->supplicant.pid > 0)
|
|
+ {
|
|
+ kill (self->priv->supplicant.pid, SIGTERM);
|
|
+ self->priv->supplicant.pid = -1;
|
|
+ }
|
|
+ if (self->priv->supplicant.watch)
|
|
+ {
|
|
+ g_source_destroy (self->priv->supplicant.watch);
|
|
+ self->priv->supplicant.watch = NULL;
|
|
+ }
|
|
+ if (self->priv->supplicant.status)
|
|
+ {
|
|
+ g_source_destroy (self->priv->supplicant.status);
|
|
+ self->priv->supplicant.status = NULL;
|
|
+ }
|
|
+ if (self->priv->supplicant.ctrl)
|
|
+ {
|
|
+ wpa_ctrl_close (self->priv->supplicant.ctrl);
|
|
+ self->priv->supplicant.ctrl = NULL;
|
|
+ }
|
|
+ if (self->priv->supplicant.stdout)
|
|
+ {
|
|
+ g_source_destroy (self->priv->supplicant.stdout);
|
|
+ self->priv->supplicant.stdout = NULL;
|
|
+ }
|
|
+
|
|
+ supplicant_remove_timeout (self);
|
|
+ remove_link_timeout (self);
|
|
+
|
|
+ /* HACK: should be fixed in wpa_supplicant. Will likely
|
|
+ * require accomodations for selinux.
|
|
+ */
|
|
+ unlink (WPA_SUPPLICANT_GLOBAL_SOCKET);
|
|
+ sock_path = supplicant_get_device_socket_path (self);
|
|
+ unlink (sock_path);
|
|
+ g_free (sock_path);
|
|
+}
|
|
+
|
|
+static void
|
|
+supplicant_watch_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->supplicant.watch = NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+supplicant_watch_cb (GPid pid,
|
|
+ gint status,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ NMDevice * dev = NM_DEVICE (user_data);
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ g_assert (self);
|
|
+
|
|
+ if (WIFEXITED (status))
|
|
+ nm_warning ("wpa_supplicant exited with error code %d", WEXITSTATUS (status));
|
|
+ else if (WIFSTOPPED (status))
|
|
+ nm_warning ("wpa_supplicant stopped unexpectedly with signal %d", WSTOPSIG (status));
|
|
+ else if (WIFSIGNALED (status))
|
|
+ nm_warning ("wpa_supplicant died with signal %d", WTERMSIG (status));
|
|
+ else
|
|
+ nm_warning ("wpa_supplicant died from an unknown cause");
|
|
+
|
|
+ supplicant_cleanup (self);
|
|
+
|
|
+ nm_device_set_active_link (dev, FALSE);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+link_timeout_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->link_timeout = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * link_timeout_cb
|
|
+ *
|
|
+ * Called when the link to the access point has been down for a specified
|
|
+ * period of time.
|
|
+ */
|
|
+static gboolean
|
|
+link_timeout_cb (gpointer user_data)
|
|
+{
|
|
+ NMDevice * dev = NM_DEVICE (user_data);
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+ NMActRequest * req = nm_device_get_act_request (dev);
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ NMData * data = nm_device_get_app_data (dev);
|
|
+ gboolean has_key;
|
|
+
|
|
+ g_assert (dev);
|
|
+
|
|
+ /* Disconnect event during initial authentication and credentials
|
|
+ * ARE checked - we are likely to have wrong key. Ask the user for
|
|
+ * another one.
|
|
+ */
|
|
+ if ( (nm_act_request_get_stage (req) == NM_ACT_STAGE_DEVICE_CONFIG)
|
|
+ && (ap_is_auth_required (ap, &has_key) && has_key))
|
|
+ {
|
|
+ /* Association/authentication failed, we must have bad encryption key */
|
|
+ nm_info ("Activation (%s/wireless): disconnected during association,"
|
|
+ " asking for new key.", nm_device_get_iface (dev));
|
|
+ supplicant_remove_timeout(self);
|
|
+ nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_info ("%s: link timed out.", nm_device_get_iface (dev));
|
|
+ nm_device_set_active_link (dev, FALSE);
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+supplicant_status_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->supplicant.status = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+#define MESSAGE_LEN 2048
|
|
+
|
|
+static gboolean
|
|
+supplicant_status_cb (GIOChannel *source,
|
|
+ GIOCondition condition,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ NMDevice * dev = NM_DEVICE (user_data);
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+ char * message;
|
|
+ size_t len;
|
|
+ struct wpa_ctrl * ctrl;
|
|
+ NMActRequest * req;
|
|
+
|
|
+ g_assert (self);
|
|
+
|
|
+ /* Do nothing if we're supposed to be canceling activation.
|
|
+ * We'll get cleaned up by the cancellation handlers later.
|
|
+ */
|
|
+ if (nm_device_activation_should_cancel (dev))
|
|
+ return TRUE;
|
|
+
|
|
+ ctrl = self->priv->supplicant.ctrl;
|
|
+ g_return_val_if_fail (ctrl != NULL, FALSE);
|
|
+
|
|
+ req = nm_device_get_act_request (NM_DEVICE (self));
|
|
+
|
|
+ message = g_malloc (MESSAGE_LEN);
|
|
+ len = MESSAGE_LEN;
|
|
+ wpa_ctrl_recv (ctrl, message, &len);
|
|
+ message[len] = '\0';
|
|
+
|
|
+ if (strstr (message, WPA_EVENT_CONNECTED) != NULL)
|
|
+ {
|
|
+ remove_link_timeout (self);
|
|
+ nm_device_set_active_link (dev, TRUE);
|
|
+
|
|
+ /* If this is the initial association during device activation,
|
|
+ * schedule the next activation stage.
|
|
+ */
|
|
+ if (req && (nm_act_request_get_stage (req) == NM_ACT_STAGE_DEVICE_CONFIG))
|
|
+ {
|
|
+ NMAccessPoint *ap = nm_act_request_get_ap (req);
|
|
+
|
|
+ nm_info ("Activation (%s/wireless) Stage 2 of 5 (Device Configure) "
|
|
+ "successful. Connected to access point '%s'.",
|
|
+ nm_device_get_iface (NM_DEVICE (self)),
|
|
+ nm_ap_get_essid (ap) ? nm_ap_get_essid (ap) : "(none)");
|
|
+ supplicant_remove_timeout (self);
|
|
+ nm_device_activate_schedule_stage3_ip_config_start (req);
|
|
+ }
|
|
+ }
|
|
+ else if (strstr (message, WPA_EVENT_DISCONNECTED) != NULL)
|
|
+ {
|
|
+ if (nm_device_is_activated (dev) || nm_device_is_activating (dev))
|
|
+ {
|
|
+ /* Start the link timeout so we allow some time for reauthentication */
|
|
+ if ((self->priv->link_timeout == NULL) && !self->priv->scanning)
|
|
+ {
|
|
+ GMainContext * context = nm_device_get_main_context (dev);
|
|
+ self->priv->link_timeout = g_timeout_source_new (8000);
|
|
+ g_source_set_callback (self->priv->link_timeout,
|
|
+ link_timeout_cb,
|
|
+ self,
|
|
+ link_timeout_done);
|
|
+ g_source_attach (self->priv->link_timeout, context);
|
|
+ g_source_unref (self->priv->link_timeout);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_device_set_active_link (dev, FALSE);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_free (message);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+
|
|
+#define NM_SUPPLICANT_TIMEOUT 20 /* how long we wait for wpa_supplicant to associate (in seconds) */
|
|
+
|
|
+static unsigned int
|
|
+get_supplicant_timeout (NMDevice80211Wireless *self)
|
|
+{
|
|
+ if (self->priv->num_freqs > 14)
|
|
+ return NM_SUPPLICANT_TIMEOUT * 2;
|
|
+ return NM_SUPPLICANT_TIMEOUT;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+supplicant_timeout_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->supplicant.timeout = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * supplicant_timeout_cb
|
|
+ *
|
|
+ * Called when the supplicant has been unable to connect to an access point
|
|
+ * within a specified period of time.
|
|
+ */
|
|
+static gboolean
|
|
+supplicant_timeout_cb (gpointer user_data)
|
|
+{
|
|
+ NMDevice * dev = NM_DEVICE (user_data);
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+ NMActRequest * req = nm_device_get_act_request (dev);
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ NMData * data = nm_device_get_app_data (dev);
|
|
+ gboolean has_key;
|
|
+
|
|
+ g_assert (self);
|
|
+
|
|
+ /* Timed out waiting for authentication success; if the security method
|
|
+ * in use does not require access point side authentication (Open System
|
|
+ * WEP, for example) then we are likely using the wrong authentication
|
|
+ * algorithm or key. Request new one from the user.
|
|
+ */
|
|
+ if (!ap_is_auth_required (ap, &has_key) && has_key)
|
|
+ {
|
|
+ /* Activation failed, we must have bad encryption key */
|
|
+ nm_info ("Activation (%s/wireless): association took too long (>%us), asking for new key.",
|
|
+ nm_device_get_iface (dev), get_supplicant_timeout (self));
|
|
+ nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ nm_info ("Activation (%s/wireless): association took too long (>%us), failing activation.",
|
|
+ nm_device_get_iface (dev), get_supplicant_timeout (self));
|
|
+ if (nm_device_is_activating (dev))
|
|
+ nm_policy_schedule_activation_failed (nm_device_get_act_request (dev));
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+supplicant_log_stdout_done (gpointer user_data)
|
|
+{
|
|
+ NMDevice80211Wireless *device = NM_DEVICE_802_11_WIRELESS (user_data);
|
|
+
|
|
+ device->priv->supplicant.stdout = NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * supplicant_log_stdout
|
|
+ *
|
|
+ * Read text from a GIOChannel that's hooked up to the stdout of
|
|
+ * wpa_supplicant, then write that text to NM's syslog service.
|
|
+ * Adapted from Gnome's bug-buddy.
|
|
+ *
|
|
+ */
|
|
+static gboolean
|
|
+supplicant_log_stdout (GIOChannel *ioc, GIOCondition condition, gpointer data)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (data);
|
|
+ gboolean retval = FALSE;
|
|
+ char *buf;
|
|
+ gsize len;
|
|
+ GIOStatus io_status;
|
|
+ GTimeVal start_time, cur_time;
|
|
+
|
|
+ #define LINE_SIZE 1024
|
|
+ buf = g_malloc0 (LINE_SIZE);
|
|
+ g_get_current_time (&start_time);
|
|
+ try_read:
|
|
+ io_status = g_io_channel_read_chars (ioc, buf, LINE_SIZE-1, &len, NULL);
|
|
+ switch (io_status)
|
|
+ {
|
|
+ case G_IO_STATUS_AGAIN:
|
|
+ g_usleep (G_USEC_PER_SEC / 60);
|
|
+ /* Only wait for data for 1/2 a second */
|
|
+ g_get_current_time (&cur_time);
|
|
+ /* Subtract 1/2 second from current time so we don't have
|
|
+ * to modify start_time.
|
|
+ */
|
|
+ g_time_val_add (&cur_time, -1 * (G_USEC_PER_SEC / 2));
|
|
+ /* Compare times. If cur_time is less, keep trying to read */
|
|
+ if ((cur_time.tv_sec < start_time.tv_sec)
|
|
+ || ((cur_time.tv_sec == start_time.tv_sec)
|
|
+ && (cur_time.tv_usec < start_time.tv_usec)))
|
|
+ goto try_read;
|
|
+ nm_warning ("Waited too long for wpa_supplicant output, some may be lost.");
|
|
+ break;
|
|
+ case G_IO_STATUS_ERROR:
|
|
+ nm_warning ("Error reading wpa_supplicant output.");
|
|
+ break;
|
|
+ case G_IO_STATUS_NORMAL:
|
|
+ retval = TRUE;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (len > 0)
|
|
+ {
|
|
+ char *end;
|
|
+ char *start;
|
|
+
|
|
+ /* Log each line separately; sometimes we get a couple lines at a time */
|
|
+ buf[LINE_SIZE-1] = '\0';
|
|
+ start = end = &buf[0];
|
|
+ while (*end != '\0')
|
|
+ {
|
|
+ if (*end == '\n')
|
|
+ {
|
|
+ *end = '\0';
|
|
+ nm_info ("wpa_supplicant(%d): %s", self->priv->supplicant.pid, start);
|
|
+ start = end + 1;
|
|
+ }
|
|
+ end++;
|
|
+ }
|
|
+ }
|
|
+ g_free (buf);
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * supplicant_child_setup
|
|
+ *
|
|
+ * Set the process group ID of the newly forked process
|
|
+ *
|
|
+ */
|
|
+static void
|
|
+supplicant_child_setup (gpointer user_data G_GNUC_UNUSED)
|
|
+{
|
|
+ /* We are in the child process at this point */
|
|
+ pid_t pid = getpid ();
|
|
+ setpgid (pid, pid);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+supplicant_exec (NMDevice80211Wireless *self)
|
|
+{
|
|
+ gboolean success = FALSE;
|
|
+ char * argv[4];
|
|
+ GError * error = NULL;
|
|
+ GPid pid = -1;
|
|
+ int sup_stdout;
|
|
+
|
|
+ argv[0] = WPA_SUPPLICANT_BIN;
|
|
+ argv[1] = "-g";
|
|
+ argv[2] = WPA_SUPPLICANT_GLOBAL_SOCKET;
|
|
+ argv[3] = NULL;
|
|
+
|
|
+ success = g_spawn_async_with_pipes ("/", argv, NULL, 0,
|
|
+ &supplicant_child_setup, NULL, &pid, NULL, &sup_stdout,
|
|
+ NULL, &error);
|
|
+ if (!success)
|
|
+ {
|
|
+ if (error)
|
|
+ {
|
|
+ nm_warning ("Couldn't start wpa_supplicant. Error: (%d) %s",
|
|
+ error->code, error->message);
|
|
+ g_error_free (error);
|
|
+ }
|
|
+ else
|
|
+ nm_warning ("Couldn't start wpa_supplicant due to an unknown error.");
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ GIOChannel * channel;
|
|
+ const char * charset = NULL;
|
|
+
|
|
+ /* Monitor output from supplicant and redirect to syslog */
|
|
+ channel = g_io_channel_unix_new (sup_stdout);
|
|
+ g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
|
|
+ g_get_charset (&charset);
|
|
+ g_io_channel_set_encoding (channel, charset, NULL);
|
|
+ self->priv->supplicant.stdout = g_io_create_watch (channel, G_IO_IN | G_IO_ERR);
|
|
+ g_io_channel_unref (channel);
|
|
+ g_source_set_priority (self->priv->supplicant.stdout, G_PRIORITY_LOW);
|
|
+ g_source_set_callback (self->priv->supplicant.stdout,
|
|
+ (GSourceFunc) supplicant_log_stdout,
|
|
+ self,
|
|
+ supplicant_log_stdout_done);
|
|
+ g_source_attach (self->priv->supplicant.stdout, nm_device_get_main_context (NM_DEVICE (self)));
|
|
+ g_source_unref (self->priv->supplicant.stdout);
|
|
+
|
|
+ /* Monitor the child process so we know when it stops */
|
|
+ self->priv->supplicant.pid = pid;
|
|
+ if (self->priv->supplicant.watch)
|
|
+ g_source_destroy (self->priv->supplicant.watch);
|
|
+ self->priv->supplicant.watch = g_child_watch_source_new (pid);
|
|
+ g_source_set_callback (self->priv->supplicant.watch,
|
|
+ (GSourceFunc) supplicant_watch_cb,
|
|
+ self,
|
|
+ supplicant_watch_done);
|
|
+ g_source_attach (self->priv->supplicant.watch, nm_device_get_main_context (NM_DEVICE (self)));
|
|
+ g_source_unref (self->priv->supplicant.watch);
|
|
+ }
|
|
+
|
|
+ return success;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+supplicant_interface_init (NMDevice80211Wireless *self)
|
|
+{
|
|
+ struct wpa_ctrl * ctrl = NULL;
|
|
+ char * socket_path;
|
|
+ const char * iface = nm_device_get_iface (NM_DEVICE (self));
|
|
+ gboolean success = FALSE;
|
|
+ int tries = 0;
|
|
+
|
|
+ /* Try to open wpa_supplicant's global control socket */
|
|
+ for (tries = 0; tries < WPA_SUPPLICANT_NUM_RETRIES && !ctrl; tries++)
|
|
+ {
|
|
+ ctrl = wpa_ctrl_open (WPA_SUPPLICANT_GLOBAL_SOCKET, NM_RUN_DIR);
|
|
+ g_usleep (WPA_SUPPLICANT_RETRY_TIME_US);
|
|
+ }
|
|
+
|
|
+ if (!ctrl)
|
|
+ {
|
|
+ nm_info ("Error opening supplicant global control interface.");
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ /* wpa_cli -g/var/run/wpa_supplicant-global interface_add eth1 "" wext /var/run/wpa_supplicant */
|
|
+ if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL,
|
|
+ "INTERFACE_ADD %s\t\twext\t" WPA_SUPPLICANT_CONTROL_SOCKET "\t", iface))
|
|
+ goto exit;
|
|
+ wpa_ctrl_close (ctrl);
|
|
+
|
|
+ /* Get a control socket to wpa_supplicant for this interface.
|
|
+ * Try a couple times to work around naive socket naming
|
|
+ * in wpa_ctrl that sometimes collides with stale ones.
|
|
+ */
|
|
+ socket_path = supplicant_get_device_socket_path (self);
|
|
+ while (!self->priv->supplicant.ctrl && (tries++ < 10))
|
|
+ self->priv->supplicant.ctrl = wpa_ctrl_open (socket_path, NM_RUN_DIR);
|
|
+ g_free (socket_path);
|
|
+ if (!self->priv->supplicant.ctrl)
|
|
+ {
|
|
+ nm_info ("Error opening control interface to supplicant.");
|
|
+ goto exit;
|
|
+ }
|
|
+ success = TRUE;
|
|
+
|
|
+exit:
|
|
+ return success;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+supplicant_send_network_config (NMDevice80211Wireless *self,
|
|
+ NMActRequest *req)
|
|
+{
|
|
+ NMAccessPoint * ap = NULL;
|
|
+ gboolean success = FALSE;
|
|
+ char * response = NULL;
|
|
+ int nwid;
|
|
+ const char * essid;
|
|
+ struct wpa_ctrl * ctrl;
|
|
+ gboolean is_adhoc;
|
|
+ char * hex_essid = NULL;
|
|
+ const char * ap_scan = "AP_SCAN 1";
|
|
+ guint32 caps;
|
|
+ gboolean supports_wpa;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, FALSE);
|
|
+ g_return_val_if_fail (req != NULL, FALSE);
|
|
+
|
|
+ ap = nm_act_request_get_ap (req);
|
|
+ g_assert (ap);
|
|
+
|
|
+ ctrl = self->priv->supplicant.ctrl;
|
|
+ g_assert (ctrl);
|
|
+
|
|
+ /* Assume that drivers that don't support WPA pretty much suck,
|
|
+ * and can't handle NM scanning along with wpa_supplicant. Which
|
|
+ * is the case for most of them, airo in particular.
|
|
+ */
|
|
+ caps = nm_device_get_type_capabilities (NM_DEVICE (self));
|
|
+ supports_wpa = (caps & NM_802_11_CAP_PROTO_WPA)
|
|
+ || (caps & NM_802_11_CAP_PROTO_WPA2);
|
|
+
|
|
+ /* Use "AP_SCAN 2" if:
|
|
+ * - The wireless network is non-broadcast or Ad-Hoc
|
|
+ * - The wireless driver does not support WPA (stupid drivers...)
|
|
+ */
|
|
+ is_adhoc = (nm_ap_get_mode(ap) == IW_MODE_ADHOC);
|
|
+ if (!nm_ap_get_broadcast (ap) || is_adhoc || !supports_wpa)
|
|
+ ap_scan = "AP_SCAN 2";
|
|
+
|
|
+ /* Tell wpa_supplicant that we'll do the scanning */
|
|
+ if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL, ap_scan))
|
|
+ goto out;
|
|
+
|
|
+ /* Standard network setup info */
|
|
+ if (!(response = nm_utils_supplicant_request (ctrl, "ADD_NETWORK")))
|
|
+ {
|
|
+ nm_warning ("Supplicant error for ADD_NETWORK.\n");
|
|
+ goto out;
|
|
+ }
|
|
+ if (sscanf (response, "%i\n", &nwid) != 1)
|
|
+ {
|
|
+ nm_warning ("Supplicant error for ADD_NETWORK. Response: '%s'\n", response);
|
|
+ g_free (response);
|
|
+ goto out;
|
|
+ }
|
|
+ g_free (response);
|
|
+
|
|
+ if (nm_device_activation_should_cancel (NM_DEVICE (self)))
|
|
+ goto out;
|
|
+
|
|
+ essid = nm_ap_get_orig_essid (ap);
|
|
+ hex_essid = cipher_bin2hexstr (essid, strlen (essid), -1);
|
|
+ if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL,
|
|
+ "SET_NETWORK %i ssid %s", nwid, hex_essid))
|
|
+ goto out;
|
|
+
|
|
+ /* For non-broadcast networks, we need to set "scan_ssid 1" to scan with probe request frames.
|
|
+ * However, don't try to probe Ad-Hoc networks.
|
|
+ */
|
|
+ if (!nm_ap_get_broadcast (ap) && !is_adhoc)
|
|
+ {
|
|
+ if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL,
|
|
+ "SET_NETWORK %i scan_ssid 1", nwid))
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Ad-Hoc ? */
|
|
+ if (is_adhoc)
|
|
+ {
|
|
+ if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL,
|
|
+ "SET_NETWORK %i mode 1", nwid))
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (nm_device_activation_should_cancel (NM_DEVICE (self)))
|
|
+ goto out;
|
|
+
|
|
+ if (!nm_ap_security_write_supplicant_config (nm_ap_get_security (ap), ctrl, nwid, is_adhoc))
|
|
+ goto out;
|
|
+
|
|
+ if (nm_device_activation_should_cancel (NM_DEVICE (self)))
|
|
+ goto out;
|
|
+
|
|
+ if (!nm_utils_supplicant_request_with_check (ctrl, "OK", __func__, NULL,
|
|
+ "ENABLE_NETWORK %i", nwid, essid))
|
|
+ goto out;
|
|
+
|
|
+ success = TRUE;
|
|
+out:
|
|
+ g_free (hex_essid);
|
|
+
|
|
+ return success;
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+supplicant_monitor_start (NMDevice80211Wireless *self)
|
|
+{
|
|
+ gboolean success = FALSE;
|
|
+ int fd = -1;
|
|
+ GIOChannel * channel;
|
|
+ GMainContext * context;
|
|
+
|
|
+ g_return_val_if_fail (self != NULL, FALSE);
|
|
+
|
|
+ /* register network event monitor */
|
|
+ if (wpa_ctrl_attach (self->priv->supplicant.ctrl) != 0)
|
|
+ goto out;
|
|
+
|
|
+ if ((fd = wpa_ctrl_get_fd (self->priv->supplicant.ctrl)) < 0)
|
|
+ goto out;
|
|
+
|
|
+ context = nm_device_get_main_context (NM_DEVICE (self));
|
|
+ channel = g_io_channel_unix_new (fd);
|
|
+ self->priv->supplicant.status = g_io_create_watch (channel, G_IO_IN);
|
|
+ g_io_channel_unref (channel);
|
|
+ g_source_set_callback (self->priv->supplicant.status,
|
|
+ (GSourceFunc) supplicant_status_cb,
|
|
+ self,
|
|
+ supplicant_status_done);
|
|
+ g_source_attach (self->priv->supplicant.status, context);
|
|
+ g_source_unref (self->priv->supplicant.status);
|
|
+
|
|
+ /* Set up a timeout on the association to kill it after get_supplicant_time() seconds */
|
|
+ self->priv->supplicant.timeout = g_timeout_source_new (get_supplicant_timeout (self) * 1000);
|
|
+ g_source_set_callback (self->priv->supplicant.timeout,
|
|
+ supplicant_timeout_cb,
|
|
+ self,
|
|
+ supplicant_timeout_done);
|
|
+ g_source_attach (self->priv->supplicant.timeout, context);
|
|
+ g_source_unref (self->priv->supplicant.timeout);
|
|
+
|
|
+ success = TRUE;
|
|
+
|
|
+out:
|
|
+ return success;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+/****************************************************************************/
|
|
+
|
|
+static NMActStageReturn
|
|
+real_act_stage2_config (NMDevice *dev,
|
|
+ NMActRequest *req)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
+ NMData * data = nm_act_request_get_data (req);
|
|
+ const char * iface;
|
|
+ gboolean ask_user = FALSE;
|
|
+
|
|
+ g_assert (ap);
|
|
+
|
|
+ supplicant_cleanup (self);
|
|
+
|
|
+ /* If we need an encryption key, get one */
|
|
+ if (ap_need_key (self, ap, &ask_user))
|
|
+ {
|
|
+ nm_dbus_get_user_key_for_network (data->dbus_connection, req, ask_user);
|
|
+ return NM_ACT_STAGE_RETURN_POSTPONE;
|
|
+ }
|
|
+
|
|
+ iface = nm_device_get_iface (dev);
|
|
+ if (!supplicant_exec (self))
|
|
+ {
|
|
+ nm_warning ("Activation (%s/wireless): couldn't start the supplicant.",
|
|
+ iface);
|
|
+ goto out;
|
|
+ }
|
|
+ if (!supplicant_interface_init (self))
|
|
+ {
|
|
+ nm_warning ("Activation (%s/wireless): couldn't connect to the supplicant.",
|
|
+ iface);
|
|
+ goto out;
|
|
+ }
|
|
+ if (!supplicant_send_network_config (self, req))
|
|
+ {
|
|
+ nm_warning ("Activation (%s/wireless): couldn't send wireless configuration"
|
|
+ " to the supplicant.", iface);
|
|
+ goto out;
|
|
+ }
|
|
+ if (!supplicant_monitor_start (self))
|
|
+ {
|
|
+ nm_warning ("Activation (%s/wireless): couldn't monitor the supplicant.",
|
|
+ iface);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* We'll get stage3 started when the supplicant connects */
|
|
+ ret = NM_ACT_STAGE_RETURN_POSTPONE;
|
|
+
|
|
+out:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static NMActStageReturn
|
|
+real_act_stage3_ip_config_start (NMDevice *dev,
|
|
+ NMActRequest *req)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
+
|
|
+ g_assert (ap);
|
|
+
|
|
+ /* User-created access points (ie, Ad-Hoc networks) don't do DHCP,
|
|
+ * everything else does.
|
|
+ */
|
|
+ if (!nm_ap_get_user_created (ap))
|
|
+ {
|
|
+ NMDevice80211WirelessClass * klass;
|
|
+ NMDeviceClass * parent_class;
|
|
+
|
|
+ /* Chain up to parent */
|
|
+ klass = NM_DEVICE_802_11_WIRELESS_GET_CLASS (self);
|
|
+ parent_class = NM_DEVICE_CLASS (g_type_class_peek_parent (klass));
|
|
+ ret = parent_class->act_stage3_ip_config_start (dev, req);
|
|
+ }
|
|
+ else
|
|
+ ret = NM_ACT_STAGE_RETURN_SUCCESS;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static NMActStageReturn
|
|
+real_act_stage4_get_ip4_config (NMDevice *dev,
|
|
+ NMActRequest *req,
|
|
+ NMIP4Config **config)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
+ NMIP4Config * real_config = NULL;
|
|
+
|
|
+ g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
|
+ g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
|
+
|
|
+ g_assert (ap);
|
|
+ if (nm_ap_get_user_created (ap))
|
|
+ {
|
|
+ real_config = nm_device_new_ip4_autoip_config (NM_DEVICE (self));
|
|
+ ret = NM_ACT_STAGE_RETURN_SUCCESS;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ NMDevice80211WirelessClass * klass;
|
|
+ NMDeviceClass * parent_class;
|
|
+
|
|
+ /* Chain up to parent */
|
|
+ klass = NM_DEVICE_802_11_WIRELESS_GET_CLASS (self);
|
|
+ parent_class = NM_DEVICE_CLASS (g_type_class_peek_parent (klass));
|
|
+ ret = parent_class->act_stage4_get_ip4_config (dev, req, &real_config);
|
|
+ }
|
|
+ *config = real_config;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static NMActStageReturn
|
|
+real_act_stage4_ip_config_timeout (NMDevice *dev,
|
|
+ NMActRequest *req,
|
|
+ NMIP4Config **config)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
+ NMIP4Config * real_config = NULL;
|
|
+ NMAPSecurity * security;
|
|
+ NMData * data;
|
|
+ gboolean has_key;
|
|
+
|
|
+ g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
|
+ g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
|
+
|
|
+ g_assert (ap);
|
|
+
|
|
+ data = nm_device_get_app_data (dev);
|
|
+ g_assert (data);
|
|
+
|
|
+ security = nm_ap_get_security (ap);
|
|
+ g_assert (security);
|
|
+
|
|
+ /* If the security credentials' validity was not checked by any
|
|
+ * peer during authentication process, and DHCP times out, then
|
|
+ * the encryption key is likely wrong. Ask the user for a new one.
|
|
+ */
|
|
+ if (!ap_is_auth_required (ap, &has_key) && has_key)
|
|
+ {
|
|
+ /* Activation failed, we must have bad encryption key */
|
|
+ nm_debug ("Activation (%s/wireless): could not get IP configuration info for '%s', asking for new key.",
|
|
+ nm_device_get_iface (dev), nm_ap_get_essid (ap) ? nm_ap_get_essid (ap) : "(none)");
|
|
+ nm_dbus_get_user_key_for_network (data->dbus_connection, req, TRUE);
|
|
+ ret = NM_ACT_STAGE_RETURN_POSTPONE;
|
|
+ }
|
|
+ else if (nm_ap_get_mode (ap) == IW_MODE_ADHOC)
|
|
+ {
|
|
+ NMDevice80211WirelessClass * klass;
|
|
+ NMDeviceClass * parent_class;
|
|
+
|
|
+ /* For Ad-Hoc networks, chain up to parent to get a Zeroconf IP */
|
|
+ klass = NM_DEVICE_802_11_WIRELESS_GET_CLASS (self);
|
|
+ parent_class = NM_DEVICE_CLASS (g_type_class_peek_parent (klass));
|
|
+ ret = parent_class->act_stage4_ip_config_timeout (dev, req, &real_config);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Non-encrypted network and IP configure failed. Alert the user. */
|
|
+ ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
+ }
|
|
+
|
|
+ *config = real_config;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+real_activation_success_handler (NMDevice *dev,
|
|
+ NMActRequest *req)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ struct ether_addr addr;
|
|
+ NMAccessPoint * ap = nm_act_request_get_ap (req);
|
|
+ gboolean automatic;
|
|
+ NMData * app_data;
|
|
+
|
|
+ app_data = nm_act_request_get_data (req);
|
|
+ g_assert (app_data);
|
|
+
|
|
+ /* Cache details in the info-daemon since the connect was successful */
|
|
+ automatic = !nm_act_request_get_user_requested (req);
|
|
+
|
|
+ /* If it's a user-created ad-hoc network, add it to the device's scan list */
|
|
+ if (!automatic && (nm_ap_get_mode (ap) == IW_MODE_ADHOC) && nm_ap_get_user_created (ap))
|
|
+ {
|
|
+ NMAccessPointList *ap_list = nm_device_802_11_wireless_ap_list_get (self);
|
|
+ if (!nm_ap_list_get_ap_by_essid (ap_list, nm_ap_get_essid (ap)))
|
|
+ nm_ap_list_append_ap (ap_list, ap);
|
|
+ }
|
|
+
|
|
+ nm_device_802_11_wireless_get_bssid (self, &addr);
|
|
+ if (!nm_ap_get_address (ap) || !nm_ethernet_address_is_valid (nm_ap_get_address (ap)))
|
|
+ nm_ap_set_address (ap, &addr);
|
|
+
|
|
+ nm_dbus_update_network_info (app_data->dbus_connection, ap, automatic);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+real_activation_failure_handler (NMDevice *dev,
|
|
+ NMActRequest *req)
|
|
+{
|
|
+ NMData * app_data;
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMAccessPoint * ap;
|
|
+
|
|
+ app_data = nm_act_request_get_data (req);
|
|
+ g_assert (app_data);
|
|
+
|
|
+ if ((ap = nm_act_request_get_ap (req)))
|
|
+ {
|
|
+ if (nm_ap_get_artificial (ap))
|
|
+ {
|
|
+ NMAccessPointList * dev_list;
|
|
+
|
|
+ /* Artificial APs are ones that don't show up in scans,
|
|
+ * but which the user explicitly attempted to connect to.
|
|
+ * However, if we fail on one of these, remove it from the
|
|
+ * list because we don't have any scan or capability info
|
|
+ * for it, and they are pretty much useless.
|
|
+ */
|
|
+ dev_list = nm_device_802_11_wireless_ap_list_get (self);
|
|
+ nm_ap_list_remove_ap (dev_list, ap);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Add the AP to the invalid list */
|
|
+ nm_ap_set_invalid (ap, TRUE);
|
|
+ nm_ap_list_append_ap (app_data->invalid_ap_list, ap);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nm_info ("Activation (%s) failed for access point (%s)", nm_device_get_iface (dev),
|
|
+ ap ? nm_ap_get_essid (ap) : "(none)");
|
|
+}
|
|
+
|
|
+static void
|
|
+real_activation_cancel_handler (NMDevice *dev,
|
|
+ NMActRequest *req)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+ NMDevice80211WirelessClass * klass;
|
|
+ NMDeviceClass * parent_class;
|
|
+
|
|
+ /* Chain up to parent first */
|
|
+ klass = NM_DEVICE_802_11_WIRELESS_GET_CLASS (self);
|
|
+ parent_class = NM_DEVICE_CLASS (g_type_class_peek_parent (klass));
|
|
+ parent_class->activation_cancel_handler (dev, req);
|
|
+
|
|
+ if (nm_act_request_get_stage (req) == NM_ACT_STAGE_NEED_USER_KEY)
|
|
+ {
|
|
+ NMData *data = nm_device_get_app_data (dev);
|
|
+ nm_dbus_cancel_get_user_key_for_network (data->dbus_connection, req);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static gboolean
|
|
+real_can_interrupt_activation (NMDevice *dev)
|
|
+{
|
|
+ NMActRequest * req;
|
|
+ gboolean interrupt = FALSE;
|
|
+
|
|
+ if ( (req = nm_device_get_act_request (dev))
|
|
+ && (nm_act_request_get_stage (req) == NM_ACT_STAGE_NEED_USER_KEY))
|
|
+ {
|
|
+ interrupt = TRUE;
|
|
+ }
|
|
+ return interrupt;
|
|
+}
|
|
+
|
|
+
|
|
+static guint32
|
|
+real_get_type_capabilities (NMDevice *dev)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev);
|
|
+
|
|
+ return self->priv->capabilities;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+nm_device_802_11_wireless_dispose (GObject *object)
|
|
+{
|
|
+ NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (object);
|
|
+ NMDevice80211WirelessClass * klass = NM_DEVICE_802_11_WIRELESS_GET_CLASS (object);
|
|
+ NMDeviceClass * parent_class;
|
|
+ NMData * data = nm_device_get_app_data (NM_DEVICE (self));
|
|
+
|
|
+ /* Make sure dispose does not run twice. */
|
|
+ if (self->priv->dispose_has_run)
|
|
+ return;
|
|
+
|
|
+ self->priv->dispose_has_run = TRUE;
|
|
+
|
|
+ g_free (self->priv->cur_essid);
|
|
+
|
|
+ /* Only do this part of the cleanup if the object is initialized */
|
|
+ if (self->priv->is_initialized)
|
|
+ {
|
|
+ nm_device_802_11_wireless_ap_list_clear (self);
|
|
+ if (self->priv->ap_list)
|
|
+ nm_ap_list_unref (self->priv->ap_list);
|
|
+ }
|
|
+
|
|
+ cancel_scan_results_timeout (self);
|
|
+
|
|
+ g_signal_handler_disconnect (G_OBJECT (data->netlink_monitor),
|
|
+ self->priv->wireless_event_id);
|
|
+
|
|
+ /* Chain up to the parent class */
|
|
+ parent_class = NM_DEVICE_CLASS (g_type_class_peek_parent (klass));
|
|
+ G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+nm_device_802_11_wireless_finalize (GObject *object)
|
|
+{
|
|
+ NMDevice80211WirelessClass * klass = NM_DEVICE_802_11_WIRELESS_GET_CLASS (object);
|
|
+ NMDeviceClass * parent_class;
|
|
+
|
|
+ /* Chain up to the parent class */
|
|
+ parent_class = NM_DEVICE_CLASS (g_type_class_peek_parent (klass));
|
|
+ G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+nm_device_802_11_wireless_class_init (NMDevice80211WirelessClass *klass)
|
|
+{
|
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
+ NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
|
|
+
|
|
+ object_class->dispose = nm_device_802_11_wireless_dispose;
|
|
+ object_class->finalize = nm_device_802_11_wireless_finalize;
|
|
+
|
|
+ parent_class->get_type_capabilities = real_get_type_capabilities;
|
|
+ parent_class->get_generic_capabilities = real_get_generic_capabilities;
|
|
+ parent_class->init = real_init;
|
|
+ parent_class->start = real_start;
|
|
+ parent_class->update_link = real_update_link;
|
|
+
|
|
+ parent_class->act_stage2_config = real_act_stage2_config;
|
|
+ parent_class->act_stage3_ip_config_start = real_act_stage3_ip_config_start;
|
|
+ parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
|
|
+ parent_class->act_stage4_ip_config_timeout = real_act_stage4_ip_config_timeout;
|
|
+ parent_class->deactivate = real_deactivate;
|
|
+ parent_class->deactivate_quickly = real_deactivate_quickly;
|
|
+ parent_class->can_interrupt_activation = real_can_interrupt_activation;
|
|
+
|
|
+ parent_class->activation_failure_handler = real_activation_failure_handler;
|
|
+ parent_class->activation_success_handler = real_activation_success_handler;
|
|
+ parent_class->activation_cancel_handler = real_activation_cancel_handler;
|
|
+
|
|
+ g_type_class_add_private (object_class, sizeof (NMDevice80211WirelessPrivate));
|
|
+}
|
|
+
|
|
+GType
|
|
+nm_device_802_11_wireless_get_type (void)
|
|
+{
|
|
+ static GType type = 0;
|
|
+ if (type == 0)
|
|
+ {
|
|
+ static const GTypeInfo info =
|
|
+ {
|
|
+ sizeof (NMDevice80211WirelessClass),
|
|
+ NULL, /* base_init */
|
|
+ NULL, /* base_finalize */
|
|
+ (GClassInitFunc) nm_device_802_11_wireless_class_init,
|
|
+ NULL, /* class_finalize */
|
|
+ NULL, /* class_data */
|
|
+ sizeof (NMDevice80211Wireless),
|
|
+ 0, /* n_preallocs */
|
|
+ (GInstanceInitFunc) nm_device_802_11_wireless_init,
|
|
+ NULL /* value_table */
|
|
+ };
|
|
+ type = g_type_register_static (NM_TYPE_DEVICE,
|
|
+ "NMDevice80211Wireless",
|
|
+ &info, 0);
|
|
+ }
|
|
+ return type;
|
|
+}
|
|
+
|
|
+
|
|
+/*****************************************/
|
|
+/* Start code ripped from wpa_supplicant */
|
|
+/*****************************************/
|
|
+/*
|
|
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+
|
|
+static int hex2num(char c)
|
|
+{
|
|
+ if (c >= '0' && c <= '9')
|
|
+ return c - '0';
|
|
+ if (c >= 'a' && c <= 'f')
|
|
+ return c - 'a' + 10;
|
|
+ if (c >= 'A' && c <= 'F')
|
|
+ return c - 'A' + 10;
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+
|
|
+static int hex2byte(const char *hex)
|
|
+{
|
|
+ int a, b;
|
|
+ a = hex2num(*hex++);
|
|
+ if (a < 0)
|
|
+ return -1;
|
|
+ b = hex2num(*hex++);
|
|
+ if (b < 0)
|
|
+ return -1;
|
|
+ return (a << 4) | b;
|
|
+}
|
|
+
|
|
+static int hexstr2bin(const char *hex, char *buf, size_t len)
|
|
+{
|
|
+ int i, a;
|
|
+ const char *ipos = hex;
|
|
+ char *opos = buf;
|
|
+
|
|
+ for (i = 0; i < len; i++) {
|
|
+ a = hex2byte(ipos);
|
|
+ if (a < 0)
|
|
+ return -1;
|
|
+ *opos++ = a;
|
|
+ ipos += 2;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+add_new_ap_to_device_list (NMDevice80211Wireless *dev,
|
|
+ NMAccessPoint *ap)
|
|
+{
|
|
+ GTimeVal cur_time;
|
|
+ NMAccessPointList * ap_list;
|
|
+
|
|
+ g_return_if_fail (dev != NULL);
|
|
+ g_return_if_fail (ap != NULL);
|
|
+
|
|
+ g_get_current_time (&cur_time);
|
|
+ nm_ap_set_last_seen (ap, &cur_time);
|
|
+
|
|
+ /* If the AP is not broadcasting its ESSID, try to fill it in here from our
|
|
+ * allowed list where we cache known MAC->ESSID associations.
|
|
+ */
|
|
+ if (!nm_ap_get_essid (ap))
|
|
+ {
|
|
+ NMData * app_data;
|
|
+
|
|
+ nm_ap_set_broadcast (ap, FALSE);
|
|
+ app_data = nm_device_get_app_data (NM_DEVICE (dev));
|
|
+ nm_ap_list_copy_one_essid_by_address (app_data, dev, ap, app_data->allowed_ap_list);
|
|
+ }
|
|
+
|
|
+ /* Add the AP to the device's AP list */
|
|
+ ap_list = nm_device_802_11_wireless_ap_list_get (dev);
|
|
+ nm_ap_list_merge_scanned_ap (dev, ap_list, ap);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+process_scan_results (NMDevice80211Wireless *dev,
|
|
+ const guint8 *res_buf,
|
|
+ guint32 res_buf_len)
|
|
+{
|
|
+ char *pos, *end, *custom, *genie, *gpos, *gend;
|
|
+ NMAccessPoint *ap = NULL;
|
|
+ size_t clen;
|
|
+ struct iw_param iwp;
|
|
+ int maxrate;
|
|
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
|
|
+
|
|
+ g_return_val_if_fail (dev != NULL, FALSE);
|
|
+ g_return_val_if_fail (res_buf != NULL, FALSE);
|
|
+ g_return_val_if_fail (res_buf_len > 0, FALSE);
|
|
+
|
|
+ pos = (char *) res_buf;
|
|
+ end = (char *) res_buf + res_buf_len;
|
|
+
|
|
+ while (pos + IW_EV_LCP_LEN <= end)
|
|
+ {
|
|
+ int ssid_len;
|
|
+
|
|
+ /* Event data may be unaligned, so make a local, aligned copy
|
|
+ * before processing. */
|
|
+ memcpy (&iwe_buf, pos, IW_EV_LCP_LEN);
|
|
+ if (iwe->len <= IW_EV_LCP_LEN)
|
|
+ break;
|
|
+
|
|
+ custom = pos + IW_EV_POINT_LEN;
|
|
+ if (dev->priv->we_version > 18 &&
|
|
+ (iwe->cmd == SIOCGIWESSID ||
|
|
+ iwe->cmd == SIOCGIWENCODE ||
|
|
+ iwe->cmd == IWEVGENIE ||
|
|
+ iwe->cmd == IWEVCUSTOM))
|
|
+ {
|
|
+ /* WE-19 removed the pointer from struct iw_point */
|
|
+ char *dpos = (char *) &iwe_buf.u.data.length;
|
|
+ int dlen = dpos - (char *) &iwe_buf;
|
|
+ memcpy (dpos, pos + IW_EV_LCP_LEN, sizeof (struct iw_event) - dlen);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ memcpy (&iwe_buf, pos, sizeof (struct iw_event));
|
|
+ custom += IW_EV_POINT_OFF;
|
|
+ }
|
|
+
|
|
+ switch (iwe->cmd)
|
|
+ {
|
|
+ case SIOCGIWAP:
|
|
+ /* New access point record */
|
|
+
|
|
+ /* Merge previous AP */
|
|
+ if (ap)
|
|
+ {
|
|
+ add_new_ap_to_device_list (dev, ap);
|
|
+ nm_ap_unref (ap);
|
|
+ ap = NULL;
|
|
+ }
|
|
+
|
|
+ /* New AP with some defaults */
|
|
+ ap = nm_ap_new ();
|
|
+ nm_ap_set_address (ap, (const struct ether_addr *)(iwe->u.ap_addr.sa_data));
|
|
+ break;
|
|
+ case SIOCGIWMODE:
|
|
+ switch (iwe->u.mode)
|
|
+ {
|
|
+ case IW_MODE_ADHOC:
|
|
+ nm_ap_set_mode (ap, IW_MODE_ADHOC);
|
|
+ break;
|
|
+ case IW_MODE_MASTER:
|
|
+ case IW_MODE_INFRA:
|
|
+ nm_ap_set_mode (ap, IW_MODE_INFRA);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ case SIOCGIWESSID:
|
|
+ ssid_len = iwe->u.essid.length;
|
|
+ if (custom + ssid_len > end)
|
|
+ break;
|
|
+ if (iwe->u.essid.flags && (ssid_len > 0) && (ssid_len <= IW_ESSID_MAX_SIZE))
|
|
+ {
|
|
+ gboolean set = TRUE;
|
|
+ char *essid = g_malloc (IW_ESSID_MAX_SIZE + 1);
|
|
+ memcpy (essid, custom, ssid_len);
|
|
+ essid[ssid_len] = '\0';
|
|
+ if (!strlen(essid))
|
|
+ set = FALSE;
|
|
+ else if ((strlen (essid) == 8) && (strcmp (essid, "<hidden>") == 0)) /* Stupid ipw drivers use <hidden> */
|
|
+ set = FALSE;
|
|
+ if (set)
|
|
+ nm_ap_set_essid (ap, essid);
|
|
+ g_free (essid);
|
|
+ }
|
|
+ break;
|
|
+ case SIOCGIWFREQ:
|
|
+ nm_ap_set_freq (ap, iw_freq2float(&(iwe->u.freq)));
|
|
+ break;
|
|
+ case IWEVQUAL:
|
|
+ nm_ap_set_strength (ap, wireless_qual_to_percent (&dev->parent,
|
|
+ &(iwe->u.qual),
|
|
+ (const iwqual *)(&dev->priv->max_qual),
|
|
+ (const iwqual *)(&dev->priv->avg_qual)));
|
|
+ break;
|
|
+ case SIOCGIWENCODE:
|
|
+ if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
|
|
+ {
|
|
+ /* Only add WEP capabilities if this AP doesn't have
|
|
+ * any encryption capabilities yet.
|
|
+ */
|
|
+ if (nm_ap_get_capabilities (ap) & NM_802_11_CAP_PROTO_NONE)
|
|
+ nm_ap_add_capabilities_for_wep (ap);
|
|
+ }
|
|
+ break;
|
|
+ case SIOCGIWRATE:
|
|
+ clen = iwe->len;
|
|
+ if (custom + clen > end)
|
|
+ break;
|
|
+ maxrate = 0;
|
|
+ while (((ssize_t) clen) >= sizeof(struct iw_param))
|
|
+ {
|
|
+ /* the payload may be unaligned, so we align it */
|
|
+ memcpy(&iwp, custom, sizeof (struct iw_param));
|
|
+ if (iwp.value > maxrate)
|
|
+ maxrate = iwp.value;
|
|
+ clen -= sizeof (struct iw_param);
|
|
+ custom += sizeof (struct iw_param);
|
|
+ }
|
|
+ nm_ap_set_rate (ap, maxrate);
|
|
+ break;
|
|
+ case IWEVGENIE:
|
|
+ gpos = genie = custom;
|
|
+ gend = genie + iwe->u.data.length;
|
|
+ if (gend > end)
|
|
+ {
|
|
+ nm_warning ("get_scan_results(): IWEVGENIE overflow.");
|
|
+ break;
|
|
+ }
|
|
+ while ((gpos + 1 < gend) && (gpos + 2 + (guint8) gpos[1] <= gend))
|
|
+ {
|
|
+ guint8 ie = gpos[0], ielen = gpos[1] + 2;
|
|
+ if (ielen > WPA_MAX_IE_LEN)
|
|
+ {
|
|
+ gpos += ielen;
|
|
+ continue;
|
|
+ }
|
|
+ switch (ie)
|
|
+ {
|
|
+ case WPA_GENERIC_INFO_ELEM:
|
|
+ if ((ielen < 2 + 4) || (memcmp (&gpos[2], "\x00\x50\xf2\x01", 4) != 0))
|
|
+ break;
|
|
+ nm_ap_add_capabilities_from_ie (ap, (const guint8 *)gpos, ielen);
|
|
+ break;
|
|
+ case WPA_RSN_INFO_ELEM:
|
|
+ nm_ap_add_capabilities_from_ie (ap, (const guint8 *)gpos, ielen);
|
|
+ break;
|
|
+ }
|
|
+ gpos += ielen;
|
|
+ }
|
|
+ break;
|
|
+ case IWEVCUSTOM:
|
|
+ clen = iwe->u.data.length;
|
|
+ if (custom + clen > end)
|
|
+ break;
|
|
+ if (clen > 7 && ((strncmp (custom, "wpa_ie=", 7) == 0) || (strncmp (custom, "rsn_ie=", 7) == 0)))
|
|
+ {
|
|
+ char *spos;
|
|
+ int bytes;
|
|
+ char *ie_buf;
|
|
+
|
|
+ spos = custom + 7;
|
|
+ bytes = custom + clen - spos;
|
|
+ if (bytes & 1)
|
|
+ break;
|
|
+ bytes /= 2;
|
|
+ if (bytes > WPA_MAX_IE_LEN)
|
|
+ {
|
|
+ nm_warning ("get_scan_results(): IE was too long (%d bytes).", bytes);
|
|
+ break;
|
|
+ }
|
|
+ ie_buf = g_malloc0 (bytes);
|
|
+ hexstr2bin (spos, ie_buf, bytes);
|
|
+ if (strncmp (custom, "wpa_ie=", 7) == 0)
|
|
+ nm_ap_add_capabilities_from_ie (ap, (const guint8 *)ie_buf, bytes);
|
|
+ else if (strncmp (custom, "rsn_ie=", 7) == 0)
|
|
+ nm_ap_add_capabilities_from_ie (ap, (const guint8 *)ie_buf, bytes);
|
|
+ g_free (ie_buf);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ pos += iwe->len;
|
|
+ }
|
|
+
|
|
+ if (ap)
|
|
+ {
|
|
+ add_new_ap_to_device_list (dev, ap);
|
|
+ nm_ap_unref (ap);
|
|
+ ap = NULL;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+/*****************************************/
|
|
+/* End code ripped from wpa_supplicant */
|
|
+/*****************************************/
|