Wireless Daemon for Linux
 help / color / mirror / Atom feed
* [PATCH v8] netdev: use wiphy radio work queue for connections
@ 2020-07-15 21:29 James Prestwood
  2020-07-15 22:12 ` Denis Kenzior
  0 siblings, 1 reply; 2+ messages in thread
From: James Prestwood @ 2020-07-15 21:29 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 12988 bytes --]

This adds connection/FT attempts to the radio work queue. This
will ensure that connections aren't delayed or done concurrently
with scanning.
---
 src/netdev.c  | 191 ++++++++++++++++++++++++++++++++++++--------------
 src/station.c |   8 +--
 2 files changed, 141 insertions(+), 58 deletions(-)

v8:
 - Put auth-proto/normal connects back together as this prevented SAE
   from randomizing the address
 - Separated out ft work into its own worker function

diff --git a/src/netdev.c b/src/netdev.c
index e469fb1c..9e2690d3 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -136,6 +136,9 @@ struct netdev {
 
 	struct l_io *pae_io;  /* for drivers without EAPoL over NL80211 */
 
+	struct l_genl_msg *connect_cmd;
+	struct wiphy_radio_work_item work;
+
 	bool connected : 1;
 	bool operational : 1;
 	bool rekey_offload_support : 1;
@@ -485,6 +488,9 @@ static void netdev_preauth_destroy(void *data)
 
 static void netdev_connect_free(struct netdev *netdev)
 {
+	if (netdev->work.id)
+		wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
+
 	if (netdev->sm) {
 		eapol_sm_free(netdev->sm);
 		netdev->sm = NULL;
@@ -530,6 +536,11 @@ static void netdev_connect_free(struct netdev *netdev)
 	netdev->ignore_connect_event = false;
 	netdev->expect_connect_failure = false;
 
+	if (netdev->connect_cmd) {
+		l_genl_msg_unref(netdev->connect_cmd);
+		netdev->connect_cmd = NULL;
+	}
+
 	netdev_rssi_polling_update(netdev);
 
 	if (netdev->connect_cmd_id) {
@@ -1019,6 +1030,9 @@ static void netdev_connect_ok(struct netdev *netdev)
 	}
 
 	netdev_rssi_polling_update(netdev);
+
+	if (netdev->work.id)
+		wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
 }
 
 static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
@@ -1487,6 +1501,9 @@ void netdev_handshake_failed(struct handshake_state *hs, uint16_t reason_code)
 		if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
 			l_error("error sending DEL_STATION");
 	}
+
+	if (netdev->work.id)
+		wiphy_radio_work_done(netdev->wiphy, netdev->work.id);
 }
 
 static void hardware_rekey_cb(struct l_genl_msg *msg, void *data)
@@ -2421,32 +2438,48 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
 
 struct rtnl_data {
 	struct netdev *netdev;
-	struct l_genl_msg *cmd_connect;
 	uint8_t addr[ETH_ALEN];
 	int ref;
 };
 
-static int netdev_begin_connection(struct netdev *netdev,
-					struct l_genl_msg *cmd_connect)
+static int netdev_begin_connection(struct netdev *netdev)
 {
-	if (cmd_connect) {
+	if (netdev->connect_cmd) {
 		netdev->connect_cmd_id = l_genl_family_send(nl80211,
-					cmd_connect, netdev_cmd_connect_cb,
-					netdev, NULL);
+						netdev->connect_cmd,
+						netdev_cmd_connect_cb, netdev,
+						NULL);
 
-		if (!netdev->connect_cmd_id) {
-			l_genl_msg_unref(cmd_connect);
-			return -EIO;
-		}
+		if (!netdev->connect_cmd_id)
+			goto failed;
+
+		netdev->connect_cmd = NULL;
 	}
 
+	/*
+	 * Set the supplicant address now, this may have already been done for
+	 * a non-randomized address connect, but if we are randomizing we need
+	 * to set it again as the address should have now changed.
+	 */
 	handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
 
-	/* set connected since the auth protocols cannot do so internally */
-	if (netdev->ap && auth_proto_start(netdev->ap))
+	if (netdev->ap) {
+		if (!auth_proto_start(netdev->ap))
+			goto failed;
+
+		/*
+		 * set connected since the auth protocols cannot do
+		 * so internally
+		 */
 		netdev->connected = true;
+	}
 
 	return 0;
+
+failed:
+	netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+					MMPDU_STATUS_CODE_UNSPECIFIED);
+	return -EIO;
 }
 
 static void netdev_mac_change_failed(struct netdev *netdev,
@@ -2461,8 +2494,6 @@ static void netdev_mac_change_failed(struct netdev *netdev,
 	 * mac_change_cmd_id was set.
 	 */
 	if (!netdev_get_is_up(netdev)) {
-		l_genl_msg_unref(req->cmd_connect);
-
 		WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
 				netdev, NETDEV_WATCH_EVENT_DOWN);
 
@@ -2474,7 +2505,7 @@ static void netdev_mac_change_failed(struct netdev *netdev,
 		 */
 		l_info("Interface still up after failing to change the MAC, "
 			"continuing with connection");
-		if (netdev_begin_connection(netdev, req->cmd_connect) < 0)
+		if (netdev_begin_connection(netdev) < 0)
 			goto failed;
 
 		return;
@@ -2517,7 +2548,7 @@ static void netdev_mac_power_up_cb(int error, uint16_t type,
 	/*
 	 * Pick up where we left off in netdev_connect_commmon.
 	 */
-	if (netdev_begin_connection(netdev, req->cmd_connect) < 0) {
+	if (netdev_begin_connection(netdev) < 0) {
 		l_error("Failed to connect after changing MAC");
 		netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
 				MMPDU_STATUS_CODE_UNSPECIFIED);
@@ -2576,9 +2607,7 @@ static void netdev_mac_power_down_cb(int error, uint16_t type,
  * Returns -EALREADY if the requested MAC matched our current MAC
  * Returns -EIO if there was an IO error when powering down
  */
-static int netdev_start_powered_mac_change(struct netdev *netdev,
-					struct scan_bss *bss,
-					struct l_genl_msg *cmd_connect)
+static int netdev_start_powered_mac_change(struct netdev *netdev)
 {
 	struct rtnl_data *req;
 	uint8_t new_addr[6];
@@ -2586,8 +2615,8 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
 	/* No address set in handshake, use per-network MAC generation */
 	if (util_mem_is_zero(netdev->handshake->spa, ETH_ALEN))
 		wiphy_generate_address_from_ssid(netdev->wiphy,
-						(const char *)bss->ssid,
-						new_addr);
+					(const char *)netdev->handshake->ssid,
+					new_addr);
 	else
 		memcpy(new_addr, netdev->handshake->spa, ETH_ALEN);
 
@@ -2600,7 +2629,6 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
 	req = l_new(struct rtnl_data, 1);
 	req->netdev = netdev;
 	/* This message will need to be unreffed upon any error */
-	req->cmd_connect = cmd_connect;
 	req->ref++;
 	memcpy(req->addr, new_addr, sizeof(req->addr));
 
@@ -2609,7 +2637,6 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
 					req, netdev_mac_destroy);
 
 	if (!netdev->mac_change_cmd_id) {
-		l_genl_msg_unref(req->cmd_connect);
 		l_free(req);
 
 		return -EIO;
@@ -2618,6 +2645,35 @@ static int netdev_start_powered_mac_change(struct netdev *netdev,
 	return 0;
 }
 
+static bool netdev_connection_work_ready(struct wiphy_radio_work_item *item)
+{
+	struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+	if (mac_per_ssid) {
+		int ret = netdev_start_powered_mac_change(netdev);
+
+		if (!ret)
+			return false;
+		else if (ret != -EALREADY)
+			goto failed;
+	}
+
+	if (netdev_begin_connection(netdev) < 0)
+		goto failed;
+
+	return false;
+
+failed:
+	netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+				MMPDU_STATUS_CODE_UNSPECIFIED);
+
+	return true;
+}
+
+static const struct wiphy_radio_work_item_ops connect_work_ops = {
+	.do_work = netdev_connection_work_ready,
+};
+
 static int netdev_connect_common(struct netdev *netdev,
 					struct l_genl_msg *cmd_connect,
 					struct scan_bss *bss,
@@ -2626,6 +2682,7 @@ static int netdev_connect_common(struct netdev *netdev,
 					netdev_event_func_t event_filter,
 					netdev_connect_cb_t cb, void *user_data)
 {
+	netdev->connect_cmd = cmd_connect;
 	netdev->event_filter = event_filter;
 	netdev->connect_cb = cb;
 	netdev->user_data = user_data;
@@ -2642,14 +2699,9 @@ static int netdev_connect_common(struct netdev *netdev,
 					NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
 		handshake_state_set_no_rekey(hs, true);
 
-	if (mac_per_ssid) {
-		int ret = netdev_start_powered_mac_change(netdev, bss,
-								cmd_connect);
-		if (ret != -EALREADY)
-			return ret;
-	}
-
-	return netdev_begin_connection(netdev, cmd_connect);
+	wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+				&connect_work_ops);
+	return 0;
 }
 
 int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
@@ -2667,7 +2719,7 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
 			netdev->type != NL80211_IFTYPE_P2P_CLIENT)
 		return -ENOTSUP;
 
-	if (netdev->connected || netdev->connect_cmd_id)
+	if (netdev->connected || netdev->connect_cmd_id || netdev->work.id)
 		return -EISCONN;
 
 	switch (hs->akm_suite) {
@@ -2709,23 +2761,36 @@ int netdev_disconnect(struct netdev *netdev,
 				netdev_disconnect_cb_t cb, void *user_data)
 {
 	struct l_genl_msg *disconnect;
+	bool send_disconnect = true;
 
 	if (netdev->type != NL80211_IFTYPE_STATION &&
 			netdev->type != NL80211_IFTYPE_P2P_CLIENT)
 		return -ENOTSUP;
 
-	if (!netdev->connected)
-		return -ENOTCONN;
-
 	if (netdev->disconnect_cmd_id)
 		return -EINPROGRESS;
 
 	/* Only perform this if we haven't successfully fully associated yet */
 	if (!netdev->operational) {
+		/*
+		 * Three possibilities here:
+		 * 1. We do not actually have a connect in progress (work.id
+		 *    is zero), then we can bail out early with an error.
+		 * 2. We have sent CMD_CONNECT but not fully connected. The
+		 *    CMD_CONNECT needs to be canceled and a disconnect should
+		 *    be sent.
+		 * 3. Queued up the connect work, but haven't sent CMD_CONNECT
+		 *    to the kernel. This case we do not need to send a
+		 *    disconnect.
+		 */
+		if (!netdev->work.id)
+			return -ENOTCONN;
+
 		if (netdev->connect_cmd_id) {
 			l_genl_family_cancel(nl80211, netdev->connect_cmd_id);
 			netdev->connect_cmd_id = 0;
-		}
+		} else
+			send_disconnect = false;
 
 		netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
 					MMPDU_REASON_CODE_UNSPECIFIED);
@@ -2733,19 +2798,23 @@ int netdev_disconnect(struct netdev *netdev,
 		netdev_connect_free(netdev);
 	}
 
-	disconnect = netdev_build_cmd_disconnect(netdev,
+	if (send_disconnect) {
+		disconnect = netdev_build_cmd_disconnect(netdev,
 					MMPDU_REASON_CODE_DEAUTH_LEAVING);
-	netdev->disconnect_cmd_id = l_genl_family_send(nl80211, disconnect,
-				netdev_cmd_disconnect_cb, netdev, NULL);
+		netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
+					disconnect, netdev_cmd_disconnect_cb,
+					netdev, NULL);
 
-	if (!netdev->disconnect_cmd_id) {
-		l_genl_msg_unref(disconnect);
-		return -EIO;
-	}
+		if (!netdev->disconnect_cmd_id) {
+			l_genl_msg_unref(disconnect);
+			return -EIO;
+		}
 
-	netdev->disconnect_cb = cb;
-	netdev->user_data = user_data;
-	netdev->aborting = true;
+		netdev->disconnect_cb = cb;
+		netdev->user_data = user_data;
+		netdev->aborting = true;
+	} else if (cb)
+		cb(netdev, true, user_data);
 
 	return 0;
 }
@@ -3087,12 +3156,31 @@ static void netdev_ft_over_ds_tx_authenticate(struct iovec *iov,
 					netdev_ft_request_cb);
 }
 
+static bool netdev_ft_work_ready(struct wiphy_radio_work_item *item)
+{
+	struct netdev *netdev = l_container_of(item, struct netdev, work);
+
+	if (!auth_proto_start(netdev->ap)) {
+		/* Restore original nonce */
+		memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
+
+		netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+						MMPDU_STATUS_CODE_UNSPECIFIED);
+		return true;
+	}
+
+	return false;
+}
+
+static const struct wiphy_radio_work_item_ops ft_work_ops = {
+	.do_work = netdev_ft_work_ready,
+};
+
 static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
 				bool over_air,
 				netdev_connect_cb_t cb)
 {
 	struct netdev_handshake_state *nhs;
-	int err = -EINVAL;
 
 	if (!netdev->operational)
 		return -ENOTCONN;
@@ -3173,15 +3261,10 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
 					netdev_ft_over_ds_tx_authenticate,
 					netdev_ft_tx_associate, netdev);
 
-	if (!auth_proto_start(netdev->ap))
-		goto restore_snonce;
+	wiphy_radio_work_insert(netdev->wiphy, &netdev->work, 1,
+				&ft_work_ops);
 
 	return 0;
-
-restore_snonce:
-	memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
-
-	return err;
 }
 
 int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
diff --git a/src/station.c b/src/station.c
index 35f4a96d..0095d6c8 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2702,10 +2702,6 @@ int station_disconnect(struct station *station)
 	if (!station->connected_bss)
 		return -ENOTCONN;
 
-	if (netdev_disconnect(station->netdev,
-					station_disconnect_cb, station) < 0)
-		return -EIO;
-
 	if (station->netconfig)
 		netconfig_reset(station->netconfig);
 
@@ -2718,6 +2714,10 @@ int station_disconnect(struct station *station)
 
 	station_enter_state(station, STATION_STATE_DISCONNECTING);
 
+	if (netdev_disconnect(station->netdev,
+					station_disconnect_cb, station) < 0)
+		return -EIO;
+
 	return 0;
 }
 
-- 
2.21.1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v8] netdev: use wiphy radio work queue for connections
  2020-07-15 21:29 [PATCH v8] netdev: use wiphy radio work queue for connections James Prestwood
@ 2020-07-15 22:12 ` Denis Kenzior
  0 siblings, 0 replies; 2+ messages in thread
From: Denis Kenzior @ 2020-07-15 22:12 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 601 bytes --]

Hi James,

On 7/15/20 4:29 PM, James Prestwood wrote:
> This adds connection/FT attempts to the radio work queue. This
> will ensure that connections aren't delayed or done concurrently
> with scanning.
> ---
>   src/netdev.c  | 191 ++++++++++++++++++++++++++++++++++++--------------
>   src/station.c |   8 +--
>   2 files changed, 141 insertions(+), 58 deletions(-)
> 
> v8:
>   - Put auth-proto/normal connects back together as this prevented SAE
>     from randomizing the address
>   - Separated out ft work into its own worker function
> 

Applied, thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-07-15 22:12 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-07-15 21:29 [PATCH v8] netdev: use wiphy radio work queue for connections James Prestwood
2020-07-15 22:12 ` Denis Kenzior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox