diff mbox series

bss: enable discovery of SSID for known OWE BSS

Message ID 20241008011404.68281-1-mail@david-bauer.net
State Changes Requested
Headers show
Series bss: enable discovery of SSID for known OWE BSS | expand

Commit Message

David Bauer Oct. 8, 2024, 1:14 a.m. UTC
OWE BSSIDs are beaconed with a hidden SSID set, including them with a
hidden SSID in the scan_results when not explicitly proed for.

Currently wpa_supplicant creates suplicate entries for these BSSIDs in
the scan-list instead of updating the hidden results with the learnt
SSID when probing on a connection for OWE networks.

Update existing entries with hidden SSID instead. Also update entries
with learnt SSID when receiving a beacon frame for this SSID.

This is required to consistently fix roaming when connected to an OWE
network.

Signed-off-by: David Bauer <mail@david-bauer.net>
---
 wpa_supplicant/bss.c | 76 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 74 insertions(+), 2 deletions(-)

Comments

Jouni Malinen Dec. 26, 2024, 5:42 p.m. UTC | #1
On Tue, Oct 08, 2024 at 03:14:03AM +0200, David Bauer wrote:
> OWE BSSIDs are beaconed with a hidden SSID set, including them with a
> hidden SSID in the scan_results when not explicitly proed for.
> 
> Currently wpa_supplicant creates suplicate entries for these BSSIDs in
> the scan-list instead of updating the hidden results with the learnt
> SSID when probing on a connection for OWE networks.
> 
> Update existing entries with hidden SSID instead. Also update entries
> with learnt SSID when receiving a beacon frame for this SSID.
> 
> This is required to consistently fix roaming when connected to an OWE
> network.

Would you be able to share a debug log showing a case which does not
work without this patch but works with the patch?

I'm not exactly fond of this type of changes as an OWE specific change
since none of the changes here are really using any information that
would be different for OWE transition mode than any other use of hidden
SSIDs. Furthermore, I do not think it is really correct to modify an
already adding BSS entry (i.e., replacing the zero length SSID with a
specific SSID) since those modified entries could result in unexpected
behavior for external programs.

> diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
> @@ -938,7 +941,76 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,

> -	bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
> +#ifdef CONFIG_OWE
> +	rsn = wpa_scan_get_ie(res, WLAN_EID_RSN);
> +	owe = wpa_scan_get_vendor_ie(res, OWE_IE_VENDOR_TYPE);
> +
> +	if (owe && rsn) {
> +		struct wpa_bss *owe_tm_bss;
> +
> +		/*
> +		 * OWE Transition networks shall be handled in two ways:
> +		 * 1. Check if we have the network in our scan table
> +		 * 2.a Scan-Table entry has known SSID, current result has none
> +		 *     --> Abort
> +		 * 2.b Scan-Table entry has no known SSID, current Result has one
> +		 *     --> Update the scan-table entry
> +		 */
> +
> +		if (ssid[1] != 0 && ssid[2] != 0) {

What is that ssid[2] != 0 about? SSID is an arbitrary octet string of
1..32 octets and even 0x00 is a valid octet.. Sure, it is not commonly
used, but still, it is valid as far as the IEEE 802.11 standard is
concerned.

> +			/* Current result has SSID */
> +
> +			/* Check if we have a potential network with missing to update */
> +			dl_list_for_each(owe_tm_bss, &wpa_s->bss, struct wpa_bss, list) {
> +				if (!ether_addr_equal(owe_tm_bss->bssid, res->bssid))
> +					continue;
> +
> +				/* Need to be encrypted transition SSID*/
> +				if (!wpa_bss_get_ie(owe_tm_bss, WLAN_EID_RSN))
> +					continue;
> +
> +				if (!wpa_bss_get_vendor_ie(owe_tm_bss, OWE_IE_VENDOR_TYPE))
> +					continue;
> +
> +				if (owe_tm_bss->ssid_len != 0 && owe_tm_bss->ssid[0] != 0)
> +					continue;
> +
> +				/* We have a network to update */
> +				owe_tm_bss->ssid_len = ssid[1];
> +				os_memcpy(owe_tm_bss->ssid, ssid + 2, ssid[1]);
> +				break;

This is something I would prefer not to do, i.e., I would rather
maintain two BSS entries for any BSS that uses hidden SSIDs (regardless
of whether it is for OWE transition mode).

> +		} else {
> +			/* Current result lacks SSID */
> +
> +			/* Check if we've learnt an SSID for said network */
> +			dl_list_for_each(owe_tm_bss, &wpa_s->bss, struct wpa_bss, list) {
> +				if (!ether_addr_equal(owe_tm_bss->bssid, res->bssid))
> +					continue;
> +
> +				/* Need to be encrypted transition SSID*/
> +				if (!wpa_bss_get_ie(owe_tm_bss, WLAN_EID_RSN))
> +					continue;
> +
> +				if (!wpa_bss_get_vendor_ie(owe_tm_bss, OWE_IE_VENDOR_TYPE))
> +					continue;
> +
> +				if (owe_tm_bss->ssid_len == 0)
> +					continue;
> +
> +				/* Transition network is stored with SSID in scan-results.
> +				 * Continue with this result.
> +				 */
> +				bss = owe_tm_bss;
> +				break;

None of this really seems to be specific to OWE transition mode.. The
best approach for handling new scan results for entries that have no
SSID might be to iterate over all wpa_supplicant BSS entries and update
them if they have the same BSSID regardless of their SSID. Even better
would be to do that only for the entries that do not have a matching
SSID entry in the same scan update round (i.e., update the local BSS
entry with the same BSSID if that entry has ssid_len == 0 or if the
current set of scan results did not have an entry for the BSSID,SSID
tuple that is in in the current BSS but BSSID matches).
diff mbox series

Patch

diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 39de8bac3..8f4b3ed79 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -884,7 +884,10 @@  void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct os_reltime *fetch_time)
 {
 	const u8 *ssid, *p2p, *mesh;
-	struct wpa_bss *bss;
+#ifdef CONFIG_OWE
+	const u8 *owe, *rsn;
+#endif
+	struct wpa_bss *bss = NULL;
 
 	if (wpa_s->conf->ignore_old_scan_res) {
 		struct os_reltime update;
@@ -938,7 +941,76 @@  void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 	if (mesh && mesh[1] <= SSID_MAX_LEN)
 		ssid = mesh;
 
-	bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
+#ifdef CONFIG_OWE
+	rsn = wpa_scan_get_ie(res, WLAN_EID_RSN);
+	owe = wpa_scan_get_vendor_ie(res, OWE_IE_VENDOR_TYPE);
+
+	if (owe && rsn) {
+		struct wpa_bss *owe_tm_bss;
+
+		/*
+		 * OWE Transition networks shall be handled in two ways:
+		 * 1. Check if we have the network in our scan table
+		 * 2.a Scan-Table entry has known SSID, current result has none
+		 *     --> Abort
+		 * 2.b Scan-Table entry has no known SSID, current Result has one
+		 *     --> Update the scan-table entry
+		 */
+
+		if (ssid[1] != 0 && ssid[2] != 0) {
+			/* Current result has SSID */
+
+			/* Check if we have a potential network with missing to update */
+			dl_list_for_each(owe_tm_bss, &wpa_s->bss, struct wpa_bss, list) {
+				if (!ether_addr_equal(owe_tm_bss->bssid, res->bssid))
+					continue;
+
+				/* Need to be encrypted transition SSID*/
+				if (!wpa_bss_get_ie(owe_tm_bss, WLAN_EID_RSN))
+					continue;
+
+				if (!wpa_bss_get_vendor_ie(owe_tm_bss, OWE_IE_VENDOR_TYPE))
+					continue;
+
+				if (owe_tm_bss->ssid_len != 0 && owe_tm_bss->ssid[0] != 0)
+					continue;
+
+				/* We have a network to update */
+				owe_tm_bss->ssid_len = ssid[1];
+				os_memcpy(owe_tm_bss->ssid, ssid + 2, ssid[1]);
+				break;
+			}
+		} else {
+			/* Current result lacks SSID */
+
+			/* Check if we've learnt an SSID for said network */
+			dl_list_for_each(owe_tm_bss, &wpa_s->bss, struct wpa_bss, list) {
+				if (!ether_addr_equal(owe_tm_bss->bssid, res->bssid))
+					continue;
+
+				/* Need to be encrypted transition SSID*/
+				if (!wpa_bss_get_ie(owe_tm_bss, WLAN_EID_RSN))
+					continue;
+
+				if (!wpa_bss_get_vendor_ie(owe_tm_bss, OWE_IE_VENDOR_TYPE))
+					continue;
+
+				if (owe_tm_bss->ssid_len == 0)
+					continue;
+
+				/* Transition network is stored with SSID in scan-results.
+				 * Continue with this result.
+				 */
+				bss = owe_tm_bss;
+				break;
+			}
+		}
+	}
+#endif
+
+	if (bss == NULL)
+		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 {