All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 07/10] dpp: support retransmitting frames with no ACK
@ 2022-01-12 17:09 James Prestwood
  0 siblings, 0 replies; 3+ messages in thread
From: James Prestwood @ 2022-01-12 17:09 UTC (permalink / raw)
  To: iwd

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

On Wed, 2022-01-12 at 10:04 -0600, Denis Kenzior wrote:
> Hi James,
> 
> On 1/11/22 18:55, James Prestwood wrote:
> > 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(-)
> > 
> 
> <snip>
> 
> > @@ -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;
> > +       }
> > +
> 
> Might be glass empty vs glass full thing, but wonder if registering
> the mlme 
> watch for each dpp object is more complicated than just doing it at
> the module 
> level (dpp_init?).  I guess you did it this way because we do not
> have a 
> dpp_find() equivalent...

Yep, that was why.

> 
> In theory it would probably be more efficient to parse the message
> once and run 
> dpp_find than having each dpp instance run the iterator.

But yes, I hadn't thought about each instance having to parse out the
wdev, I can just add dpp_find instead.

> 
> Alternatively, maybe l_genl_family_register should be done only when
> DPP 
> actually starts instead of at creation time?
> 
> Also, do we need to reset the cookie in dpp_reset or so?  Maybe it
> doesn't matter.
> 

Only would matter if cookies are reused or counted per-wdev but it
doesn't hurt to zero it out.

> 
> >         l_free(dpp);
> >   }
> >   
> 
> <snip>
> 
> > @@ -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;
> > +               }
> > +       }
> > +
> 
> This may look nicer with nl80211_parse_attrs.  At least for
> cookie/wdev/ack. 
> And avoids some of the silent returns you have when the length
> doesn't match 
> what is expected.
> 
> > +       /*
> > +        * 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;
> > +       }
> 
> That way you can check these before even needing the frame.
> 
> > +
> > +       iov.iov_base = (void *)frame;
> > +       iov.iov_len = frame_len;
> > +
> 
> Actually, it occurs to me that we could add ATTR_FRAME support to 
> nl80211_parse_attrs by using the struct iovec for it.

Yeah I'll do this instead. I played around with passing data/length but
I always forget about iovec's.

> 
> > +       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();
> 
> Regards,
> -Denis


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

* Re: [PATCH 07/10] dpp: support retransmitting frames with no ACK
@ 2022-01-12 16:04 Denis Kenzior
  0 siblings, 0 replies; 3+ messages in thread
From: Denis Kenzior @ 2022-01-12 16:04 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 1/11/22 18:55, James Prestwood wrote:
> 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(-)
> 

<snip>

> @@ -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;
> +	}
> +

Might be glass empty vs glass full thing, but wonder if registering the mlme 
watch for each dpp object is more complicated than just doing it at the module 
level (dpp_init?).  I guess you did it this way because we do not have a 
dpp_find() equivalent...

In theory it would probably be more efficient to parse the message once and run 
dpp_find than having each dpp instance run the iterator.

Alternatively, maybe l_genl_family_register should be done only when DPP 
actually starts instead of at creation time?

Also, do we need to reset the cookie in dpp_reset or so?  Maybe it doesn't matter.

>   	l_free(dpp);
>   }
>   

<snip>

> @@ -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;
> +		}
> +	}
> +

This may look nicer with nl80211_parse_attrs.  At least for cookie/wdev/ack. 
And avoids some of the silent returns you have when the length doesn't match 
what is expected.

> +	/*
> +	 * 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;
> +	}

That way you can check these before even needing the frame.

> +
> +	iov.iov_base = (void *)frame;
> +	iov.iov_len = frame_len;
> +

Actually, it occurs to me that we could add ATTR_FRAME support to 
nl80211_parse_attrs by using the struct iovec for it.

> +	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();

Regards,
-Denis

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

* [PATCH 07/10] dpp: support retransmitting frames with no ACK
@ 2022-01-12  0:55 James Prestwood
  0 siblings, 0 replies; 3+ messages in thread
From: James Prestwood @ 2022-01-12  0:55 UTC (permalink / raw)
  To: iwd

[-- 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

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

end of thread, other threads:[~2022-01-12 17:09 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-12 17:09 [PATCH 07/10] dpp: support retransmitting frames with no ACK James Prestwood
  -- strict thread matches above, loose matches on Subject: below --
2022-01-12 16:04 Denis Kenzior
2022-01-12  0:55 James Prestwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.