Wireless Daemon for Linux
 help / color / mirror / Atom feed
From: James Prestwood <prestwoj at gmail.com>
To: iwd at lists.01.org
Subject: [PATCH 07/10] dpp: support retransmitting frames with no ACK
Date: Tue, 11 Jan 2022 16:55:55 -0800	[thread overview]
Message-ID: <20220112005558.1158405-7-prestwoj@gmail.com> (raw)
In-Reply-To: 20220112005558.1158405-1-prestwoj@gmail.com

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

The DPP spec says nothing about how to handle re-transmits but it
was found in testing this can happen relatively easily for a few
reasons.

If the configurator requests a channel switch but does not get onto
the new channel quick enough the enrollee may have already sent the
authenticate response and it was missed. Also by nature of how the
kernel goes offchannel there are moments in time between ROC when
the card is idle and not receiving any frames.

Only frames where there was no ACK will be retransmitted. If the
peer received the frame and dropped it resending the same frame wont
do any good.
---
 src/dpp.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/src/dpp.c b/src/dpp.c
index c22788e7..a4cc315b 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -52,6 +52,9 @@
 #include "src/scan.h"
 #include "src/network.h"
 #include "src/handshake.h"
+#include "src/nl80211util.h"
+
+#define DPP_FRAME_MAX_RETRIES 5
 
 static uint32_t netdev_watch;
 static struct l_genl_family *nl80211;
@@ -118,6 +121,9 @@ struct dpp_sm {
 
 	struct dpp_configuration *config;
 	uint32_t connect_scan_id;
+	uint64_t frame_cookie;
+	uint32_t mlme_watch;
+	uint8_t frame_retry;
 
 	struct l_dbus_message *message;
 };
@@ -174,6 +180,7 @@ static void dpp_reset(struct dpp_sm *dpp)
 
 	dpp->state = DPP_STATE_NOTHING;
 	dpp->new_freq = 0;
+	dpp->frame_retry = 0;
 
 	explicit_bzero(dpp->r_nonce, dpp->nonce_len);
 	explicit_bzero(dpp->i_nonce, dpp->nonce_len);
@@ -204,13 +211,26 @@ static void dpp_free(struct dpp_sm *dpp)
 		dpp->boot_private = NULL;
 	}
 
+	if (dpp->mlme_watch) {
+		l_genl_family_unregister(nl80211, dpp->mlme_watch);
+		dpp->mlme_watch = 0;
+	}
+
 	l_free(dpp);
 }
 
 static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data)
 {
-	if (l_genl_msg_get_error(msg) < 0)
+	struct dpp_sm *dpp = user_data;
+
+	if (l_genl_msg_get_error(msg) < 0) {
 		l_error("Error sending frame");
+		return;
+	}
+
+	if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &dpp->frame_cookie,
+				NL80211_ATTR_UNSPEC) < 0)
+		l_error("Error parsing frame cookie");
 }
 
 static void dpp_send_frame(struct dpp_sm *dpp, struct iovec *iov, size_t iov_len,
@@ -1575,6 +1595,76 @@ static void dpp_handle_frame(const struct mmpdu_header *frame,
 	}
 }
 
+static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
+{
+	struct dpp_sm *dpp = user_data;
+	uint64_t wdev_id = 0;
+	uint64_t cookie = 0;
+	bool ack = false;
+	struct l_genl_attr attr;
+	const void *data;
+	uint16_t len;
+	uint16_t type;
+	const void *frame = NULL;
+	uint16_t frame_len = 0;
+	struct iovec iov;
+	uint8_t cmd = l_genl_msg_get_command(msg);
+
+	if (cmd != NL80211_CMD_FRAME_TX_STATUS)
+		return;
+
+	if (dpp->state <= DPP_STATE_PRESENCE)
+		return;
+
+	l_genl_attr_init(&attr, msg);
+
+	while (l_genl_attr_next(&attr, &type, &len, &data)) {
+		switch (type) {
+		case NL80211_ATTR_WDEV:
+			if (len != 8)
+				return;
+
+			wdev_id = l_get_u64(data);
+			break;
+		case NL80211_ATTR_COOKIE:
+			if (len != 8)
+				return;
+
+			cookie = l_get_u64(data);
+			break;
+		case NL80211_ATTR_ACK:
+			ack = true;
+			break;
+		case NL80211_ATTR_FRAME:
+			frame = data;
+			frame_len = len;
+			break;
+		}
+	}
+
+	/*
+	 * Only want to handle the no-ACK case. Re-transmitting an ACKed
+	 * frame likely wont do any good, at least in the case of DPP.
+	 */
+	if (dpp->wdev_id != wdev_id || dpp->frame_cookie != cookie ||
+				ack || !frame)
+		return;
+
+	if (dpp->frame_retry > DPP_FRAME_MAX_RETRIES) {
+		dpp_reset(dpp);
+		return;
+	}
+
+	iov.iov_base = (void *)frame;
+	iov.iov_len = frame_len;
+
+	l_debug("No ACK from peer, re-transmitting");
+
+	dpp->frame_retry++;
+
+	dpp_send_frame(dpp, &iov, 1, dpp->current_freq);
+}
+
 static void dpp_create(struct netdev *netdev)
 {
 	struct l_dbus *dbus = dbus_get_bus();
@@ -1612,6 +1702,9 @@ static void dpp_create(struct netdev *netdev)
 				dpp_conf_request_prefix,
 				sizeof(dpp_conf_request_prefix),
 				dpp_handle_config_request_frame, dpp, NULL);
+
+	dpp->mlme_watch = l_genl_family_register(nl80211, "mlme",
+						dpp_mlme_notify, dpp, NULL);
 }
 
 static void dpp_netdev_watch(struct netdev *netdev,
-- 
2.31.1

             reply	other threads:[~2022-01-12  0:55 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-12  0:55 James Prestwood [this message]
  -- strict thread matches above, loose matches on Subject: below --
2022-01-12 16:04 [PATCH 07/10] dpp: support retransmitting frames with no ACK Denis Kenzior
2022-01-12 17:09 James Prestwood

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220112005558.1158405-7-prestwoj@gmail.com \
    --to=iwd@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox