diff --git a/src/wps/wps.c b/src/wps/wps.c index 619af158d..f7ff2496e 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -236,6 +236,36 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg) } +/** + * wps_ap_priority_compar - Prioritize WPS IE from two APs + * @wps_a: WPS IE contents from Beacon or Probe Response frame + * @wps_b: WPS IE contents from Beacon or Probe Response frame + * Returns: 1 if wps_b is considered more likely selection for WPS + * provisioning, -1 if wps_a is considered more like, or 0 if no preference + */ +int wps_ap_priority_compar(const struct wpabuf *wps_a, + const struct wpabuf *wps_b) +{ + struct wps_parse_attr attr_a, attr_b; + int sel_a, sel_b; + + if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0) + return 1; + if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0) + return -1; + + sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0; + sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0; + + if (sel_a && !sel_b) + return -1; + if (!sel_a && sel_b) + return 1; + + return 0; +} + + /** * wps_get_uuid_e - Get UUID-E from WPS IE * @msg: WPS IE contents from Beacon or Probe Response frame diff --git a/src/wps/wps.h b/src/wps/wps.h index 41082aad2..8536499d7 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -195,6 +195,8 @@ struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code); int wps_is_selected_pbc_registrar(const struct wpabuf *msg); int wps_is_selected_pin_registrar(const struct wpabuf *msg); +int wps_ap_priority_compar(const struct wpabuf *wps_a, + const struct wpabuf *wps_b); const u8 * wps_get_uuid_e(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index fd01bb8fa..90d40c802 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -641,6 +641,54 @@ static int wpa_scan_result_compar(const void *a, const void *b) } +#ifdef CONFIG_WPS +/* 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; +} +#endif /* CONFIG_WPS */ + + /** * wpa_supplicant_get_scan_results - Get scan results * @wpa_s: Pointer to wpa_supplicant data @@ -658,6 +706,7 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, { struct wpa_scan_results *scan_res; size_t i; + int (*compar)(const void *, const void *) = wpa_scan_result_compar; if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) scan_res = ieee80211_sta_get_scan_results(wpa_s); @@ -668,8 +717,16 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, return NULL; } +#ifdef CONFIG_WPS + if (wpas_wps_in_progress(wpa_s)) { + wpa_printf(MSG_DEBUG, "WPS: Order scan results with WPS " + "provisioning rules"); + compar = wpa_scan_result_wps_compar; + } +#endif /* CONFIG_WPS */ + qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), - wpa_scan_result_compar); + compar); wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 2b90e5746..db615f635 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -1264,3 +1264,16 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS_ER */ return 0; } + + +int wpas_wps_in_progress(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS) + return 1; + } + + return 0; +} diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 701bcb577..5417c501e 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -62,6 +62,7 @@ int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin, struct wps_new_ap_settings *settings); int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s); +int wpas_wps_in_progress(struct wpa_supplicant *wpa_s); #else /* CONFIG_WPS */