Roaming on android blacklists incorrect bss

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jouni and all,

On Android M we've seen cases where Android's way of roaming sometimes end up blacklisting incorrect bss.

Connected to BSSID1 to roam to another AP the following sequence of commands are used:
SET_NETWORK 0 bssid <BSSID2>
ENABLE_NETWORK 0
REASSOCIATE

Most of the time this works just fine, though if authentication timer times out (probably due to auth/assoc/eapol packet loss) the BSSID roamed away from gets blacklisted (BSSID1), not the one failing to reassociate with (BSSID2).

Interesting lines from the log look like this:

wlan0: Considering connect request: reassociate: 1 selected: <BSSID2> bssid: <BBSID1> pending: 00:00:00:00:00:00 wpa_state: COMPLETED ssid=<SSID> current_ssid=<SSID>
wlan0: Request association with <BSSID2>
wlan0: Re-association to the same ESS
...
wlan0: Add radio work 'connect'@0x7f9769c230
wlan0: First radio work item in the queue - schedule start immediately
wlan0: Starting radio work 'connect'@0x7f9769c230 after 0.000144 second wait
wlan0: Trying to associate with SSID <SSID>
...
wlan0: State: COMPLETED -> ASSOCIATING
...
Limit connection to BSSID <BBSID2> freq=5180 MHz based on scan results (bssid_set=1)
...
nl80211: Connect (ifindex=6)
  * bssid=<BSSID2>
  * bssid_hint=<BSSID2>
...
nl80211: Connect request send successfully
wlan0: Setting authentication timeout: 10 sec 0 usec
...
wlan0: Authentication with <BSSID1> timed out.
Added BSSID <BSSID1> into blacklist
TDLS: Remove peers on disassociation
wlan0: WPA: Clear old PMK and PTK
wlan0: Request to deauthenticate - bssid=<BSSID1> pending_bssid=00:00:00:00:00:00 reason=3 state=ASSOCIATING

Question is, is this way of using the REASSOCIATE command to perform roam operation valid? I worked on a patch that solved this specific case but had to apply some hacks to reproduce it with hwsim tests. It would be great with some feedback on the scenario and attached patches. I think not all of them should really be applied but should help discussing the problem seen.

Thanks
Mikael Kanstrup



>From f6dca1dbe2d71ad7b4a0f18e82ba266e86aae758 Mon Sep 17 00:00:00 2001
From: Mikael Kanstrup <mikael.kanstrup@xxxxxxxxxxxxxx>
Date: Thu, 30 Jun 2016 12:32:55 +0200
Subject: [PATCH 1/5] nl80211: Add driver parameter force_bss_selection

Add driver parameter command to force capability flag
WPA_DRIVER_FLAGS_BSS_SELECTION even if driver states otherwise.
---
 src/drivers/driver_nl80211.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index c89665b..e6d67bc 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -6930,6 +6930,12 @@ static int nl80211_set_param(void *priv, const char *param)
 		drv->force_connect_cmd = 1;
 	}
 
+	if (os_strstr(param, "force_bss_selection=1")) {
+		struct i802_bss *bss = priv;
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+		drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+	}
+
 	if (os_strstr(param, "no_offchannel_tx=1")) {
 		struct i802_bss *bss = priv;
 		struct wpa_driver_nl80211_data *drv = bss->drv;
-- 
2.4.2

>From 850a3d8a4732beda8171e934760d1b8e66c3cafe Mon Sep 17 00:00:00 2001
From: Mikael Kanstrup <mikael.kanstrup@xxxxxxxxxxxxxx>
Date: Thu, 30 Jun 2016 12:37:33 +0200
Subject: [PATCH 2/5] wpa_supplicant: Support multiple driver parameters

Support multiple driver parameters for driver_param config option. The option
can now be a comma separated list of parameters.
---
 wpa_supplicant/main.c              |  2 +-
 wpa_supplicant/wpa_supplicant.c    | 38 +++++++++++++++++++++++++++++++++++++-
 wpa_supplicant/wpa_supplicant.conf |  2 +-
 3 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index e08c2fd..3524990 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -87,7 +87,7 @@ static void usage(void)
 	       "  -N = start describing new interface\n"
 	       "  -o = override driver parameter for new interfaces\n"
 	       "  -O = override ctrl_interface parameter for new interfaces\n"
-	       "  -p = driver parameters\n"
+	       "  -p = driver parameters (comma separated if multiple)\n"
 	       "  -P = PID file\n"
 	       "  -q = decrease debugging verbosity (-qq even less)\n"
 #ifdef CONFIG_DEBUG_SYSLOG
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 6999bbb..70dc07b 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -4505,6 +4505,42 @@ radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
 }
 
 
+static int wpas_driver_set_params(struct wpa_supplicant *wpa_s)
+{
+	int ret = 0;
+	size_t len;
+	const char *pos;
+	const char* driver_param = wpa_s->conf->driver_param;
+	char* param;
+
+	if (!driver_param)
+		return 0;
+	param = os_malloc(os_strlen(driver_param));
+	if (!param)
+		return -1;
+
+	do {
+		pos = os_strchr(driver_param, ',');
+		if (pos)
+			len = pos - driver_param;
+		else
+			len = os_strlen(driver_param);
+
+		os_memcpy(param, driver_param, len);
+		param[len] = '\0';
+
+		if (wpa_drv_set_param(wpa_s, param) < 0) {
+			ret = -1;
+			break;
+		}
+		driver_param = pos + 1;
+	} while (pos);
+
+	os_free(param);
+	return ret;
+}
+
+
 static int wpas_init_driver(struct wpa_supplicant *wpa_s,
 			    struct wpa_interface *iface)
 {
@@ -4529,7 +4565,7 @@ next_driver:
 			"interface");
 		return -1;
 	}
-	if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+	if (wpas_driver_set_params(wpa_s) < 0) {
 		wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
 			"driver_param '%s'", wpa_s->conf->driver_param);
 		return -1;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 1d86a71..c7f20d7 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -203,7 +203,7 @@ fast_reauth=1
 # This field can be used to configure arbitrary driver interace parameters. The
 # format is specific to the selected driver interface. This field is not used
 # in most cases.
-#driver_param="field=value"
+#driver_param="field=value,field2=value2"
 
 # Country code
 # The ISO/IEC alpha2 country code for the country in which this device is
-- 
2.4.2

>From 4e4a7f4bde92f3ef358615b7ab52b0449cd8a95b Mon Sep 17 00:00:00 2001
From: Mikael Kanstrup <mikael.kanstrup@xxxxxxxxxxxxxx>
Date: Wed, 29 Jun 2016 15:36:36 +0200
Subject: [PATCH 3/5] Implement IGNORE_AUTH_RESP control interface debug
 command

Implement IGNORE_AUTH_RESP command to simulate auth/assoc response loss
and eapol rx packet loss by ignoring corresponding incoming events.
---
 wpa_supplicant/ctrl_iface.c       | 15 +++++++++++++++
 wpa_supplicant/events.c           | 10 ++++++++++
 wpa_supplicant/wpa_cli.c          |  7 +++++++
 wpa_supplicant/wpa_supplicant.c   |  5 +++++
 wpa_supplicant/wpa_supplicant_i.h |  2 ++
 5 files changed, 39 insertions(+)

diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e75323d..f8c76c7 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2102,6 +2102,18 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpa_supplicant_ctrl_iface_ignore_auth_resp(struct wpa_supplicant *wpa_s,
+					    const char *params,
+					    char *buf, size_t buflen)
+{
+	int enable = atoi(params);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ignore_auth_resp enable=%d", enable);
+	wpa_s->ignore_auth_resp = enable;
+	os_memcpy(buf, "OK\n", 3);
+	return 3;
+}
+
+
 static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
 					   char *cmd)
 {
@@ -8689,6 +8701,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_status(
 			wpa_s, buf + 6, reply, reply_size);
+	} else if (os_strncmp(buf, "IGNORE_AUTH_RESP", 16) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_ignore_auth_resp(
+			wpa_s, buf + 16, reply, reply_size);
 	} else if (os_strcmp(buf, "PMKSA") == 0) {
 		reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index b7a3bc0..d5dc1a7 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3431,6 +3431,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		sme_event_auth(wpa_s, data);
 		break;
 	case EVENT_ASSOC:
+		if (wpa_s->ignore_auth_resp) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_ASSOC - ignore_auth_resp active!");
+			break;
+		}
 		wpa_supplicant_event_assoc(wpa_s, data);
 		if (data && data->assoc_info.authorized)
 			wpa_supplicant_event_assoc_auth(wpa_s, data);
@@ -3445,6 +3450,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 				    data ? &data->disassoc_info : NULL);
 		break;
 	case EVENT_DEAUTH:
+		if (wpa_s->ignore_auth_resp) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_DEAUTH - ignore_auth_resp active!");
+			break;
+		}
 		wpas_event_deauth(wpa_s,
 				  data ? &data->deauth_info : NULL);
 		break;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 53036ae..82c8e44 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -525,6 +525,10 @@ static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
 	return wpa_ctrl_command(ctrl, "STATUS");
 }
 
+static int wpa_cli_cmd_ignore_auth_resp(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "IGNORE_AUTH_RESP", 1, argc, argv);
+}
 
 static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -3477,6 +3481,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
 	{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
 	  cli_cmd_flag_none,
 	  "<interface type> = retrieve preferred freq list for the specified interface type" },
+	{ "ignore_auth_resp", wpa_cli_cmd_ignore_auth_resp, NULL,
+	  cli_cmd_flag_none,
+	  "<0|1> ignore received auth/assoc response and rx eapol packets" },
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 70dc07b..57881e8 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -3266,6 +3266,11 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
 	wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
+	if (wpa_s->ignore_auth_resp) {
+		wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!");
+		return;
+	}
+
 #ifdef CONFIG_PEERKEY
 	if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
 	    wpa_s->current_ssid->peerkey &&
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index e45f662..0a4c6e9 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1065,6 +1065,8 @@ struct wpa_supplicant {
 	 */
 	struct wpabuf *lci;
 	struct os_reltime lci_time;
+
+	int ignore_auth_resp;
 };
 
 
-- 
2.4.2

>From 0ddc036ab21b155a4e8a4552456220985723fae1 Mon Sep 17 00:00:00 2001
From: Mikael Kanstrup <mikael.kanstrup@xxxxxxxxxxxxxx>
Date: Thu, 30 Jun 2016 10:37:43 +0200
Subject: [PATCH 4/5] tests: Add testcase for roaming failure with reassoc and
 bssid_set

Add testcase that verifies that a failed roaming attempt performed with
reassociate command and bssid_set=1 blacklists the correct AP.
---
 tests/hwsim/test_ap_roam.py | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/tests/hwsim/test_ap_roam.py b/tests/hwsim/test_ap_roam.py
index 11d9755..6389ead 100644
--- a/tests/hwsim/test_ap_roam.py
+++ b/tests/hwsim/test_ap_roam.py
@@ -11,6 +11,7 @@ logger = logging.getLogger()
 
 import hwsim_utils
 import hostapd
+from wpasupplicant import WpaSupplicant
 
 @remote_compatible
 def test_ap_roam_open(dev, apdev):
@@ -61,6 +62,45 @@ def test_ap_roam_wpa2_psk(dev, apdev):
     dev[0].roam(apdev[0]['bssid'])
     hwsim_utils.test_connectivity(dev[0], hapd0)
 
+def get_blacklist(dev):
+    return dev.request("BLACKLIST").splitlines()
+
+def test_ap_roam_with_reassoc_wpa2_psk_failed(dev, apdev, params):
+    """Roam using reassoc between two WPA2-PSK APs"""
+    params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+    hapd0 = hostapd.add_ap(apdev[0], params)
+    bssid0 = hapd0.own_addr()
+
+    wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+    wpas.interface_add("wlan5", drv_params="force_connect_cmd=1,force_bss_selection=1")
+
+    id = wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+    hwsim_utils.test_connectivity(wpas, hapd0)
+
+    hapd1 = hostapd.add_ap(apdev[1], params)
+    bssid1 = hapd1.own_addr()
+    wpas.scan_for_bss(bssid1, freq=2412)
+
+    if "OK" not in wpas.request("SET_NETWORK " + str(id) + " bssid " + bssid1):
+        raise Exception("SET_NETWORK failed")
+    if "OK" not in wpas.request("IGNORE_AUTH_RESP 1"):
+        raise Exception("IGNORE_AUTH_RESP failed")
+    if "OK" not in wpas.request("REASSOCIATE"):
+        raise Exception("REASSOCIATE failed")
+
+    logger.info("Wait ~10s for auth timeout...")
+    ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], 12)
+    if not ev:
+        raise Exception("CTRL-EVENT-SCAN-STARTED not seen");
+
+    b = get_blacklist(wpas)
+    if bssid0 in b:
+	raise Exception("Unexpected blacklist contents: " + str(b))
+
+    if "OK" not in wpas.request("IGNORE_AUTH_RESP 0"):
+        raise Exception("IGNORE_AUTH_RESP failed")
+    wpas.wait_connected(timeout=5)
+
 def test_ap_roam_wpa2_psk_failed(dev, apdev, params):
     """Roam failure with WPA2-PSK AP due to wrong passphrase"""
     params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
-- 
2.4.2

>From bef2433754014e13143f10ed2a778c8bbba0a518 Mon Sep 17 00:00:00 2001
From: Mikael Kanstrup <mikael.kanstrup@xxxxxxxxxxxxxx>
Date: Wed, 29 Jun 2016 15:44:19 +0200
Subject: [PATCH 5/5] Blacklist correct bssid on auth timeout if bssid_set

If authentication times out while performing reassociate with
bssid_set=1 incorrect bssid end up being blacklisted. Use pending_bss
field on auth timeout and deauth to ensure correct AP get blacklisted.

Change-Id: I11eec4f5bf05c6512486307c5afae969cdde4e02
---
 wpa_supplicant/wpa_supplicant.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 57881e8..73316db 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -192,7 +192,7 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	const u8 *bssid = wpa_s->bssid;
-	if (is_zero_ether_addr(bssid))
+	if (!is_zero_ether_addr(wpa_s->pending_bssid))
 		bssid = wpa_s->pending_bssid;
 	wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
 		MAC2STR(bssid));
@@ -2156,7 +2156,10 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 	} else {
 		wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
 			wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
-		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+		if (bss && ssid->bssid_set)
+			os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+		else
+			os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 	}
 	if (!wpa_s->pno)
 		wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -2685,12 +2688,12 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 		MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
 		reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
 
-	if (!is_zero_ether_addr(wpa_s->bssid))
-		addr = wpa_s->bssid;
-	else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+	if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
 		 (wpa_s->wpa_state == WPA_AUTHENTICATING ||
 		  wpa_s->wpa_state == WPA_ASSOCIATING))
 		addr = wpa_s->pending_bssid;
+	else if (!is_zero_ether_addr(wpa_s->bssid))
+		addr = wpa_s->bssid;
 	else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
 		/*
 		 * When using driver-based BSS selection, we may not know the
-- 
2.4.2

_______________________________________________
Hostap mailing list
Hostap@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/hostap

[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux