From 41e650ae5c90a83a0c22989d43527901347dacc0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 11 Jun 2010 13:50:13 -0700 Subject: [PATCH] WPS: Use different scan result sorting rules when doing WPS provisioning The AP configuration may change after provisioning, so it is better not to use the current security policy to prioritize results. Instead, use WPS Selected Registrar attribute as the main sorting key and use signal strength next without considering security policy or rate sets. The non-WPS provisioning case remains as-is, i.e., this change applies only when trying to find an AP for WPS provisioning. --- src/wps/wps.c | 30 +++++++++++++++++ src/wps/wps.h | 2 ++ wpa_supplicant/scan.c | 59 ++++++++++++++++++++++++++++++++- wpa_supplicant/wps_supplicant.c | 13 ++++++++ wpa_supplicant/wps_supplicant.h | 1 + 5 files changed, 104 insertions(+), 1 deletion(-) 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 */