/* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int wpa_a, wpa_b, maxrate_a, maxrate_b; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; if (wpa_b && !wpa_a) return 1; if (!wpa_b && wpa_a) return -1; /* privacy support preferred */ if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && (wb->caps & IEEE80211_CAP_PRIVACY)) return 1; if ((wa->caps & IEEE80211_CAP_PRIVACY) && (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; /* best/max rate preferred if signal level close enough XXX */ if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { maxrate_a = wpa_scan_get_max_rate(wa); maxrate_b = wpa_scan_get_max_rate(wb); if (maxrate_a != maxrate_b) return maxrate_b - maxrate_a; } /* use freq for channel preference */ /* all things being equal, use signal level; if signal levels are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ if (wb->level == wa->level) return wb->qual - wa->qual; return wb->level - wa->level; }
/* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( const struct wpa_scan_res *res, char *buf, size_t buflen) { char *pos, *end; int ret; const u8 *ie, *ie2; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", MAC2STR(res->bssid), res->freq, res->level); if (ret < 0 || ret >= end - pos) return -1; pos += ret; ie = wpa_scan_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_scan_get_ie(res, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); pos = wpa_supplicant_wps_ie_txt(pos, end, res); if (!ie && !ie2 && res->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } /* Just to make the fields line up nicely when printed */ if (!ie && !ie2) { ret = os_snprintf(pos, end - pos, "\t"); if (ret < 0 || ret >= end - pos) return -1; pos += ret; } ie = wpa_scan_get_ie(res, WLAN_EID_SSID); if (res->caps & IEEE80211_CAP_IBSS) ret = os_snprintf(pos, end - pos, "\t%s%s", "(*)", wpa_ssid_txt(ie + 2, ie[1])); else ret = os_snprintf(pos, end - pos, "\t%s", ie ? wpa_ssid_txt(ie + 2, ie[1]) : ""); if (ret < 0 || ret >= end - pos) return -1; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) return -1; pos += ret; return pos - buf; }
/* Compare function for sorting scan results when searching a WPS AP for * provisioning. Return >0 if @b is considered better. */ static int wpa_scan_result_wps_compar(const void *a, const void *b) { struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int uses_wps_a, uses_wps_b; struct wpabuf *wps_a, *wps_b; int res; /* Optimization - check WPS IE existence before allocated memory and * doing full reassembly. */ uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL; uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL; if (uses_wps_a && !uses_wps_b) return -1; if (!uses_wps_a && uses_wps_b) return 1; if (uses_wps_a && uses_wps_b) { wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE); wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE); res = wps_ap_priority_compar(wps_a, wps_b); wpabuf_free(wps_a); wpabuf_free(wps_b); if (res) return res; } /* * Do not use current AP security policy as a sorting criteria during * WPS provisioning step since the AP may get reconfigured at the * completion of provisioning. */ /* all things being equal, use signal level; if signal levels are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ if (wb->level == wa->level) return wb->qual - wa->qual; return wb->level - wa->level; }
static int are_ies_equal(const struct wpa_bss *old, const struct wpa_scan_res *new_res, u32 ie) { const u8 *old_ie, *new_ie; struct wpabuf *old_ie_buff = NULL; struct wpabuf *new_ie_buff = NULL; int new_ie_len, old_ie_len, ret, is_multi; switch (ie) { case WPA_IE_VENDOR_TYPE: old_ie = wpa_bss_get_vendor_ie(old, ie); new_ie = wpa_scan_get_vendor_ie(new_res, ie); is_multi = 0; break; case WPS_IE_VENDOR_TYPE: old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie); new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie); is_multi = 1; break; case WLAN_EID_RSN: case WLAN_EID_SUPP_RATES: case WLAN_EID_EXT_SUPP_RATES: old_ie = wpa_bss_get_ie(old, ie); new_ie = wpa_scan_get_ie(new_res, ie); is_multi = 0; break; default: wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__); return 0; } if (is_multi) { /* in case of multiple IEs stored in buffer */ old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL; new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL; old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0; new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0; } else { /* in case of single IE */ old_ie_len = old_ie ? old_ie[1] + 2 : 0; new_ie_len = new_ie ? new_ie[1] + 2 : 0; } if (!old_ie || !new_ie) ret = !old_ie && !new_ie; else ret = (old_ie_len == new_ie_len && os_memcmp(old_ie, new_ie, old_ie_len) == 0); wpabuf_free(old_ie_buff); wpabuf_free(new_ie_buff); return ret; }
static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s) { size_t i; int ret = 0; struct wpa_scan_res *curr = NULL; struct wpa_ssid *ssid = wpa_s->current_ssid; const u8 *ie; if (wpa_s->scan_res == NULL) return -1; for (i = 0; i < wpa_s->scan_res->num; i++) { struct wpa_scan_res *r = wpa_s->scan_res->res[i]; if (os_memcmp(r->bssid, wpa_s->bssid, ETH_ALEN) != 0) continue; ie = wpa_scan_get_ie(r, WLAN_EID_SSID); if (ssid == NULL || ((ie && ie[1] == ssid->ssid_len && os_memcmp(ie + 2, ssid->ssid, ssid->ssid_len) == 0) || ssid->ssid_len == 0)) { curr = r; break; } } if (curr) { ie = wpa_scan_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE); if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) ret = -1; ie = wpa_scan_get_ie(curr, WLAN_EID_RSN); if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) ret = -1; } else { ret = -1; } return ret; }
static struct wpa_scan_res * wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid) { struct wpa_ssid *ssid; struct wpa_scan_res *bss; size_t i; struct wpa_blacklist *e; const u8 *ie; wpa_printf(MSG_DEBUG, "Try to find non-WPA AP"); for (i = 0; i < wpa_s->scan_res->num; i++) { const u8 *ssid_; u8 wpa_ie_len, rsn_ie_len, ssid_len; bss = wpa_s->scan_res->res[i]; ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); ssid_ = ie ? ie + 2 : (u8 *) ""; ssid_len = ie ? ie[1] : 0; ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", (int) i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), wpa_ie_len, rsn_ie_len, bss->caps); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e && e->count > 1) { wpa_printf(MSG_DEBUG, " skip - blacklisted"); continue; } if (ssid_len == 0) { wpa_printf(MSG_DEBUG, " skip - SSID not known"); continue; } for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = ssid->ssid_len != 0; if (ssid->disabled) { wpa_printf(MSG_DEBUG, " skip - disabled"); continue; } #ifdef CONFIG_WPS if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { /* Only allow wildcard SSID match if an AP * advertises active WPS operation that matches * with our mode. */ check_ssid = 1; if (ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; } #endif /* CONFIG_WPS */ if (check_ssid && (ssid_len != ssid->ssid_len || os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { wpa_printf(MSG_DEBUG, " skip - " "SSID mismatch"); continue; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, " skip - " "BSSID mismatch"); continue; } if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { wpa_printf(MSG_DEBUG, " skip - " "non-WPA network not allowed"); continue; } if ((ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_PSK_SHA256)) && (wpa_ie_len != 0 || rsn_ie_len != 0)) { wpa_printf(MSG_DEBUG, " skip - " "WPA network"); continue; } if (!wpa_supplicant_match_privacy(bss, ssid)) { wpa_printf(MSG_DEBUG, " skip - " "privacy mismatch"); continue; } if (bss->caps & IEEE80211_CAP_IBSS) { wpa_printf(MSG_DEBUG, " skip - " "IBSS (adhoc) network"); continue; } wpa_printf(MSG_DEBUG, " selected non-WPA AP " MACSTR " ssid='%s'", MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len)); *selected_ssid = ssid; return bss; } } return NULL; }
static struct wpa_scan_res * wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid) { struct wpa_ssid *ssid; struct wpa_scan_res *bss; size_t i; struct wpa_blacklist *e; const u8 *ie; wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP"); for (i = 0; i < wpa_s->scan_res->num; i++) { const u8 *ssid_; u8 wpa_ie_len, rsn_ie_len, ssid_len; bss = wpa_s->scan_res->res[i]; ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); ssid_ = ie ? ie + 2 : (u8 *) ""; ssid_len = ie ? ie[1] : 0; ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", (int) i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), wpa_ie_len, rsn_ie_len, bss->caps); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e && e->count > 1) { wpa_printf(MSG_DEBUG, " skip - blacklisted"); continue; } if (ssid_len == 0) { wpa_printf(MSG_DEBUG, " skip - SSID not known"); continue; } if (wpa_ie_len == 0 && rsn_ie_len == 0) { wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); continue; } for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = 1; if (ssid->disabled) { wpa_printf(MSG_DEBUG, " skip - disabled"); continue; } #ifdef CONFIG_WPS if (ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; #endif /* CONFIG_WPS */ if (check_ssid && (ssid_len != ssid->ssid_len || os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { wpa_printf(MSG_DEBUG, " skip - " "SSID mismatch"); continue; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, " skip - " "BSSID mismatch"); continue; } if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) continue; wpa_printf(MSG_DEBUG, " selected WPA AP " MACSTR " ssid='%s'", MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len)); *selected_ssid = ssid; return bss; } } return NULL; }
static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_scan_res *bss) { struct wpa_ie_data ie; int proto_match = 0; const u8 *rsn_ie, *wpa_ie; int ret; ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); if (ret >= 0) return ret; rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { wpa_printf(MSG_DEBUG, " skip RSN IE - parse failed"); break; } if (!(ie.proto & ssid->proto)) { wpa_printf(MSG_DEBUG, " skip RSN IE - proto " "mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { wpa_printf(MSG_DEBUG, " skip RSN IE - PTK cipher " "mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { wpa_printf(MSG_DEBUG, " skip RSN IE - GTK cipher " "mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { wpa_printf(MSG_DEBUG, " skip RSN IE - key mgmt " "mismatch"); break; } #ifdef CONFIG_IEEE80211W if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && ssid->ieee80211w == IEEE80211W_REQUIRED) { wpa_printf(MSG_DEBUG, " skip RSN IE - no mgmt frame " "protection"); break; } #endif /* CONFIG_IEEE80211W */ wpa_printf(MSG_DEBUG, " selected based on RSN IE"); return 1; } wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { wpa_printf(MSG_DEBUG, " skip WPA IE - parse failed"); break; } if (!(ie.proto & ssid->proto)) { wpa_printf(MSG_DEBUG, " skip WPA IE - proto " "mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { wpa_printf(MSG_DEBUG, " skip WPA IE - PTK cipher " "mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { wpa_printf(MSG_DEBUG, " skip WPA IE - GTK cipher " "mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { wpa_printf(MSG_DEBUG, " skip WPA IE - key mgmt " "mismatch"); break; } wpa_printf(MSG_DEBUG, " selected based on WPA IE"); return 1; } if (proto_match == 0) wpa_printf(MSG_DEBUG, " skip - no WPA/RSN proto match"); return 0; }
static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_scan_res *bss, struct wpa_ssid *group) { const u8 *ssid_; u8 wpa_ie_len, rsn_ie_len, ssid_len; int wpa; struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); ssid_ = ie ? ie + 2 : (u8 *) ""; ssid_len = ie ? ie[1] : 0; ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e && e->count > 1) { wpa_printf(MSG_DEBUG, " skip - blacklisted"); return 0; } if (ssid_len == 0) { wpa_printf(MSG_DEBUG, " skip - SSID not known"); return 0; } wpa = wpa_ie_len > 0 || rsn_ie_len > 0; for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); if (ssid->disabled) { wpa_printf(MSG_DEBUG, " skip - disabled"); continue; } #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { wpa_printf(MSG_DEBUG, " skip - blacklisted (WPS)"); continue; } if (wpa && ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Only allow wildcard SSID match if an AP * advertises active WPS operation that matches * with our mode. */ check_ssid = 1; if (ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; } #endif /* CONFIG_WPS */ if (check_ssid && (ssid_len != ssid->ssid_len || os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { wpa_printf(MSG_DEBUG, " skip - SSID mismatch"); continue; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, " skip - BSSID mismatch"); continue; } if (wpa && !wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) continue; if (!wpa && !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { wpa_printf(MSG_DEBUG, " skip - non-WPA network not " "allowed"); continue; } if (!wpa && !wpa_supplicant_match_privacy(bss, ssid)) { wpa_printf(MSG_DEBUG, " skip - privacy mismatch"); continue; } if (!wpa && (bss->caps & IEEE80211_CAP_IBSS)) { wpa_printf(MSG_DEBUG, " skip - IBSS (adhoc) " "network"); continue; } if (!freq_allowed(ssid->freq_list, bss->freq)) { wpa_printf(MSG_DEBUG, " skip - frequency not " "allowed"); continue; } #ifdef CONFIG_P2P /* * TODO: skip the AP if its P2P IE has Group Formation * bit set in the P2P Group Capability Bitmap and we * are not in Group Formation with that device. */ #endif /* CONFIG_P2P */ /* Matching configuration found */ return ssid; } /* No matching configuration found */ return 0; }
/* Compare function for sorting scan results. Return >0 if @b is considered * better. */ static int wpa_scan_result_compar(const void *a, const void *b) { #define IS_5GHZ(n) (n > 4000) #define MIN(a,b) a < b ? a : b struct wpa_scan_res **_wa = (void *) a; struct wpa_scan_res **_wb = (void *) b; struct wpa_scan_res *wa = *_wa; struct wpa_scan_res *wb = *_wb; int wpa_a, wpa_b, maxrate_a, maxrate_b; int snr_a, snr_b; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; if (wpa_b && !wpa_a) return 1; if (!wpa_b && wpa_a) return -1; /* privacy support preferred */ if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && (wb->caps & IEEE80211_CAP_PRIVACY)) return 1; if ((wa->caps & IEEE80211_CAP_PRIVACY) && (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) && !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) { snr_a = MIN(wa->level - wa->noise, GREAT_SNR); snr_b = MIN(wb->level - wb->noise, GREAT_SNR); } else { /* Not suitable information to calculate SNR, so use level */ snr_a = wa->level; snr_b = wb->level; } /* best/max rate preferred if SNR close enough */ if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { maxrate_a = wpa_scan_get_max_rate(wa); maxrate_b = wpa_scan_get_max_rate(wb); if (maxrate_a != maxrate_b) return maxrate_b - maxrate_a; if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq)) return IS_5GHZ(wa->freq) ? -1 : 1; } /* use freq for channel preference */ /* all things being equal, use SNR; if SNRs are * identical, use quality values since some drivers may only report * that value and leave the signal level zero */ if (snr_b == snr_a) return wb->qual - wa->qual; return snr_b - snr_a; #undef MIN #undef IS_5GHZ }
/** * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result * @wpa_s: Pointer to wpa_supplicant data * @res: Scan result * @fetch_time: Time when the result was fetched from the driver * * This function updates a BSS table entry (or adds one) based on a scan result. * This is called separately for each scan result between the calls to * wpa_bss_update_start() and wpa_bss_update_end(). */ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time) { const u8 *ssid, *p2p, *mesh; struct wpa_bss *bss; if (wpa_s->conf->ignore_old_scan_res) { struct os_reltime update; calculate_update_time(fetch_time, res->age, &update); if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) { struct os_reltime age; os_reltime_sub(&wpa_s->scan_trigger_time, &update, &age); wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS " "table entry that is %u.%06u seconds older " "than our scan trigger", (unsigned int) age.sec, (unsigned int) age.usec); return; } } ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " MACSTR, MAC2STR(res->bssid)); return; } if (ssid[1] > SSID_MAX_LEN) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " MACSTR, MAC2STR(res->bssid)); return; } p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); #ifdef CONFIG_P2P if (p2p == NULL && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { /* * If it's a P2P specific interface, then don't update * the scan result without a P2P IE. */ wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR " update for P2P interface", MAC2STR(res->bssid)); return; } #endif /* CONFIG_P2P */ if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) return; /* Skip P2P listen discovery results here */ /* TODO: add option for ignoring BSSes we are not interested in * (to save memory) */ mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID); if (mesh && mesh[1] <= SSID_MAX_LEN) ssid = mesh; bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); if (bss == NULL) bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time); else { bss = wpa_bss_update(wpa_s, bss, res, fetch_time); if (wpa_s->last_scan_res) { unsigned int i; for (i = 0; i < wpa_s->last_scan_res_used; i++) { if (bss == wpa_s->last_scan_res[i]) { /* Already in the list */ return; } } } } if (bss == NULL) return; if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) { struct wpa_bss **n; unsigned int siz; if (wpa_s->last_scan_res_size == 0) siz = 32; else siz = wpa_s->last_scan_res_size * 2; n = os_realloc_array(wpa_s->last_scan_res, siz, sizeof(struct wpa_bss *)); if (n == NULL) return; wpa_s->last_scan_res = n; wpa_s->last_scan_res_size = siz; } if (wpa_s->last_scan_res) wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; }
static struct wpa_bss * wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_scan_res *res, struct os_reltime *fetch_time) { u32 changes; changes = wpa_bss_compare_res(bss, res); if (changes & WPA_BSS_FREQ_CHANGED_FLAG) wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d", MAC2STR(bss->bssid), bss->freq, res->freq); bss->scan_miss_count = 0; bss->last_update_idx = wpa_s->bss_update_idx; wpa_bss_copy_res(bss, res, fetch_time); /* Move the entry to the end of the list */ dl_list_del(&bss->list); #ifdef CONFIG_P2P if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) { /* * This can happen when non-P2P station interface runs a scan * without P2P IE in the Probe Request frame. P2P GO would reply * to that with a Probe Response that does not include P2P IE. * Do not update the IEs in this BSS entry to avoid such loss of * information that may be needed for P2P operations to * determine group information. */ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for " MACSTR " since that would remove P2P IE information", MAC2STR(bss->bssid)); } else #endif /* CONFIG_P2P */ if (bss->ie_len + bss->beacon_ie_len >= res->ie_len + res->beacon_ie_len) { os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); bss->ie_len = res->ie_len; bss->beacon_ie_len = res->beacon_ie_len; } else { struct wpa_bss *nbss; struct dl_list *prev = bss->list_id.prev; dl_list_del(&bss->list_id); nbss = os_realloc(bss, sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (nbss) { unsigned int i; for (i = 0; i < wpa_s->last_scan_res_used; i++) { if (wpa_s->last_scan_res[i] == bss) { wpa_s->last_scan_res[i] = nbss; break; } } if (wpa_s->current_bss == bss) wpa_s->current_bss = nbss; wpa_bss_update_pending_connect(wpa_s, bss, nbss); bss = nbss; os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); bss->ie_len = res->ie_len; bss->beacon_ie_len = res->beacon_ie_len; } dl_list_add(prev, &bss->list_id); } if (changes & WPA_BSS_IES_CHANGED_FLAG) wpa_bss_set_hessid(bss); dl_list_add_tail(&wpa_s->bss, &bss->list); notify_bss_changes(wpa_s, changes, bss); return bss; }
static struct wpa_bss * wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res, struct wpa_ssid *group, struct wpa_ssid **selected_ssid) { struct wpa_ssid *ssid; struct wpa_scan_res *bss; size_t i; struct wpa_blacklist *e; const u8 *ie; wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP"); for (i = 0; i < scan_res->num; i++) { const u8 *ssid_; u8 wpa_ie_len, rsn_ie_len, ssid_len; bss = scan_res->res[i]; ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); ssid_ = ie ? ie + 2 : (u8 *) ""; ssid_len = ie ? ie[1] : 0; ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x", (int) i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), wpa_ie_len, rsn_ie_len, bss->caps); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e) { int limit = 1; if (wpa_supplicant_enabled_networks(wpa_s->conf) == 1) { /* * When only a single network is enabled, we can * trigger blacklisting on the first failure. This * should not be done with multiple enabled networks to * avoid getting forced to move into a worse ESS on * single error if there are no other BSSes of the * current ESS. */ limit = 0; } if (e->count > limit) { wpa_printf(MSG_DEBUG, " skip - blacklisted " "(count=%d limit=%d)", e->count, limit); continue; } } if (ssid_len == 0) { wpa_printf(MSG_DEBUG, " skip - SSID not known"); continue; } if (wpa_ie_len == 0 && rsn_ie_len == 0) { wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); continue; } for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = 1; if (ssid->disabled) { wpa_printf(MSG_DEBUG, " skip - disabled"); continue; } #ifdef CONFIG_WPS if (ssid->ssid_len == 0 && wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) check_ssid = 0; #endif /* CONFIG_WPS */ if (check_ssid && (ssid_len != ssid->ssid_len || os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { wpa_printf(MSG_DEBUG, " skip - " "SSID mismatch"); continue; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, " skip - " "BSSID mismatch"); continue; } if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) continue; if (!freq_allowed(ssid->freq_list, bss->freq)) { wpa_printf(MSG_DEBUG, " skip - " "frequency not allowed"); continue; } wpa_printf(MSG_DEBUG, " selected WPA AP " MACSTR " ssid='%s'", MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len)); *selected_ssid = ssid; return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len); } } return NULL; }
static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) { u8 bssid[ETH_ALEN]; size_t i; struct wpa_scan_results *results; struct wpa_scan_res *bss; int ret; char *pos, *end; const u8 *ie, *ie2; results = wpa_s->scan_res; if (results == NULL) return 0; if (hwaddr_aton(cmd, bssid) == 0) { for (i = 0; i < results->num; i++) { if (os_memcmp(bssid, results->res[i]->bssid, ETH_ALEN) == 0) break; } } else i = atoi(cmd); if (i >= results->num || results->res[i] == NULL) return 0; /* no match found */ bss = results->res[i]; pos = buf; end = buf + buflen; ret = snprintf(pos, end - pos, "bssid=" MACSTR "\n" "freq=%d\n" "beacon_int=%d\n" "capabilities=0x%04x\n" "qual=%d\n" "noise=%d\n" "level=%d\n" "tsf=%016llu\n" "ie=", MAC2STR(bss->bssid), bss->freq, bss->beacon_int, bss->caps, bss->qual, bss->noise, bss->level, (unsigned long long) bss->tsf); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ie = (const u8 *) (bss + 1); for (i = 0; i < bss->ie_len; i++) { ret = snprintf(pos, end - pos, "%02x", *ie++); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } ret = snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "flags="); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_scan_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } ret = snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); ret = os_snprintf(pos, end - pos, "ssid=%s\n", ie ? wpa_ssid_txt(ie + 2, ie[1]) : ""); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; return pos - buf; }
static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const struct wps_credential *cred) { struct wpa_driver_capa capa; size_t i; struct wpa_scan_res *bss; const u8 *ie; struct wpa_ie_data adv; int wpa2 = 0, ccmp = 0; /* * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in * case they are configured for mixed mode operation (WPA+WPA2 and * TKIP+CCMP). Try to use scan results to figure out whether the AP * actually supports stronger security and select that if the client * has support for it, too. */ if (wpa_drv_get_capa(wpa_s, &capa)) return; /* Unknown what driver supports */ if (wpa_supplicant_get_scan_results(wpa_s) || wpa_s->scan_res == NULL) return; /* Could not get scan results for checking advertised * parameters */ for (i = 0; i < wpa_s->scan_res->num; i++) { bss = wpa_s->scan_res->res[i]; if (os_memcmp(bss->bssid, cred->mac_addr, ETH_ALEN) != 0) continue; ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); if (ie == NULL) continue; if (ie[1] != ssid->ssid_len || ssid->ssid == NULL || os_memcmp(ie + 2, ssid->ssid, ssid->ssid_len) != 0) continue; wpa_printf(MSG_DEBUG, "WPS: AP found from scan results"); break; } if (i == wpa_s->scan_res->num) { wpa_printf(MSG_DEBUG, "WPS: The AP was not found from scan " "results - use credential as-is"); return; } ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) { wpa2 = 1; if (adv.pairwise_cipher & WPA_CIPHER_CCMP) ccmp = 1; } else { ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 && adv.pairwise_cipher & WPA_CIPHER_CCMP) ccmp = 1; } if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) && (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) { /* * TODO: This could be the initial AP configuration and the * Beacon contents could change shortly. Should request a new * scan and delay addition of the network until the updated * scan results are available. */ wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA " "support - use credential as-is"); return; } if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && (ssid->pairwise_cipher & WPA_CIPHER_TKIP) && (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential " "based on scan results"); if (wpa_s->conf->ap_scan == 1) ssid->pairwise_cipher |= WPA_CIPHER_CCMP; else ssid->pairwise_cipher = WPA_CIPHER_CCMP; } if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) && (ssid->proto & WPA_PROTO_WPA) && (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) { wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential " "based on scan results"); if (wpa_s->conf->ap_scan == 1) ssid->proto |= WPA_PROTO_RSN; else ssid->proto = WPA_PROTO_RSN; } }
/** * wpas_dbus_bssid_properties - Return the properties of a scanned network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface * @res: wpa_supplicant scan result for which to get properties * Returns: a dbus message containing the properties for the requested network * * Handler function for "properties" method call of a scanned network. * Returns a dbus message containing the the properties. */ DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, struct wpa_supplicant *wpa_s, struct wpa_scan_res *res) { DBusMessage *reply = NULL; DBusMessageIter iter, iter_dict; const u8 *ie; /* Dump the properties into a dbus message */ reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) goto error; if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", (const char *) res->bssid, ETH_ALEN)) goto error; ie = wpa_scan_get_ie(res, WLAN_EID_SSID); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", (const char *) (ie + 2), ie[1])) goto error; } ie = wpa_scan_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", (const char *) ie, ie[1] + 2)) goto error; } ie = wpa_scan_get_ie(res, WLAN_EID_RSN); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", (const char *) ie, ie[1] + 2)) goto error; } ie = wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE); if (ie) { if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", (const char *) ie, ie[1] + 2)) goto error; } if (res->freq) { if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", res->freq)) goto error; } if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", res->caps)) goto error; if (!wpa_dbus_dict_append_int32(&iter_dict, "quality", res->qual)) goto error; if (!wpa_dbus_dict_append_int32(&iter_dict, "noise", res->noise)) goto error; if (!wpa_dbus_dict_append_int32(&iter_dict, "level", res->level)) goto error; if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", wpa_scan_get_max_rate(res) * 500000)) goto error; if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) goto error; return reply; error: if (reply) dbus_message_unref(reply); return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, "an internal error occurred returning " "BSSID properties."); }