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 {