[PATCH] Add wpa_cli command to show roaming candidates

Gery Kahn geryk at ti.com
Sun Aug 7 12:49:01 EDT 2011


The wpa_supplicant collects APs for roaming during background scanning.
The patch provides new cli command to get the list of roaming
candidates by selected APs from common wpa_supplicant list. The selection rules
are AP with the same SSID, AP with the same security type as connected netwrok
and not connected AP.

The patch introduces new function wpa_bss_ssid_match_security() to check if
selected AP has the same security as chosen ESS.

To test if AP has same WPS security as ESS introduced new function
wpa_ssid_match_wps(). There is also new file ssid.c to include SSID
functionality.


Signed-off-by: Gery Kahn <geryk at ti.com>
---
 wpa_supplicant/Makefile         |    1 +
 wpa_supplicant/bss.c            |  162 +++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/bss.h            |    3 +
 wpa_supplicant/ctrl_iface.c     |   37 +++++++++
 wpa_supplicant/events.c         |    1 -
 wpa_supplicant/ssid.c           |   84 ++++++++++++++++++++
 wpa_supplicant/ssid.h           |   21 +++++
 wpa_supplicant/wpa_cli.c        |   10 +++
 wpa_supplicant/wps_supplicant.c |   61 +-------------
 wpa_supplicant/wps_supplicant.h |    4 +
 10 files changed, 327 insertions(+), 57 deletions(-)
 create mode 100644 wpa_supplicant/ssid.c
 create mode 100644 wpa_supplicant/ssid.h

diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 4b3f5a0..866179d 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -56,6 +56,7 @@ install: all
 OBJS = config.o
 OBJS += notify.o
 OBJS += bss.o
+OBJS += ssid.o
 OBJS += eap_register.o
 OBJS += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 21d6322..503bb20 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -16,13 +16,16 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "rsn_supp/wpa.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
 #include "drivers/driver.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
 #include "notify.h"
 #include "scan.h"
 #include "bss.h"
+#include "ssid.h"
 
 
 /**
@@ -267,6 +270,165 @@ static u32 wpa_bss_compare_res(const struct wpa_bss *old,
 	return changes;
 }
 
+int wpa_bss_ssid_match_wps(struct wpa_supplicant *wpa_s,
+			   struct wpa_ssid *ssid, struct wpa_bss *bss)
+{
+	struct wpabuf *wps_ie;
+	int ret;
+
+	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+		return -1;
+
+	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+
+	ret = wpa_ssid_match_wps(wpa_s, ssid, wps_ie);
+
+	if (wps_ie)
+		wpabuf_free(wps_ie);
+
+	return ret;
+}
+
+int wpa_bss_ssid_match_security(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *ssid,
+				struct wpa_bss *bss)
+{
+	struct wpa_ie_data ie;
+	int proto_match = 0;
+	const u8 *rsn_ie, *wpa_ie;
+	int ret;
+	int wep_ok;
+
+	ret = wpa_bss_ssid_match_wps(wpa_s, ssid, bss);
+	if (ret >= 0)
+		return ret;
+
+	/* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
+	wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+		(((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+		  ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
+		 (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
+
+	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsn_ie)
+		wpa_dbg(wpa_s, MSG_DEBUG, "No RSN_IE");
+
+	while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
+		proto_match++;
+
+		if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - parse "
+				"failed");
+			break;
+		}
+
+		if (wep_ok &&
+		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+		{
+			wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
+				"in RSN IE");
+			return 1;
+		}
+
+		if (!(ie.proto & ssid->proto)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - proto "
+				"mismatch");
+			break;
+		}
+
+		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - PTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.group_cipher & ssid->group_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - GTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - key mgmt "
+				"mismatch");
+			break;
+		}
+
+#ifdef CONFIG_IEEE80211W
+		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+		    ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
+				"frame protection");
+			break;
+		}
+#endif /* CONFIG_IEEE80211W */
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
+		return 1;
+	}
+
+	wpa_ie = wpa_bss_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_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - parse "
+				"failed");
+			break;
+		}
+
+		if (wep_ok &&
+		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+		{
+			wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
+				"in WPA IE");
+			return 1;
+		}
+
+		if (!(ie.proto & ssid->proto)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - proto "
+				"mismatch");
+			break;
+		}
+
+		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - PTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.group_cipher & ssid->group_cipher)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - GTK "
+				"cipher mismatch");
+			break;
+		}
+
+		if (!(ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - key mgmt "
+				"mismatch");
+			break;
+		}
+
+		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on WPA IE");
+		return 1;
+	}
+
+	if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
+	    wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no WPA/RSN proto match");
+		return 0;
+	}
+
+	if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "   allow in non-WPA/WPA2");
+		return 1;
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "   reject due to mismatch with "
+		"WPA/WPA2");
+
+	return 0;
+}
 
 static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
 			       const struct wpa_bss *bss)
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 992b9c0..54d8a99 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -71,6 +71,9 @@ struct wpa_bss {
 	/* followed by beacon_ie_len octets of IEs */
 };
 
+int wpa_bss_ssid_match_security(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *ssid,
+				struct wpa_bss *bss);
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 730e607..44e1d9a 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2103,6 +2103,39 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
 	return 0;
 }
 
+static int wpa_supplicant_ctrl_iface_list_candidates(
+	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	struct wpa_bss *bss;
+	char *pos, *end;
+	int ret;
+
+	pos = buf;
+	end = buf + buflen;
+
+	/*
+	 * Find roaming candidates from bss list. Select BSSes with the same
+	 * ssid and the same security type, except BSS connected to
+	 */
+	dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+		if (os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN) == 0 ||
+		    bss->ssid_len != wpa_s->current_ssid->ssid_len ||
+		    os_memcmp(bss->ssid, wpa_s->current_ssid->ssid,
+			      bss->ssid_len) != 0 ||
+		    wpa_bss_ssid_match_security(wpa_s,
+						wpa_s->current_ssid, bss))
+			continue;
+
+		ret = os_snprintf(pos, end - pos,
+			"BSS " MACSTR "  freq %d  level %d\n",
+			MAC2STR(bss->bssid), bss->freq, bss->level);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
 
 #ifdef CONFIG_P2P
 static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
@@ -3270,6 +3303,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 	} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
 		if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "LIST_CANDIDATES") == 0) {
+		if (wpa_s->wpa_state == WPA_COMPLETED)
+			reply_len = wpa_supplicant_ctrl_iface_list_candidates(
+					wpa_s, reply, reply_size);
 	} else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
 		if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16))
 			reply_len = -1;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index a307eda..2a11770 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -460,7 +460,6 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
 	return 0;
 }
 
-
 static int freq_allowed(int *freqs, int freq)
 {
 	int i;
diff --git a/wpa_supplicant/ssid.c b/wpa_supplicant/ssid.c
new file mode 100644
index 0000000..0655852
--- /dev/null
+++ b/wpa_supplicant/ssid.c
@@ -0,0 +1,84 @@
+/*
+ * SSID table
+ * Copyright (c) 2011, Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "eap_peer/eap.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "notify.h"
+#include "bss.h"
+#include "wps_supplicant.h"
+
+
+int wpa_ssid_match_wps(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, struct wpabuf *wps_ie)
+{
+	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+		if (!wps_ie) {
+			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
+			return 0;
+		}
+
+		if (!wps_is_selected_pbc_registrar(wps_ie)) {
+			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
+				   "without active PBC Registrar");
+			return 0;
+		}
+
+		/* TODO: overlap detection */
+		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+			   "(Active PBC)");
+		return 1;
+	}
+
+	if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+		if (!wps_ie) {
+			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
+			return 0;
+		}
+
+		/*
+		 * Start with WPS APs that advertise our address as an
+		 * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
+		 * allow any WPS AP after couple of scans since some APs do not
+		 * set Selected Registrar attribute properly when using
+		 * external Registrar.
+		 */
+		if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
+			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
+				wpa_printf(MSG_DEBUG, "   skip - WPS AP "
+					   "without active PIN Registrar");
+				return 0;
+			}
+			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+		} else {
+			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+				   "(Authorized MAC or Active PIN)");
+		}
+		return 1;
+	}
+
+	if (wps_ie) {
+		wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+		return 1;
+	}
+
+	return -1;
+}
+
diff --git a/wpa_supplicant/ssid.h b/wpa_supplicant/ssid.h
new file mode 100644
index 0000000..8ddebae
--- /dev/null
+++ b/wpa_supplicant/ssid.h
@@ -0,0 +1,21 @@
+/*
+ * WPA Supplicant / Network configuration operations
+ * Copyright (c) 2011, Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SSID_H
+#define SSID_H
+
+int wpa_ssid_match_wps(struct wpa_supplicant *wpa_s,
+			    struct wpa_ssid *ssid, struct wpabuf *wps_ie);
+
+#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 9439856..7b84b7d 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1678,6 +1678,13 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_list_candidates(struct wpa_ctrl *ctrl,
+				       int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "LIST_CANDIDATES");
+}
+
+
 #ifdef CONFIG_P2P
 
 static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -2540,6 +2547,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
 	{ "roam", wpa_cli_cmd_roam,
 	  cli_cmd_flag_none,
 	  "<addr> = roam to the specified BSS" },
+	{ "list_candidates", wpa_cli_cmd_list_candidates,
+	  cli_cmd_flag_none,
+	  "= list candidates for roaming" },
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none,
 	  "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 6f70cad..b750ef1 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -33,16 +33,13 @@
 #include "blacklist.h"
 #include "bss.h"
 #include "scan.h"
+#include "ssid.h"
 #include "ap.h"
 #include "p2p/p2p.h"
 #include "p2p_supplicant.h"
 #include "wps_supplicant.h"
 
 
-#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
-#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
-#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
-
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
 
@@ -1201,67 +1198,19 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
 			    struct wpa_ssid *ssid, struct wpa_scan_res *bss)
 {
 	struct wpabuf *wps_ie;
+	int ret;
 
 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
 		return -1;
 
 	wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
-	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
-		if (!wps_ie) {
-			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
-			return 0;
-		}
-
-		if (!wps_is_selected_pbc_registrar(wps_ie)) {
-			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
-				   "without active PBC Registrar");
-			wpabuf_free(wps_ie);
-			return 0;
-		}
-
-		/* TODO: overlap detection */
-		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
-			   "(Active PBC)");
-		wpabuf_free(wps_ie);
-		return 1;
-	}
-
-	if (eap_is_wps_pin_enrollee(&ssid->eap)) {
-		if (!wps_ie) {
-			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
-			return 0;
-		}
 
-		/*
-		 * Start with WPS APs that advertise our address as an
-		 * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
-		 * allow any WPS AP after couple of scans since some APs do not
-		 * set Selected Registrar attribute properly when using
-		 * external Registrar.
-		 */
-		if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
-			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
-				wpa_printf(MSG_DEBUG, "   skip - WPS AP "
-					   "without active PIN Registrar");
-				wpabuf_free(wps_ie);
-				return 0;
-			}
-			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
-		} else {
-			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
-				   "(Authorized MAC or Active PIN)");
-		}
-		wpabuf_free(wps_ie);
-		return 1;
-	}
+	ret = wpa_ssid_match_wps(wpa_s, ssid, wps_ie);
 
-	if (wps_ie) {
-		wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+	if (wps_ie)
 		wpabuf_free(wps_ie);
-		return 1;
-	}
 
-	return -1;
+	return ret;
 }
 
 
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index b38c091..c42b2b2 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -22,6 +22,10 @@ struct wpa_scan_res;
 #include "wps/wps.h"
 #include "wps/wps_defs.h"
 
+#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
+#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
+#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
+
 struct wpa_bss;
 
 struct wps_new_ap_settings {
-- 
1.7.0.4



More information about the HostAP mailing list