From f0ecd6be34f8e7df6ff7d1414514d0b355062edd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 22 Oct 2007 03:15:05 +0000 Subject: [PATCH] 2007-10-21 Dan Williams * src/NetworkManagerAP.c src/NetworkManagerAP.h - Rename 'articifical' -> 'fake' since that's what they are until noticed in scans - (nm_ap_new_fake_from_connection): new function to create a 'fake' AP from the attributes in an NMConnection object - (security_compatible): better handle Dynamic WEP and LEAP; handle WPA Enterprise - (nm_ap_match_in_list): find a matching AP in a scan list * src/nm-device-802-11-wireless.c - (get_active_ap): add an 'ignore_ap' argument to ignore a specific AP when searching the scan list; match on frequency and mode too - (nm_device_802_11_wireless_get_frequency): implement - (merge_scanned_ap): replace duplicate matching logic with nm_ap_match_in_list() - (real_act_stage1_prepare): handle a NULL specific object; ie where the user is trying to connect to a hidden network that is not yet known from the scan list - (activation_success_handler): now that the card knows the AP's BSSID, there may already be a scanned AP in the scan list that is what we really wanted to connect to, but didn't know at the time. Use that instead of the 'fake' AP created at activation start and get rid of the 'fake' AP - (cull_scan_list): don't remove fake APs git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@2993 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- ChangeLog | 28 +++ src/NetworkManagerAP.c | 329 +++++++++++++++++++++++++++++--- src/NetworkManagerAP.h | 9 +- src/nm-device-802-11-wireless.c | 196 ++++++++++++------- 4 files changed, 471 insertions(+), 91 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7ae113efd8..22f2166ae8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2007-10-21 Dan Williams + + * src/NetworkManagerAP.c + src/NetworkManagerAP.h + - Rename 'articifical' -> 'fake' since that's what they are until + noticed in scans + - (nm_ap_new_fake_from_connection): new function to create a 'fake' AP + from the attributes in an NMConnection object + - (security_compatible): better handle Dynamic WEP and LEAP; handle + WPA Enterprise + - (nm_ap_match_in_list): find a matching AP in a scan list + + * src/nm-device-802-11-wireless.c + - (get_active_ap): add an 'ignore_ap' argument to ignore a specific + AP when searching the scan list; match on frequency and mode too + - (nm_device_802_11_wireless_get_frequency): implement + - (merge_scanned_ap): replace duplicate matching logic with + nm_ap_match_in_list() + - (real_act_stage1_prepare): handle a NULL specific object; ie where + the user is trying to connect to a hidden network that is not yet + known from the scan list + - (activation_success_handler): now that the card knows the AP's BSSID, + there may already be a scanned AP in the scan list that is what + we really wanted to connect to, but didn't know at the time. Use + that instead of the 'fake' AP created at activation start and get + rid of the 'fake' AP + - (cull_scan_list): don't remove fake APs + 2007-10-21 Dan Williams * src/nm-activation-request.h diff --git a/src/NetworkManagerAP.c b/src/NetworkManagerAP.c index 9870585b0e..de6df06d23 100644 --- a/src/NetworkManagerAP.c +++ b/src/NetworkManagerAP.c @@ -51,7 +51,7 @@ typedef struct /* Non-scanned attributes */ gboolean invalid; - gboolean artificial; /* Whether or not the AP is from a scan */ + gboolean fake; /* Whether or not the AP is from a scan */ gboolean broadcast; /* Whether or not the AP is broadcasting (hidden) */ gboolean user_created; /* Whether or not the AP was created * by the user with "Create network..." @@ -481,6 +481,164 @@ nm_ap_new_from_properties (GHashTable *properties) return ap; } +static gboolean +has_proto (NMSettingWirelessSecurity *sec, const char *proto) +{ + GSList *iter; + + for (iter = sec->proto; iter; iter = g_slist_next (iter)) + if (!strcmp (iter->data, proto)) + return TRUE; + return FALSE; +} + +static gboolean +has_proto_wpa (NMSettingWirelessSecurity *sec) +{ + return has_proto (sec, "wpa"); +} + +static gboolean +has_proto_rsn (NMSettingWirelessSecurity *sec) +{ + return has_proto (sec, "rsn"); +} + +static void +add_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec, gboolean group) +{ + GSList *iter; + GSList *ciphers = group ? sec->group : sec->pairwise; + + for (iter = ciphers; iter; iter = g_slist_next (iter)) { + guint32 flags = NM_802_11_AP_SEC_NONE; + guint32 orig_flags; + + if (!strcmp (iter->data, "wep40")) + flags |= group ? NM_802_11_AP_SEC_GROUP_WEP40 : NM_802_11_AP_SEC_PAIR_WEP40; + else if (!strcmp (iter->data, "wep104")) + flags |= group ? NM_802_11_AP_SEC_GROUP_WEP104 : NM_802_11_AP_SEC_PAIR_WEP104; + else if (!strcmp (iter->data, "tkip")) + flags |= group ? NM_802_11_AP_SEC_GROUP_TKIP : NM_802_11_AP_SEC_PAIR_TKIP; + else if (!strcmp (iter->data, "ccmp")) + flags |= group ? NM_802_11_AP_SEC_GROUP_CCMP : NM_802_11_AP_SEC_PAIR_CCMP; + + if (has_proto_wpa (sec)) { + orig_flags = nm_ap_get_wpa_flags (ap); + nm_ap_set_wpa_flags (ap, orig_flags | flags); + } + if (has_proto_rsn (sec)) { + orig_flags = nm_ap_get_rsn_flags (ap); + nm_ap_set_rsn_flags (ap, orig_flags | flags); + } + } +} + +NMAccessPoint * +nm_ap_new_fake_from_connection (NMConnection *connection) +{ + NMAccessPoint *ap; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + GByteArray *ssid; + guint32 len; + guint32 flags; + + g_return_val_if_fail (connection != NULL, NULL); + + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_SETTING_WIRELESS); + g_return_val_if_fail (s_wireless != NULL, NULL); + g_return_val_if_fail (s_wireless->ssid != NULL, NULL); + g_return_val_if_fail (s_wireless->ssid->len > 0, NULL); + + ap = nm_ap_new (); + + len = s_wireless->ssid->len; + ssid = g_byte_array_sized_new (len); + g_byte_array_append (ssid, (const guint8 *) s_wireless->ssid->data, len); + nm_ap_set_ssid (ap, ssid); + g_byte_array_free (ssid, TRUE); + + // FIXME: bssid too? + + if (s_wireless->mode) { + if (!strcmp (s_wireless->mode, "infrastructure")) + nm_ap_set_mode (ap, IW_MODE_INFRA); + else if (!strcmp (s_wireless->mode, "adhoc")) + nm_ap_set_mode (ap, IW_MODE_ADHOC); + else + goto error; + } else { + nm_ap_set_mode (ap, IW_MODE_INFRA); + } + + if (s_wireless->channel) { + guint32 freq = channel_to_freq (s_wireless->channel); + + if (freq == -1) + goto error; + + nm_ap_set_freq (ap, freq); + } + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_SETTING_WIRELESS_SECURITY); + if (!s_wireless_sec) + goto done; + + flags = nm_ap_get_flags (ap); + + /* Static WEP or no security */ + if (!strcmp (s_wireless_sec->key_mgmt, "none")) { + /* static wep? */ + if ( s_wireless_sec->wep_key0 + || s_wireless_sec->wep_key1 + || s_wireless_sec->wep_key2 + || s_wireless_sec->wep_key3) + nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY); + + goto done; + } + + nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY); + + if ( !strcmp (s_wireless_sec->key_mgmt, "wpa-psk") + || !strcmp (s_wireless_sec->key_mgmt, "wpa-none")) { + if (has_proto_wpa (s_wireless_sec)) { + flags = nm_ap_get_wpa_flags (ap); + nm_ap_set_wpa_flags (ap, flags | NM_802_11_AP_SEC_KEY_MGMT_PSK); + } + + if (has_proto_rsn (s_wireless_sec)) { + flags = nm_ap_get_rsn_flags (ap); + nm_ap_set_rsn_flags (ap, flags | NM_802_11_AP_SEC_KEY_MGMT_PSK); + } + } + + if ( !strcmp (s_wireless_sec->key_mgmt, "ieee8021x") + || !strcmp (s_wireless_sec->key_mgmt, "wpa-eap")) { + if (has_proto_wpa (s_wireless_sec)) { + flags = nm_ap_get_wpa_flags (ap); + nm_ap_set_wpa_flags (ap, flags | NM_802_11_AP_SEC_KEY_MGMT_802_1X); + } + + if (has_proto_rsn (s_wireless_sec)) { + flags = nm_ap_get_rsn_flags (ap); + nm_ap_set_rsn_flags (ap, flags | NM_802_11_AP_SEC_KEY_MGMT_802_1X); + } + } + + add_ciphers (ap, s_wireless_sec, FALSE); + add_ciphers (ap, s_wireless_sec, TRUE); + +done: + return ap; + +error: + g_object_unref (ap); + return NULL; +} + + #define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" #define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] @@ -856,29 +1014,26 @@ void nm_ap_set_invalid (NMAccessPoint *ap, gboolean invalid) /* - * Get/Set functions to indicate that an access point is - * 'artificial', ie whether or not it was actually scanned - * by the card or not - * + * Get/Set functions to indicate that an access point is 'fake', ie whether + * or not it was created from scan results */ -gboolean nm_ap_get_artificial (const NMAccessPoint *ap) +gboolean nm_ap_get_fake (const NMAccessPoint *ap) { g_return_val_if_fail (NM_IS_AP (ap), FALSE); - return NM_AP_GET_PRIVATE (ap)->artificial; + return NM_AP_GET_PRIVATE (ap)->fake; } -void nm_ap_set_artificial (NMAccessPoint *ap, gboolean artificial) +void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake) { g_return_if_fail (NM_IS_AP (ap)); - NM_AP_GET_PRIVATE (ap)->artificial = artificial; + NM_AP_GET_PRIVATE (ap)->fake = fake; } /* - * Get/Set functions to indicate whether an access point is broadcasting - * (hidden). This is a superset of artificial. + * Get/Set functions to indicate whether an AP broadcasts its SSID. */ gboolean nm_ap_get_broadcast (NMAccessPoint *ap) { @@ -1091,27 +1246,67 @@ security_compatible (NMAccessPoint *self, if (priv->mode != IW_MODE_INFRA) return FALSE; - /* Dynamic WEP or LEAP/Network EAP */ + /* Dynamic WEP or LEAP */ if (!strcmp (s_wireless_sec->key_mgmt, "ieee8021x")) { - // FIXME: should we allow APs that advertise WPA/RSN support here? - if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY) - || (wpa_flags != NM_802_11_AP_SEC_NONE) - || (rsn_flags != NM_802_11_AP_SEC_NONE)) + if (!(flags & NM_802_11_AP_FLAGS_PRIVACY)) return FALSE; + + /* If the AP is advertising a WPA IE, make sure it supports WEP ciphers */ + if (wpa_flags != NM_802_11_AP_SEC_NONE) { + gboolean found = FALSE; + GSList *iter; + + if (!(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + + /* quick check; can't use AP if it doesn't support at least one + * WEP cipher in both pairwise and group suites. + */ + if ( !(wpa_flags & (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104)) + || !(wpa_flags & (NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104))) + return FALSE; + + /* Match at least one pairwise cipher with AP's capability */ + for (iter = s_wireless_sec->pairwise; iter; iter = g_slist_next (iter)) { + if ((found = match_cipher (iter->data, "wep40", wpa_flags, wpa_flags, NM_802_11_AP_SEC_PAIR_WEP40))) + break; + if ((found = match_cipher (iter->data, "wep104", wpa_flags, wpa_flags, NM_802_11_AP_SEC_PAIR_WEP104))) + break; + } + if (!found) + return FALSE; + + /* Match at least one group cipher with AP's capability */ + for (iter = s_wireless_sec->group; iter; iter = g_slist_next (iter)) { + if ((found = match_cipher (iter->data, "wep40", wpa_flags, wpa_flags, NM_802_11_AP_SEC_GROUP_WEP40))) + break; + if ((found = match_cipher (iter->data, "wep104", wpa_flags, wpa_flags, NM_802_11_AP_SEC_GROUP_WEP104))) + break; + } + if (!found) + return FALSE; + } return TRUE; } - /* WPA[2]-PSK */ - if (!strcmp (s_wireless_sec->key_mgmt, "wpa-psk")) { + /* WPA[2]-PSK and WPA[2] Enterprise */ + if ( !strcmp (s_wireless_sec->key_mgmt, "wpa-psk") + || !strcmp (s_wireless_sec->key_mgmt, "wpa-eap")) { GSList * elt; gboolean found = FALSE; if (!s_wireless_sec->pairwise || !s_wireless_sec->group) return FALSE; - if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) - return FALSE; + if (!strcmp (s_wireless_sec->key_mgmt, "wpa-psk")) { + if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) + return FALSE; + } else if (!strcmp (s_wireless_sec->key_mgmt, "wpa-eap")) { + if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + return FALSE; + } // FIXME: should handle WPA and RSN separately here to ensure that // if the Connection only uses WPA we don't match a cipher against @@ -1144,10 +1339,6 @@ security_compatible (NMAccessPoint *self, return TRUE; } - if (!strcmp (s_wireless_sec->key_mgmt, "wpa-eap")) { - // FIXME: implement - } - return FALSE; } @@ -1204,6 +1395,94 @@ nm_ap_check_compatible (NMAccessPoint *self, return security_compatible (self, connection, s_wireless); } +static gboolean +capabilities_compatible (guint32 a_flags, guint32 b_flags) +{ + /* Make sure there's a common key management method */ + if (!((a_flags & 0x300) & (b_flags & 0x300))) + return FALSE; + + /* Ensure common pairwise ciphers */ + if (!((a_flags & 0xF) & (b_flags & 0xF))) + return FALSE; + + /* Ensure common group ciphers */ + if (!((a_flags & 0xF0) & (b_flags & 0xF0))) + return FALSE; + + return TRUE; +} + +NMAccessPoint * +nm_ap_match_in_list (NMAccessPoint *find_ap, + GSList *ap_list, + gboolean strict_match) +{ + GSList *iter; + + g_return_val_if_fail (find_ap != NULL, NULL); + + for (iter = ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint * list_ap = NM_AP (iter->data); + const GByteArray * list_ssid = nm_ap_get_ssid (list_ap); + const struct ether_addr * list_addr = nm_ap_get_address (list_ap); + + const GByteArray * find_ssid = nm_ap_get_ssid (find_ap); + const struct ether_addr * find_addr = nm_ap_get_address (find_ap); + + /* SSID match; if both APs are hiding their SSIDs, + * let matching continue on BSSID and other properties + */ + if ( (!list_ssid && find_ssid) + || (list_ssid && !find_ssid) + || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE)) + continue; + + /* BSSID match */ + if ( (strict_match || nm_ethernet_address_is_valid (find_addr)) + && nm_ethernet_address_is_valid (list_addr) + && memcmp (list_addr->ether_addr_octet, + find_addr->ether_addr_octet, + ETH_ALEN) != 0) { + continue; + } + + /* mode match */ + if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap)) + continue; + + /* Frequency match */ + if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap)) + continue; + + /* AP flags */ + if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap)) + continue; + + if (strict_match) { + if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap)) + continue; + + if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap)) + continue; + } else { + guint32 list_wpa_flags = nm_ap_get_wpa_flags (list_ap); + guint32 find_wpa_flags = nm_ap_get_wpa_flags (find_ap); + guint32 list_rsn_flags = nm_ap_get_rsn_flags (list_ap); + guint32 find_rsn_flags = nm_ap_get_rsn_flags (find_ap); + + /* Just ensure that there is overlap in the capabilities */ + if ( !capabilities_compatible (list_wpa_flags, find_wpa_flags) + && !capabilities_compatible (list_rsn_flags, find_rsn_flags)) + continue; + } + + return list_ap; + } + + return NULL; +} + struct cf_pair { guint32 chan; diff --git a/src/NetworkManagerAP.h b/src/NetworkManagerAP.h index e9289efc9f..2f431e22dd 100644 --- a/src/NetworkManagerAP.h +++ b/src/NetworkManagerAP.h @@ -61,6 +61,7 @@ GType nm_ap_get_type (void); NMAccessPoint * nm_ap_new (void); NMAccessPoint * nm_ap_new_from_properties (GHashTable *properties); +NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection); void nm_ap_export_to_dbus (NMAccessPoint *ap); const char * nm_ap_get_dbus_path (NMAccessPoint *ap); @@ -98,8 +99,8 @@ void nm_ap_set_rate (NMAccessPoint *ap, guint16 rate); gboolean nm_ap_get_invalid (const NMAccessPoint *ap); void nm_ap_set_invalid (NMAccessPoint *ap, gboolean invalid); -gboolean nm_ap_get_artificial (const NMAccessPoint *ap); -void nm_ap_set_artificial (NMAccessPoint *ap, gboolean artificial); +gboolean nm_ap_get_fake (const NMAccessPoint *ap); +void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake); gboolean nm_ap_get_broadcast (NMAccessPoint *ap); void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast); @@ -120,6 +121,10 @@ guint32 nm_ap_add_security_from_ie (guint32 flags, gboolean nm_ap_check_compatible (NMAccessPoint *self, NMConnection *connection); +NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap, + GSList *ap_list, + gboolean strict_match); + void nm_ap_print_self (NMAccessPoint *ap, const char * prefix); guint32 freq_to_channel (guint32 freq); diff --git a/src/nm-device-802-11-wireless.c b/src/nm-device-802-11-wireless.c index b858f7c937..c7991e3408 100644 --- a/src/nm-device-802-11-wireless.c +++ b/src/nm-device-802-11-wireless.c @@ -49,6 +49,8 @@ static gboolean impl_device_get_access_points (NMDevice80211Wireless *device, GPtrArray **aps, GError **err); +static guint32 nm_device_802_11_wireless_get_frequency (NMDevice80211Wireless *self); + #if DEBUG static void nm_device_802_11_wireless_ap_list_print (NMDevice80211Wireless *self); #endif @@ -489,7 +491,8 @@ out: } static NMAccessPoint * -get_active_ap (NMDevice80211Wireless *self) +get_active_ap (NMDevice80211Wireless *self, + NMAccessPoint *ignore_ap) { struct ether_addr bssid; const GByteArray *ssid; @@ -507,9 +510,21 @@ get_active_ap (NMDevice80211Wireless *self) const struct ether_addr *ap_bssid = nm_ap_get_address (ap); const GByteArray *ap_ssid = nm_ap_get_ssid (ap); - if ( nm_ethernet_addresses_are_equal (&bssid, ap_bssid) - && nm_utils_same_ssid (ssid, ap_ssid, TRUE)) - return ap; + if (ignore_ap && (ap == ignore_ap)) + continue; + + if ( !nm_ethernet_addresses_are_equal (&bssid, ap_bssid) + || !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) + continue; + + if (nm_device_802_11_wireless_get_mode (self) != nm_ap_get_mode (ap)) + continue; + + if (nm_device_802_11_wireless_get_frequency (self) != nm_ap_get_freq (ap)) + continue; + + // FIXME: handle security settings here too + return ap; } return NULL; @@ -558,7 +573,7 @@ periodic_update (NMDevice80211Wireless *self, gboolean honor_scan) if (honor_scan && priv->scanning) return; - new_ap = get_active_ap (self); + new_ap = get_active_ap (self, NULL); if (new_ap) nm_device_802_11_wireless_update_signal_strength (self, new_ap); @@ -961,6 +976,7 @@ nm_device_802_11_wireless_get_mode (NMDevice80211Wireless *self) nm_dev_sock_close (sk); } +out: return mode; } @@ -1016,6 +1032,39 @@ nm_device_802_11_wireless_set_mode (NMDevice80211Wireless *self, } +/* + * nm_device_802_11_wireless_get_frequency + * + * Get current frequency + * + */ +static guint32 +nm_device_802_11_wireless_get_frequency (NMDevice80211Wireless *self) +{ + NMSock *sk; + int err; + double freq = 0; + const char *iface; + struct iwreq wrq; + + g_return_val_if_fail (self != NULL, 0); + + iface = nm_device_get_iface (NM_DEVICE (self)); + sk = nm_dev_sock_open (iface, DEV_WIRELESS, __FUNCTION__, NULL); + if (!sk) + return 0; + + nm_ioctl_info ("%s: About to GET IWFREQ.", iface); + err = iw_get_ext (nm_dev_sock_get_fd (sk), iface, SIOCGIWFREQ, &wrq); + if (err >= 0) + freq = iw_freq2float (&wrq.u.freq); + else if (err == -1) + nm_warning ("(%s) error getting frequency: %s", iface, strerror (errno)); + + nm_dev_sock_close (sk); + return (guint32) (freq / 1000000); +} + /* * wireless_stats_to_percent * @@ -1601,48 +1650,7 @@ merge_scanned_ap (NMDevice80211Wireless *self, nm_ap_set_broadcast (merge_ap, FALSE); } - for (elt = self->priv->ap_list; elt; elt = g_slist_next (elt)) { - NMAccessPoint * list_ap = NM_AP (elt->data); - const GByteArray * list_ssid = nm_ap_get_ssid (list_ap); - const struct ether_addr * list_addr = nm_ap_get_address (list_ap); - int list_mode = nm_ap_get_mode (list_ap); - double list_freq = nm_ap_get_freq (list_ap); - - const GByteArray * merge_ssid = nm_ap_get_ssid (merge_ap); - const struct ether_addr * merge_addr = nm_ap_get_address (merge_ap); - int merge_mode = nm_ap_get_mode (merge_ap); - double merge_freq = nm_ap_get_freq (merge_ap); - - /* SSID match; if both APs are hiding their SSIDs, - * let matching continue on BSSID and other properties - */ - if ( (!list_ssid && merge_ssid) - || (list_ssid && !merge_ssid) - || !nm_utils_same_ssid (list_ssid, merge_ssid, TRUE)) - continue; - - /* BSSID match */ - if ( nm_ethernet_address_is_valid (list_addr) - && memcmp (list_addr->ether_addr_octet, - merge_addr->ether_addr_octet, - ETH_ALEN) != 0) { - continue; - } - - /* mode match */ - if (list_mode != merge_mode) - continue; - - /* Frequency match */ - if ((int) list_freq != (int) merge_freq) - continue; - - // FIXME: make sure WPA AP doesn't get matched with WEP by taking - // flags into AP account - found_ap = list_ap; - break; - } - + found_ap = nm_ap_match_in_list (merge_ap, self->priv->ap_list, TRUE); if (found_ap) { nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap)); nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap)); @@ -1650,11 +1658,12 @@ merge_scanned_ap (NMDevice80211Wireless *self, nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap)); nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap)); nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap)); + nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap)); /* If the AP is noticed in a scan, it's automatically no longer - * artificial, since it clearly exists somewhere. + * fake, since it clearly exists somewhere. */ - nm_ap_set_artificial (found_ap, FALSE); + nm_ap_set_fake (found_ap, FALSE); } else { /* New entry in the list */ // FIXME: figure out if reference counts are correct here for AP objects @@ -1694,6 +1703,8 @@ cull_scan_list (NMDevice80211Wireless * self) /* Don't ever prune the AP we're currently associated with */ if (cur_ap_path && !strcmp (cur_ap_path, nm_ap_get_dbus_path (ap))) keep = TRUE; + if (nm_ap_get_fake (ap)) + keep = TRUE; if (!keep && (ap_time + prune_interval_s < cur_time.tv_sec)) outdated_list = g_slist_append (outdated_list, ap); @@ -2400,12 +2411,49 @@ static NMActStageReturn real_act_stage1_prepare (NMDevice *dev) { NMDevice80211Wireless *self = NM_DEVICE_802_11_WIRELESS (dev); - NMAccessPoint *ap; + NMAccessPoint *ap = NULL; - /* Make sure we've got an AP to connect to */ + /* If the user is trying to connect to an AP that NM doesn't yet know about + * (hidden network or something), create an fake AP from the security + * settings in the connection to use until the AP is recognized from the + * scan list, which should show up when the connection is successful. + */ ap = nm_device_802_11_wireless_get_activation_ap (self); - if (!ap) - return NM_ACT_STAGE_RETURN_FAILURE; + if (!ap) { + NMActRequest *req; + NMConnection *connection; + GSList *iter; + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + connection = nm_act_request_get_connection (req); + g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + /* Find a compatible AP in the scan list */ + for (iter = self->priv->ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint *candidate = NM_AP (iter->data); + + if (nm_ap_check_compatible (candidate, connection)) { + ap = candidate; + break; + } + } + + /* If no compatible AP was found, create a fake AP (network is likely + * hidden) and try to use that. + */ + if (!ap) { + ap = nm_ap_new_fake_from_connection (connection); + g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + self->priv->ap_list = g_slist_append (self->priv->ap_list, ap); + nm_ap_export_to_dbus (ap); + g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, ap); + } + + nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (ap)); + } set_current_ap (self, ap); @@ -2658,21 +2706,40 @@ real_act_stage4_ip_config_timeout (NMDevice *dev, static void activation_success_handler (NMDevice *dev) { - NMDevice80211Wireless * self = NM_DEVICE_802_11_WIRELESS (dev); - NMAccessPoint * ap; - gboolean automatic; + NMDevice80211Wireless *self = NM_DEVICE_802_11_WIRELESS (dev); + NMAccessPoint *ap; + struct ether_addr bssid; + NMAccessPoint *tmp_ap; ap = nm_device_802_11_wireless_get_activation_ap (self); - /* Cache details in the info-daemon since the connect was successful */ - automatic = !nm_act_request_get_user_requested (nm_device_get_act_request (dev)); + /* If the activate AP was fake, it probably won't have a BSSID at all. + * But if activation was successful, the card will know the BSSID. Grab + * the BSSID off the card and fill in the BSSID of the activation AP. + */ + if (!nm_ap_get_fake (ap)) + goto done; - /* 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)) { - if (!ap_list_get_ap_by_ssid (self->priv->ap_list, nm_ap_get_ssid (ap))) - self->priv->ap_list = g_slist_append (self->priv->ap_list, ap); + nm_device_802_11_wireless_get_bssid (self, &bssid); + if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap))) + nm_ap_set_address (ap, &bssid); + + tmp_ap = get_active_ap (self, ap); + if (tmp_ap) { + NMActRequest *req = nm_device_get_act_request (NM_DEVICE (self)); + GSList *elt; + + /* Found a better match in the scan list than the fake AP. Use it + * instead. + */ + nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (tmp_ap)); + + self->priv->ap_list = g_slist_remove (self->priv->ap_list, ap); + g_object_unref (ap); + ap = tmp_ap; } +done: periodic_update (self, FALSE); } @@ -2685,13 +2752,14 @@ activation_failure_handler (NMDevice *dev) const GByteArray * ssid; if ((ap = nm_device_802_11_wireless_get_activation_ap (self))) { - if (nm_ap_get_artificial (ap)) { - /* Artificial APs are ones that don't show up in scans, + if (nm_ap_get_fake (ap)) { + /* Fake 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. */ + access_point_removed (self, ap); self->priv->ap_list = g_slist_remove (self->priv->ap_list, ap); g_object_unref (ap); } else {