All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] ANQP Refactor to use frame-xchg
@ 2020-06-02 17:30 James Prestwood
  2020-06-02 17:30 ` [PATCH 01/10] frame-xchg: fix bug when starting new xchg from callback James Prestwood
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

I have included two of Andrews patches fixing the group zero bug
as well.

Andrew Zaborowski (2):
  frame-xchg: Fix frame_watch_remove_by_handler for group 0
  frame-xchg: Use frame_watch_group_match in frame_watch_group_get

James Prestwood (8):
  frame-xchg: fix bug when starting new xchg from callback
  anqp: refactor to use frame-xchg
  station: add ANQP state watch API
  network: delay connect if ANQP has not completed
  hwsim: add new 'Delay' property to Rules
  doc: document new 'Delay' property for hwsim Rules
  auto-t: add 'Delay' property to hwsim python module
  auto-t: add test for delayed ANQP response

 autotests/testHotspot/anqp_delay_test.py |  83 +++++
 autotests/util/hwsim.py                  |   8 +
 doc/hwsim-rules-api.txt                  |   5 +
 src/anqp.c                               | 449 +++++++----------------
 src/anqp.h                               |   3 +-
 src/frame-xchg.c                         |  62 ++--
 src/network.c                            |  58 +++
 src/station.c                            |  45 ++-
 src/station.h                            |  13 +
 tools/hwsim.c                            |  53 ++-
 10 files changed, 414 insertions(+), 365 deletions(-)
 create mode 100644 autotests/testHotspot/anqp_delay_test.py

-- 
2.21.1

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

* [PATCH 01/10] frame-xchg: fix bug when starting new xchg from callback
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 02/10] frame-xchg: Fix frame_watch_remove_by_handler for group 0 James Prestwood
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

This bug is caused by the following behavior:

 1. Start a frame-xchg, wait for callback
 2. From callback start a new frame-xchg, same prefix.

The new frame-xchg request will detect that there is a duplicate watch,
which is correct behavior. It will then remove this duplicate from the
watchlist. The issue here is that we are in the watchlist notify loop
from the original xchg. This causes that loop to read from the now
freed watchlist item, causing an invalid read.

Instead of freeing the item immediately, check if the notify loop is in
progress and only set 'id' to zero and 'stale_items' to true. This will
allow the notify loop to finish, then the watchlist code will prune out
any stale items. If not in the notify loop the item can be freed as it
was before.
---
 src/frame-xchg.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index 4c950fd3..71751ca4 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -532,7 +532,17 @@ static bool frame_watch_check_duplicate(void *data, void *user_data)
 	}
 
 drop:
-	/* Drop the existing watch as a duplicate of the new one */
+	/*
+	 * Drop the existing watch as a duplicate of the new one. If we are in
+	 * the watchlist notify loop, just mark this item as stale and it will
+	 * be cleaned up afterwards
+	 */
+	if (watch->group->watches.in_notify) {
+		super->id = 0;
+		watch->group->watches.stale_items = true;
+		return false;
+	}
+
 	frame_watch_free(&watch->super);
 	return true;
 }
-- 
2.21.1

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

* [PATCH 02/10] frame-xchg: Fix frame_watch_remove_by_handler for group 0
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
  2020-06-02 17:30 ` [PATCH 01/10] frame-xchg: fix bug when starting new xchg from callback James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 03/10] frame-xchg: Use frame_watch_group_match in frame_watch_group_get James Prestwood
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

From: Andrew Zaborowski <andrew.zaborowski@intel.com>

Don't match the default group's (group_id 0) wdev_id against the
provided wdev_id because the default group can be used on all wdevs and
its wdev_id is 0.  Also match individual item's wdev_id in the group to
make up for this although it normally wouldn't matter.
---
 src/frame-xchg.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index 71751ca4..ba8bd0f0 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -671,6 +671,7 @@ bool frame_watch_wdev_remove(uint64_t wdev_id)
 }
 
 struct frame_watch_handler_check_info {
+	uint64_t wdev_id;
 	frame_watch_cb_t handler;
 	void *user_data;
 };
@@ -681,7 +682,8 @@ static bool frame_watch_item_remove_by_handler(void *data, void *user_data)
 		l_container_of(data, struct frame_watch, super);
 	struct frame_watch_handler_check_info *info = user_data;
 
-	if (watch->super.notify != info->handler ||
+	if (watch->wdev_id != info->wdev_id ||
+			watch->super.notify != info->handler ||
 			watch->super.notify_data != info->user_data)
 		return false;
 
@@ -712,9 +714,9 @@ static bool frame_watch_remove_by_handler(uint64_t wdev_id, uint32_t group_id,
 						void *user_data)
 {
 	struct watch_group_match_info group_info =
-		{ wdev_id, group_id };
+		{ group_id == 0 ? 0 : wdev_id, group_id };
 	struct frame_watch_handler_check_info handler_info =
-		{ handler, user_data };
+		{ wdev_id, handler, user_data };
 	struct watch_group *group = l_queue_find(watch_groups,
 						frame_watch_group_match,
 						&group_info);
-- 
2.21.1

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

* [PATCH 03/10] frame-xchg: Use frame_watch_group_match in frame_watch_group_get
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
  2020-06-02 17:30 ` [PATCH 01/10] frame-xchg: fix bug when starting new xchg from callback James Prestwood
  2020-06-02 17:30 ` [PATCH 02/10] frame-xchg: Fix frame_watch_remove_by_handler for group 0 James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 04/10] anqp: refactor to use frame-xchg James Prestwood
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

From: Andrew Zaborowski <andrew.zaborowski@intel.com>

---
 src/frame-xchg.c | 42 +++++++++++++++++++-----------------------
 1 file changed, 19 insertions(+), 23 deletions(-)

diff --git a/src/frame-xchg.c b/src/frame-xchg.c
index ba8bd0f0..7e516d0a 100644
--- a/src/frame-xchg.c
+++ b/src/frame-xchg.c
@@ -439,21 +439,30 @@ err:
 	return NULL;
 }
 
-static struct watch_group *frame_watch_group_get(uint64_t wdev_id, uint32_t id)
+struct watch_group_match_info {
+	uint64_t wdev_id;
+	uint32_t id;
+};
+
+static bool frame_watch_group_match(const void *a, const void *b)
 {
-	const struct l_queue_entry *entry;
-	struct watch_group *group;
+	const struct watch_group *group = a;
+	const struct watch_group_match_info *info = b;
 
-	for (entry = l_queue_get_entries(watch_groups); entry;
-			entry = entry->next) {
-		group = entry->data;
+	return group->wdev_id == info->wdev_id && group->id == info->id;
+}
 
-		if (group->id == id && (id == 0 || group->wdev_id == wdev_id))
-			return group;
+static struct watch_group *frame_watch_group_get(uint64_t wdev_id, uint32_t id)
+{
+	struct watch_group_match_info info = { id == 0 ? 0 : wdev_id, id };
+	struct watch_group *group = l_queue_find(watch_groups,
+						frame_watch_group_match, &info);
+
+	if (!group) {
+		group = frame_watch_group_new(wdev_id, id);
+		l_queue_push_tail(watch_groups, group);
 	}
 
-	group = frame_watch_group_new(wdev_id, id);
-	l_queue_push_tail(watch_groups, group);
 	return group;
 }
 
@@ -600,19 +609,6 @@ bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type,
 	return true;
 }
 
-struct watch_group_match_info {
-	uint64_t wdev_id;
-	uint32_t id;
-};
-
-static bool frame_watch_group_match(const void *a, const void *b)
-{
-	const struct watch_group *group = a;
-	const struct watch_group_match_info *info = b;
-
-	return group->wdev_id == info->wdev_id && group->id == info->id;
-}
-
 bool frame_watch_group_remove(uint64_t wdev_id, uint32_t group_id)
 {
 	struct watch_group_match_info info = { wdev_id, group_id };
-- 
2.21.1

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

* [PATCH 04/10] anqp: refactor to use frame-xchg
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (2 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 03/10] frame-xchg: Use frame_watch_group_match in frame_watch_group_get James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 05/10] station: add ANQP state watch API James Prestwood
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

The new frame-xchg module now handles a lot of what ANQP used to do. ANQP
now does not need to depend on nl80211/netdev for building and sending
frames. It also no longer needs any of the request lookups or frame
watches because frame-xchg filters this for us. The request queue did
need to remain in ANQP because station fires off ANQP requests all in
a row, and frame-xchg cannot handle this. Now ANQP just sends out the
first request, and queue's the rest. As the responses come in it will
send out the next frame in the queue until there are no more left.

From an API perspective:
 - anqp_request() was changed to take the wdev_id rather than ifindex.
 - anqp_cancel() was added so that station can properly clean up ANQP
   requests if the device disappears.

During testing a bug was also fixed in station on the timeout path
where the request queue would get popped twice.
---
 src/anqp.c    | 449 ++++++++++++++------------------------------------
 src/anqp.h    |   3 +-
 src/station.c |  24 +--
 3 files changed, 143 insertions(+), 333 deletions(-)

diff --git a/src/anqp.c b/src/anqp.c
index 6627bd81..f54c741b 100644
--- a/src/anqp.c
+++ b/src/anqp.c
@@ -31,34 +31,31 @@
 #include "src/module.h"
 #include "src/anqp.h"
 #include "src/util.h"
-#include "src/eap-private.h"
 #include "src/ie.h"
-#include "src/nl80211util.h"
-#include "src/nl80211cmd.h"
 #include "src/scan.h"
-#include "src/netdev.h"
 #include "src/iwd.h"
 #include "src/mpdu.h"
-#include "src/wiphy.h"
+#include "src/frame-xchg.h"
 
 #include "linux/nl80211.h"
 
+#define ANQP_GROUP	0
+
 struct anqp_request {
-	uint32_t ifindex;
+	uint64_t wdev_id;
 	anqp_response_func_t anqp_cb;
 	anqp_destroy_func_t anqp_destroy;
 	void *anqp_data;
-	uint64_t anqp_cookie;
 	uint8_t anqp_token;
+	uint32_t frequency;
+	uint8_t *frame;
+	size_t frame_len;
+	uint32_t id;
 };
 
-static struct l_genl_family *nl80211 = NULL;
-
 static struct l_queue *anqp_requests;
 static uint8_t anqp_token = 0;
-
-static uint32_t netdev_watch;
-static uint32_t unicast_watch;
+static uint32_t request_id;
 
 static void anqp_destroy(void *user_data)
 {
@@ -66,70 +63,33 @@ static void anqp_destroy(void *user_data)
 
 	if (request->anqp_destroy)
 		request->anqp_destroy(request->anqp_data);
-}
-
-static void netdev_gas_request_cb(struct l_genl_msg *msg, void *user_data)
-{
-	struct anqp_request *request = user_data;
-
-	if (l_genl_msg_get_error(msg) != 0)
-		goto error;
-
-	if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &request->anqp_cookie,
-					NL80211_ATTR_UNSPEC) < 0)
-		goto error;
-
-	return;
-
-error:
-	l_debug("Error sending CMD_FRAME (%d)", l_genl_msg_get_error(msg));
-
-	if (request->anqp_cb)
-		request->anqp_cb(ANQP_FAILED, NULL, 0, request->anqp_data);
-
-	if (request->anqp_destroy)
-		request->anqp_destroy(request->anqp_data);
 
+	l_free(request->frame);
 	l_free(request);
 }
 
-static bool match_token(const void *a, const void *b)
-{
-	const struct anqp_request *request = a;
-	const struct token_match {
-		uint32_t ifindex;
-		uint8_t token;
+static void anqp_request_next(void);
 
-	} *match = b;
-
-	if (request->ifindex != match->ifindex)
-		return false;
-
-	if (request->anqp_token != match->token)
-		return false;
-
-	return true;
-}
-
-static void anqp_response_frame_event(uint32_t ifindex,
-					const struct mmpdu_header *hdr,
-					const void *body, size_t body_len)
+/*
+ * By using frame-xchg we should get called back here for any frame matching our
+ * prefix until the duration expires. If frame-xchg is never signalled 'done'
+ * (returning true) we should get a timeout in anqp_frame_timeout. This is
+ * why we drop any improperly formatted frames without cleaning up the request.
+ */
+static bool anqp_response_frame_event(const struct mmpdu_header *hdr,
+					const void *body, size_t body_len,
+					int rssi, void *user_data)
 {
-	struct anqp_request *request;
+	struct anqp_request *request = user_data;
 	const uint8_t *ptr = body;
 	uint16_t status_code;
 	uint16_t delay;
 	uint16_t qrlen;
 	uint8_t adv_proto_len;
 	uint8_t token;
-	struct token_match {
-		uint32_t ifindex;
-		uint8_t token;
-
-	} match;
 
 	if (body_len < 9)
-		return;
+		return false;
 
 	/* Skip past category/action since this frame was prefixed matched */
 	ptr += 2;
@@ -138,12 +98,8 @@ static void anqp_response_frame_event(uint32_t ifindex,
 	/* dialog token */
 	token = *ptr++;
 
-	match.ifindex = ifindex;
-	match.token = token;
-
-	request = l_queue_find(anqp_requests, match_token, &match);
-	if (!request)
-		return;
+	if (request->anqp_token != token)
+		return false;
 
 	status_code = l_get_le16(ptr);
 	ptr += 2;
@@ -151,7 +107,7 @@ static void anqp_response_frame_event(uint32_t ifindex,
 
 	if (status_code != 0) {
 		l_error("Bad status code on GAS response %u", status_code);
-		return;
+		return false;
 	}
 
 	delay = l_get_le16(ptr);
@@ -166,12 +122,12 @@ static void anqp_response_frame_event(uint32_t ifindex,
 	 */
 	if (delay != 0) {
 		l_error("GAS comeback delay was not zero");
-		return;
+		return false;
 	}
 
 	if (*ptr != IE_TYPE_ADVERTISEMENT_PROTOCOL) {
 		l_error("GAS request not advertisement protocol");
-		return;
+		return false;
 	}
 
 	ptr++;
@@ -181,339 +137,188 @@ static void anqp_response_frame_event(uint32_t ifindex,
 	body_len--;
 
 	if (body_len < adv_proto_len)
-		return;
+		return false;
 
 	ptr += adv_proto_len;
 	body_len -= adv_proto_len;
 
 	if (body_len < 2)
-		return;
+		return false;
 
 	qrlen = l_get_le16(ptr);
 	ptr += 2;
 
 	if (body_len < qrlen)
-		return;
-
-	l_queue_remove(anqp_requests, request);
+		return false;
 
 	l_debug("ANQP response received from "MAC, MAC_STR(hdr->address_2));
 
 	if (request->anqp_cb)
-		request->anqp_cb(ANQP_SUCCESS, ptr, qrlen,
-					request->anqp_data);
+		request->anqp_cb(ANQP_SUCCESS, ptr, qrlen, request->anqp_data);
 
-	if (request->anqp_destroy)
-		request->anqp_destroy(request->anqp_data);
+	l_queue_remove(anqp_requests, request);
+	anqp_destroy(request);
 
-	l_free(request);
+	if (l_queue_isempty(anqp_requests))
+		return true;
+
+	anqp_request_next();
 
-	return;
+	return false;
 }
 
-static void netdev_gas_timeout_cb(void *user_data)
+static const struct frame_xchg_prefix anqp_frame_prefix = {
+	.data = (uint8_t []) {
+		0x04, 0x0b,
+	},
+	.len = 2,
+};
+
+static void anqp_frame_timeout(int error, void *user_data)
 {
 	struct anqp_request *request = user_data;
+	enum anqp_result result = ANQP_TIMEOUT;
 
-	l_debug("GAS request timed out");
+	if (error < 0) {
+		result = ANQP_FAILED;
+		l_error("Sending ANQP request failed: %s (%i)",
+			strerror(-error), -error);
+	}
 
 	if (request->anqp_cb)
-		request->anqp_cb(ANQP_TIMEOUT, NULL, 0,
-					request->anqp_data);
+		request->anqp_cb(result, NULL, 0, request->anqp_data);
 
-	/* allows anqp_request to be re-entrant */
 	if (request->anqp_destroy)
 		request->anqp_destroy(request->anqp_data);
 
 	l_queue_remove(anqp_requests, request);
-	l_free(request);
-}
-
-static bool match_cookie(const void *a, const void *b)
-{
-	const struct anqp_request *request = a;
-	const struct cookie_match {
-		uint64_t cookie;
-		uint32_t ifindex;
-	} *match = b;
-
-	if (match->ifindex != request->ifindex)
-		return false;
+	anqp_destroy(request);
 
-	if (match->cookie != request->anqp_cookie)
-		return false;
+	if (l_queue_isempty(anqp_requests))
+		return;
 
-	return true;
+	anqp_request_next();
 }
 
-static void anqp_frame_wait_cancel_event(struct l_genl_msg *msg,
-						uint32_t ifindex)
+static void anqp_request_next(void)
 {
-	uint64_t cookie;
 	struct anqp_request *request;
-	struct cookie_match {
-		uint64_t cookie;
-		uint32_t ifindex;
-	} match;
-
-	l_debug("");
+	struct iovec iov[2];
 
-	if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &cookie,
-					NL80211_ATTR_UNSPEC) < 0)
+	if (l_queue_isempty(anqp_requests))
 		return;
 
-	match.cookie = cookie;
-	match.ifindex = ifindex;
+	request = l_queue_peek_head(anqp_requests);
 
-	request = l_queue_find(anqp_requests, match_cookie, &match);
-	if (!request)
-		return;
+	iov[0].iov_base = request->frame;
+	iov[0].iov_len = request->frame_len;
+	iov[1].iov_base = NULL;
 
-	if (cookie != request->anqp_cookie)
-		return;
+	l_debug("Sending ANQP request");
 
-	netdev_gas_timeout_cb(request);
+	frame_xchg_start(request->wdev_id, iov, request->frequency, 0, 300, 0,
+				ANQP_GROUP, anqp_frame_timeout, request,
+				&anqp_frame_prefix, anqp_response_frame_event,
+				NULL);
 }
 
-uint32_t anqp_request(uint32_t ifindex, const uint8_t *addr,
-				struct scan_bss *bss, const uint8_t *anqp,
-				size_t len, anqp_response_func_t cb,
-				void *user_data, anqp_destroy_func_t destroy)
+static uint8_t *anqp_build_frame(const uint8_t *addr, struct scan_bss *bss,
+					const uint8_t *anqp, size_t len,
+					size_t *len_out)
 {
-	struct anqp_request *request;
-	uint8_t frame[512];
-	struct l_genl_msg *msg;
-	struct iovec iov[2];
-	uint32_t id;
-	uint32_t duration = 300;
-	struct netdev *netdev = netdev_find(ifindex);
+	uint8_t *frame = l_malloc(len + 33);
+	uint8_t *ptr;
 
-	if (!netdev)
-		return 0;
+	memset(frame, 0, len + 33);
 
-	/*
-	 * TODO: Netdev dependencies will eventually be removed so we need
-	 * another way to figure out wiphy capabilities.
-	 */
-	if (!wiphy_can_offchannel_tx(netdev_get_wiphy(netdev))) {
-		l_error("ANQP failed, driver does not support offchannel TX");
-		return 0;
-	}
+	l_put_le16(0x00d0, frame + 0);
+	memcpy(frame + 4, bss->addr, 6);
+	memcpy(frame + 10, addr, 6);
+	memcpy(frame + 16, bss->addr, 6);
 
-	frame[0] = 0x04;		/* Category: Public */
-	frame[1] = 0x0a;		/* Action: GAS initial Request */
-	frame[2] = anqp_token;		/* Dialog Token */
-	frame[3] = IE_TYPE_ADVERTISEMENT_PROTOCOL;
-	frame[4] = 2;
-	frame[5] = 0x7f;
-	frame[6] = IE_ADVERTISEMENT_ANQP;
-	l_put_le16(len, frame + 7);
+	ptr = frame + 24;
 
-	iov[0].iov_base = frame;
-	iov[0].iov_len = 9;
-	iov[1].iov_base = (void *)anqp;
-	iov[1].iov_len = len;
+	*ptr++ = 0x04;			/* Category: Public */
+	*ptr++ = 0x0a;			/* Action: GAS initial Request */
+	*ptr++ = anqp_token++;		/* Dialog Token */
+	*ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL;
+	*ptr++ = 2;
 
-	request = l_new(struct anqp_request, 1);
-
-	request->ifindex = ifindex;
-	request->anqp_cb = cb;
-	request->anqp_destroy = destroy;
-	request->anqp_token = anqp_token++;
-	request->anqp_data = user_data;
-
-	msg = nl80211_build_cmd_frame(ifindex, addr, bss->addr,
-					bss->frequency, iov, 2);
-
-	l_genl_msg_append_attr(msg, NL80211_ATTR_OFFCHANNEL_TX_OK, 0, "");
-	l_genl_msg_append_attr(msg, NL80211_ATTR_DURATION, 4, &duration);
-
-	id = l_genl_family_send(nl80211, msg, netdev_gas_request_cb,
-					request, NULL);
-
-	if (!id) {
-		l_debug("Failed to send ANQP request");
-		l_genl_msg_unref(msg);
-		l_free(request);
-		return 0;
-	}
+	*ptr++ = 0x7f;
+	*ptr++ = IE_ADVERTISEMENT_ANQP;
+	l_put_le16(len, ptr);
+	ptr += 2;
 
-	l_debug("ANQP request sent to "MAC, MAC_STR(bss->addr));
+	memcpy(ptr, anqp, len);
+	ptr += len;
 
-	l_queue_push_head(anqp_requests, request);
+	*len_out = ptr - frame;
 
-	return id;
+	return frame;
 }
 
-static void netdev_frame_cb(struct l_genl_msg *msg, void *user_data)
+uint32_t anqp_request(uint64_t wdev_id, const uint8_t *addr,
+			struct scan_bss *bss, const uint8_t *anqp,
+			size_t len, anqp_response_func_t cb,
+			void *user_data, anqp_destroy_func_t destroy)
 {
-	if (l_genl_msg_get_error(msg) < 0)
-		l_error("Could not register frame watch type %04x: %i",
-			L_PTR_TO_UINT(user_data), l_genl_msg_get_error(msg));
-}
+	struct anqp_request *request;
 
-static void anqp_register_frame(uint32_t ifindex)
-{
-	struct l_genl_msg *msg;
-	uint16_t frame_type = 0x00d0;
-	uint8_t prefix[] = { 0x04, 0x0b };
+	request = l_new(struct anqp_request, 1);
 
-	msg = l_genl_msg_new_sized(NL80211_CMD_REGISTER_FRAME, 34);
+	request->wdev_id = wdev_id;
+	request->frequency = bss->frequency;
+	request->anqp_cb = cb;
+	request->anqp_destroy = destroy;
+	request->anqp_token = anqp_token;
+	request->anqp_data = user_data;
+	request->id = ++request_id;
 
-	l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
-	l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_TYPE, 2, &frame_type);
-	l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
-					sizeof(prefix), prefix);
+	request->frame = anqp_build_frame(addr, bss, anqp, len,
+						&request->frame_len);
 
-	l_genl_family_send(nl80211, msg, netdev_frame_cb,
-			L_UINT_TO_PTR(frame_type), NULL);
-}
+	l_queue_push_tail(anqp_requests, request);
 
-static void anqp_netdev_watch(struct netdev *netdev,
-				enum netdev_watch_event event, void *user_data)
-{
-	switch (event) {
-	case NETDEV_WATCH_EVENT_NEW:
-		if (netdev_get_iftype(netdev) == NETDEV_IFTYPE_STATION)
-			anqp_register_frame(netdev_get_ifindex(netdev));
+	if (l_queue_length(anqp_requests) == 1)
+		anqp_request_next();
 
-		return;
-	default:
-		break;
-	}
+	return true;
 }
 
-static void anqp_unicast_notify(struct l_genl_msg *msg, void *user_data)
+static bool match_id(const void *a, const void *b)
 {
-	const struct mmpdu_header *mpdu = NULL;
-	const uint8_t *body;
-	struct l_genl_attr attr;
-	uint16_t type, len;
-	uint16_t frame_len = 0;
-	const void *data;
-	uint8_t cmd;
-	uint32_t ifindex = 0;
-
-	if (l_queue_isempty(anqp_requests))
-		return;
-
-	cmd = l_genl_msg_get_command(msg);
-	if (!cmd)
-		return;
-
-	if (!l_genl_attr_init(&attr, msg))
-		return;
-
-	while (l_genl_attr_next(&attr, &type, &len, &data)) {
-		switch (type) {
-		case NL80211_ATTR_IFINDEX:
-			if (len != sizeof(uint32_t)) {
-				l_warn("Invalid interface index attribute");
-				return;
-			}
-
-			ifindex = *((uint32_t *) data);
-
-			break;
-		case NL80211_ATTR_FRAME:
-			if (mpdu)
-				return;
-
-			mpdu = mpdu_validate(data, len);
-			if (!mpdu)
-				l_error("Frame didn't validate as MMPDU");
-
-			frame_len = len;
-			break;
-		}
-	}
-
-	if (!ifindex || !mpdu)
-		return;
-
-	body = mmpdu_body(mpdu);
+	const struct anqp_request *request = a;
 
-	anqp_response_frame_event(ifindex, mpdu, body,
-				(const uint8_t *) mpdu + frame_len - body);
+	return request->id == L_PTR_TO_UINT(b);
 }
 
-static void anqp_mlme_notify(struct l_genl_msg *msg, void *user_data)
+void anqp_cancel(uint32_t id)
 {
-	struct l_genl_attr attr;
-	uint16_t type, len;
-	const void *data;
-	uint8_t cmd;
-	uint32_t ifindex = 0;
-
-	if (l_queue_isempty(anqp_requests))
-		return;
-
-	cmd = l_genl_msg_get_command(msg);
-
-	l_debug("MLME notification %s(%u)", nl80211cmd_to_string(cmd), cmd);
-
-	if (!l_genl_attr_init(&attr, msg))
-		return;
-
-	while (l_genl_attr_next(&attr, &type, &len, &data)) {
-		switch (type) {
-		case NL80211_ATTR_IFINDEX:
-			if (len != sizeof(uint32_t)) {
-				l_warn("Invalid interface index attribute");
-				return;
-			}
-
-			ifindex = *((uint32_t *) data);
-			break;
-		}
-	}
+	struct anqp_request *request = l_queue_find(anqp_requests, match_id,
+							L_UINT_TO_PTR(id));
 
-	if (!ifindex) {
-		l_warn("MLME notification is missing ifindex attribute");
-		return;
-	}
+	/*
+	 * Frame-xchg sends frames one at a time, so only if the frame has
+	 * actually been given to frame-xchg do we need to stop it. Otherwise
+	 * just remove it from the queue
+	 */
+	if (request == l_queue_peek_head(anqp_requests))
+		frame_xchg_stop(request->wdev_id);
 
-	switch (cmd) {
-	case NL80211_CMD_FRAME_WAIT_CANCEL:
-		anqp_frame_wait_cancel_event(msg, ifindex);
-		break;
-	}
+	l_queue_remove(anqp_requests, request);
+	anqp_destroy(request);
 }
 
 static int anqp_init(void)
 {
-	struct l_genl *genl = iwd_get_genl();
-
-	nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
-
 	anqp_requests = l_queue_new();
 
-	netdev_watch =  netdev_watch_add(anqp_netdev_watch, NULL, NULL);
-
-	unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
-						anqp_unicast_notify,
-						NULL, NULL);
-
-	if (!l_genl_family_register(nl80211, "mlme", anqp_mlme_notify,
-								NULL, NULL))
-		l_error("Registering for MLME notification failed");
-
 	return 0;
 }
 
 static void anqp_exit(void)
 {
-	struct l_genl *genl = iwd_get_genl();
-
-	l_genl_family_free(nl80211);
-	nl80211 = NULL;
-
 	l_queue_destroy(anqp_requests, anqp_destroy);
-
-	netdev_watch_remove(netdev_watch);
-
-	l_genl_remove_unicast_watch(genl, unicast_watch);
 }
 
 IWD_MODULE(anqp, anqp_init, anqp_exit);
-IWD_MODULE_DEPENDS(anqp, netdev);
diff --git a/src/anqp.h b/src/anqp.h
index 998277dd..4d96c9ec 100644
--- a/src/anqp.h
+++ b/src/anqp.h
@@ -34,7 +34,8 @@ typedef void (*anqp_response_func_t)(enum anqp_result result,
 					const void *anqp, size_t len,
 					void *user_data);
 
-uint32_t anqp_request(uint32_t ifindex, const uint8_t *addr,
+uint32_t anqp_request(uint64_t wdev_id, const uint8_t *addr,
 			struct scan_bss *bss, const uint8_t *anqp, size_t len,
 			anqp_response_func_t cb, void *user_data,
 			anqp_destroy_func_t destroy);
+void anqp_cancel(uint32_t id);
diff --git a/src/station.c b/src/station.c
index c532319a..d4c827a1 100644
--- a/src/station.c
+++ b/src/station.c
@@ -447,12 +447,9 @@ static void station_anqp_response_cb(enum anqp_result result,
 	char **realms = NULL;
 	struct nai_search search;
 
-	entry->pending = 0;
-
 	l_debug("");
 
-	if (result == ANQP_TIMEOUT) {
-		l_queue_remove(station->anqp_pending, entry);
+	if (result != ANQP_SUCCESS) {
 		/* TODO: try next BSS */
 		goto request_done;
 	}
@@ -551,11 +548,10 @@ static bool station_start_anqp(struct station *station, struct network *network,
 	 * these are checked in hs20_find_settings_file.
 	 */
 
-	entry->pending = anqp_request(netdev_get_ifindex(station->netdev),
-					netdev_get_address(station->netdev),
-					bss, anqp, ptr - anqp,
-					station_anqp_response_cb,
-					entry, l_free);
+	entry->pending = anqp_request(netdev_get_wdev_id(station->netdev),
+				netdev_get_address(station->netdev), bss, anqp,
+				ptr - anqp, station_anqp_response_cb,
+				entry, l_free);
 	if (!entry->pending) {
 		l_free(entry);
 		return false;
@@ -3107,6 +3103,14 @@ static struct station *station_create(struct netdev *netdev)
 	return station;
 }
 
+static void remove_anqp(void *data)
+{
+	struct anqp_entry *entry = data;
+
+	anqp_cancel(entry->pending);
+	l_free(entry);
+}
+
 static void station_free(struct station *station)
 {
 	l_debug("");
@@ -3164,7 +3168,7 @@ static void station_free(struct station *station)
 
 	watchlist_destroy(&station->state_watches);
 
-	l_queue_destroy(station->anqp_pending, l_free);
+	l_queue_destroy(station->anqp_pending, remove_anqp);
 
 	l_free(station);
 }
-- 
2.21.1

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

* [PATCH 05/10] station: add ANQP state watch API
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (3 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 04/10] anqp: refactor to use frame-xchg James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 06/10] network: delay connect if ANQP has not completed James Prestwood
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

This is to allow network to watch for ANQP activity in order to
fix the race condition between scanning finishing and ANQP finishing.
Without this it is possible for a DBus Connect() to come in before
ANQP has completed and causing the network to return NotConfigured,
when its actually in the process of obtaining all the network info.

The watch was made globally in station due to network not having
a station object until each individual network is created. Adding a
watch during network creation would result in many watchers as well
as a lot of removal/addition as networks are found and lost.
---
 src/station.c | 21 +++++++++++++++++++++
 src/station.h | 13 +++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/src/station.c b/src/station.c
index d4c827a1..8c284c50 100644
--- a/src/station.c
+++ b/src/station.c
@@ -59,6 +59,7 @@ static uint32_t netdev_watch;
 static uint32_t mfp_setting;
 static bool anqp_disabled;
 static bool netconfig_enabled;
+static struct watchlist anqp_watches;
 
 struct station {
 	enum station_state state;
@@ -485,6 +486,9 @@ static void station_anqp_response_cb(enum anqp_result result,
 request_done:
 	l_queue_remove(station->anqp_pending, entry);
 
+	WATCHLIST_NOTIFY(&anqp_watches, station_anqp_watch_func_t,
+				STATION_ANQP_FINISHED, network);
+
 	/* If no more requests, resume scanning */
 	if (!l_queue_isempty(station->anqp_pending))
 		return;
@@ -559,6 +563,8 @@ static bool station_start_anqp(struct station *station, struct network *network,
 
 	l_queue_push_head(station->anqp_pending, entry);
 
+	WATCHLIST_NOTIFY(&anqp_watches, station_anqp_watch_func_t,
+				STATION_ANQP_STARTED, network);
 	return true;
 }
 
@@ -1214,6 +1220,18 @@ bool station_remove_state_watch(struct station *station, uint32_t id)
 	return watchlist_remove(&station->state_watches, id);
 }
 
+uint32_t station_add_anqp_watch(station_anqp_watch_func_t func,
+				void *user_data,
+				station_destroy_func_t destroy)
+{
+	return watchlist_add(&anqp_watches, func, user_data, destroy);
+}
+
+void station_remove_anqp_watch(uint32_t id)
+{
+	watchlist_remove(&anqp_watches, id);
+}
+
 bool station_set_autoconnect(struct station *station, bool autoconnect)
 {
 	if (station->autoconnect == autoconnect)
@@ -3271,6 +3289,8 @@ static int station_init(void)
 	if (!netconfig_enabled)
 		l_info("station: Network configuration is disabled.");
 
+	watchlist_init(&anqp_watches, NULL);
+
 	return 0;
 }
 
@@ -3280,6 +3300,7 @@ static void station_exit(void)
 	netdev_watch_remove(netdev_watch);
 	l_queue_destroy(station_list, NULL);
 	station_list = NULL;
+	watchlist_destroy(&anqp_watches);
 }
 
 IWD_MODULE(station, station_init, station_exit)
diff --git a/src/station.h b/src/station.h
index 4155081b..17a0f8df 100644
--- a/src/station.h
+++ b/src/station.h
@@ -43,8 +43,16 @@ enum station_state {
 	STATION_STATE_ROAMING
 };
 
+enum station_anqp_state {
+	STATION_ANQP_STARTED,
+	STATION_ANQP_FINISHED,
+};
+
 typedef void (*station_foreach_func_t)(struct station *, void *data);
 typedef void (*station_state_watch_func_t)(enum station_state, void *userdata);
+typedef void (*station_anqp_watch_func_t)(enum station_anqp_state,
+						struct network *network,
+						void *user_data);
 typedef void (*station_destroy_func_t)(void *userdata);
 typedef void (*station_network_foreach_func_t)(struct network *, void *data);
 
@@ -66,6 +74,11 @@ uint32_t station_add_state_watch(struct station *station,
 					station_destroy_func_t destroy);
 bool station_remove_state_watch(struct station *station, uint32_t id);
 
+uint32_t station_add_anqp_watch(station_anqp_watch_func_t func,
+				void *user_data,
+				station_destroy_func_t destroy);
+void station_remove_anqp_watch(uint32_t id);
+
 bool station_set_autoconnect(struct station *station, bool autoconnect);
 
 void station_ap_directed_roam(struct station *station,
-- 
2.21.1

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

* [PATCH 06/10] network: delay connect if ANQP has not completed
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (4 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 05/10] station: add ANQP state watch API James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 07/10] hwsim: add new 'Delay' property to Rules James Prestwood
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

Using the new station ANQP watch network can delay the connection
request until after ANQP has finished. Since station may be
autoconnecting we must also add a check in network_autoconnect
which prevents it from autoconnecting if we have a pending Connect
request.
---
 src/network.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/src/network.c b/src/network.c
index c08d7e72..b2599c7c 100644
--- a/src/network.c
+++ b/src/network.c
@@ -52,6 +52,7 @@
 #include "src/util.h"
 
 static uint32_t known_networks_watch;
+static uint32_t anqp_watch;
 
 struct network {
 	char ssid[33];
@@ -72,7 +73,10 @@ struct network {
 	bool update_psk:1;  /* Whether PSK should be written to storage */
 	bool ask_passphrase:1; /* Whether we should force-ask agent */
 	bool is_hs20:1;
+	bool anqp_pending:1;	/* Set if there is a pending ANQP request */
 	int rank;
+	/* Holds DBus Connect() message if it comes in before ANQP finishes */
+	struct l_dbus_message *connect_after_anqp;
 };
 
 static bool network_settings_load(struct network *network)
@@ -521,6 +525,10 @@ int network_autoconnect(struct network *network, struct scan_bss *bss)
 	bool is_rsn;
 	int ret;
 
+	/* already waiting for an ANQP response to connect */
+	if (network->connect_after_anqp)
+		return -EALREADY;
+
 	switch (security) {
 	case SECURITY_NONE:
 		is_rsn = false;
@@ -1102,6 +1110,11 @@ static struct l_dbus_message *network_connect_8021x(struct network *network,
 			goto error;
 		}
 
+		if (network->connect_after_anqp) {
+			l_dbus_message_unref(network->connect_after_anqp);
+			network->connect_after_anqp = NULL;
+		}
+
 		station_connect_network(station, network, bss, message);
 
 		return NULL;
@@ -1156,6 +1169,19 @@ static struct l_dbus_message *network_connect(struct l_dbus *dbus,
 		station_connect_network(station, network, bss, message);
 		return NULL;
 	case SECURITY_8021X:
+		/*
+		 * If there is an ongoing ANQP request we must wait for that to
+		 * finish. Save the message and wait for the ANQP watch to
+		 * fire
+		 */
+		if (network->anqp_pending) {
+			network->connect_after_anqp =
+						l_dbus_message_ref(message);
+			l_debug("Pending ANQP request, delaying connect to %s",
+						network->ssid);
+			return NULL;
+		}
+
 		if (!network_settings_load(network))
 			return dbus_error_not_configured(message);
 
@@ -1484,6 +1510,33 @@ static void known_networks_changed(enum known_networks_event event,
 	}
 }
 
+static void anqp_watch_changed(enum station_anqp_state state,
+				struct network *network, void *user_data)
+{
+	network->anqp_pending = state == STATION_ANQP_STARTED;
+
+	if (state == STATION_ANQP_FINISHED && network->connect_after_anqp) {
+		struct l_dbus_message *reply;
+
+		l_debug("ANQP complete, resuming connect to %s", network->ssid);
+
+		if (!network_settings_load(network))
+			reply = dbus_error_not_configured(
+						network->connect_after_anqp);
+		else
+			reply = network_connect_8021x(network,
+					network_bss_select(network, true),
+					network->connect_after_anqp);
+
+		if (!reply)
+			return;
+
+		dbus_pending_reply(&network->connect_after_anqp, reply);
+
+		return;
+	}
+}
+
 static void setup_network_interface(struct l_dbus_interface *interface)
 {
 	l_dbus_interface_method(interface, "Connect", 0,
@@ -1517,6 +1570,8 @@ static int network_init(void)
 	known_networks_watch =
 		known_networks_watch_add(known_networks_changed, NULL, NULL);
 
+	anqp_watch = station_add_anqp_watch(anqp_watch_changed, NULL, NULL);
+
 	return 0;
 }
 
@@ -1525,6 +1580,9 @@ static void network_exit(void)
 	known_networks_watch_remove(known_networks_watch);
 	known_networks_watch = 0;
 
+	station_remove_anqp_watch(anqp_watch);
+	anqp_watch = 0;
+
 	l_dbus_unregister_interface(dbus_get_bus(), IWD_NETWORK_INTERFACE);
 }
 
-- 
2.21.1

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

* [PATCH 07/10] hwsim: add new 'Delay' property to Rules
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (5 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 06/10] network: delay connect if ANQP has not completed James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-08 20:15   ` Denis Kenzior
  2020-06-02 17:30 ` [PATCH 08/10] doc: document new 'Delay' property for hwsim Rules James Prestwood
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

This allows a matching frame to be delayed by some number of
milliseconds.
---
 tools/hwsim.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 48 insertions(+), 5 deletions(-)

diff --git a/tools/hwsim.c b/tools/hwsim.c
index d066f814..3ccc936e 100644
--- a/tools/hwsim.c
+++ b/tools/hwsim.c
@@ -113,6 +113,7 @@ enum hwsim_tx_control_flags {
 };
 
 #define IEEE80211_TX_RATE_TABLE_SIZE	4
+#define HWSIM_DELAY_MIN_MS		1
 
 struct hwsim_rule {
 	unsigned int id;
@@ -125,6 +126,7 @@ struct hwsim_rule {
 	uint32_t frequency;
 	int priority;
 	int signal;
+	int delay;
 };
 
 struct hwsim_support {
@@ -1106,7 +1108,8 @@ static bool radio_match_addr(const struct radio_info_rec *radio,
 
 static void process_rules(const struct radio_info_rec *src_radio,
 				const struct radio_info_rec *dst_radio,
-				struct hwsim_frame *frame, bool *drop)
+				struct hwsim_frame *frame, bool *drop,
+				uint32_t *delay)
 {
 	const struct l_queue_entry *rule_entry;
 
@@ -1149,6 +1152,9 @@ static void process_rules(const struct radio_info_rec *src_radio,
 			frame->signal = rule->signal / 100;
 
 		*drop = rule->drop;
+
+		if (delay)
+			*delay = rule->delay;
 	}
 }
 
@@ -1232,7 +1238,7 @@ static void hwsim_frame_unref(struct hwsim_frame *frame)
 			bool drop = false;
 
 			process_rules(frame->ack_radio, frame->src_radio,
-					frame, &drop);
+					frame, &drop, NULL);
 
 			if (!drop)
 				frame->flags |= HWSIM_TX_STAT_ACK;
@@ -1374,13 +1380,14 @@ static void process_frame(struct hwsim_frame *frame)
 	bool drop_mcast = false;
 
 	if (util_is_broadcast_address(frame->dst_ether_addr))
-		process_rules(frame->src_radio, NULL, frame, &drop_mcast);
+		process_rules(frame->src_radio, NULL, frame, &drop_mcast, NULL);
 
 	for (entry = l_queue_get_entries(radio_info); entry;
 			entry = entry->next) {
 		struct radio_info_rec *radio = entry->data;
 		struct send_frame_info *send_info;
 		bool drop = drop_mcast;
+		uint32_t delay = HWSIM_DELAY_MIN_MS;
 
 		if (radio == frame->src_radio)
 			continue;
@@ -1413,7 +1420,7 @@ static void process_frame(struct hwsim_frame *frame)
 				continue;
 		}
 
-		process_rules(frame->src_radio, radio, frame, &drop);
+		process_rules(frame->src_radio, radio, frame, &drop, &delay);
 
 		if (drop)
 			continue;
@@ -1422,7 +1429,7 @@ static void process_frame(struct hwsim_frame *frame)
 		send_info->radio = radio;
 		send_info->frame = hwsim_frame_ref(frame);
 
-		if (!l_timeout_create_ms(1, frame_delay_callback,
+		if (!l_timeout_create_ms(delay, frame_delay_callback,
 						send_info, NULL)) {
 			l_error("Error delaying frame, frame will be dropped");
 			send_frame_destroy(send_info);
@@ -1875,6 +1882,7 @@ static struct l_dbus_message *rule_add(struct l_dbus *dbus,
 	rule->id = next_rule_id++;
 	rule->source_any = true;
 	rule->destination_any = true;
+	rule->delay = HWSIM_DELAY_MIN_MS;
 
 	if (!rules)
 		rules = l_queue_new();
@@ -2160,6 +2168,37 @@ static struct l_dbus_message *rule_property_set_drop(
 	return l_dbus_message_new_method_return(message);
 }
 
+static bool rule_property_get_delay(struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_builder *builder,
+					void *user_data)
+{
+	struct hwsim_rule *rule = user_data;
+
+	l_dbus_message_builder_append_basic(builder, 'u', &rule->delay);
+
+	return true;
+}
+
+static struct l_dbus_message *rule_property_set_delay(
+					struct l_dbus *dbus,
+					struct l_dbus_message *message,
+					struct l_dbus_message_iter *new_value,
+					l_dbus_property_complete_cb_t complete,
+					void *user_data)
+{
+	struct hwsim_rule *rule = user_data;
+	uint32_t val;
+
+	if (!l_dbus_message_iter_get_variant(new_value, "u", &val) ||
+				val < HWSIM_DELAY_MIN_MS)
+		return dbus_error_invalid_args(message);
+
+	rule->delay = val;
+
+	return l_dbus_message_new_method_return(message);
+}
+
 static void setup_rule_interface(struct l_dbus_interface *interface)
 {
 	l_dbus_interface_method(interface, "Remove", 0, rule_remove, "", "");
@@ -2192,6 +2231,10 @@ static void setup_rule_interface(struct l_dbus_interface *interface)
 					L_DBUS_PROPERTY_FLAG_AUTO_EMIT, "b",
 					rule_property_get_drop,
 					rule_property_set_drop);
+	l_dbus_interface_property(interface, "Delay",
+					L_DBUS_PROPERTY_FLAG_AUTO_EMIT, "u",
+					rule_property_get_delay,
+					rule_property_set_delay);
 }
 
 static void request_name_callback(struct l_dbus *dbus, bool success,
-- 
2.21.1

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

* [PATCH 08/10] doc: document new 'Delay' property for hwsim Rules
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (6 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 07/10] hwsim: add new 'Delay' property to Rules James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 09/10] auto-t: add 'Delay' property to hwsim python module James Prestwood
  2020-06-02 17:30 ` [PATCH 10/10] auto-t: add test for delayed ANQP response James Prestwood
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

---
 doc/hwsim-rules-api.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/doc/hwsim-rules-api.txt b/doc/hwsim-rules-api.txt
index 0002db36..448ede14 100644
--- a/doc/hwsim-rules-api.txt
+++ b/doc/hwsim-rules-api.txt
@@ -68,3 +68,8 @@ Properties	string Source
 
 		bool Drop
 			If true, nothing is passed to the receiver.
+
+		uint32 Delay
+			Set the millisecond delay for any matching packets. This
+			value cannot be less than 1 as a 1ms delay is required
+			for test reliability.
-- 
2.21.1

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

* [PATCH 09/10] auto-t: add 'Delay' property to hwsim python module
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (7 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 08/10] doc: document new 'Delay' property for hwsim Rules James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  2020-06-02 17:30 ` [PATCH 10/10] auto-t: add test for delayed ANQP response James Prestwood
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

---
 autotests/util/hwsim.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/autotests/util/hwsim.py b/autotests/util/hwsim.py
index 4d3748b1..6d2b3e8f 100755
--- a/autotests/util/hwsim.py
+++ b/autotests/util/hwsim.py
@@ -112,6 +112,14 @@ class Rule(HwsimDBusAbstract):
     def drop(self, value):
         self._prop_proxy.Set(self._iface_name, 'Drop', dbus.Boolean(value))
 
+    @property
+    def delay(self):
+        return int(self._properties['Delay'])
+
+    @delay.setter
+    def delay(self, value):
+        self._prop_proxy.Set(self._iface_name, 'Delay', dbus.UInt32(value))
+
     def remove(self):
         self._iface.Remove(reply_handler=self._success,
                 error_handler=self._failure)
-- 
2.21.1

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

* [PATCH 10/10] auto-t: add test for delayed ANQP response
  2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
                   ` (8 preceding siblings ...)
  2020-06-02 17:30 ` [PATCH 09/10] auto-t: add 'Delay' property to hwsim python module James Prestwood
@ 2020-06-02 17:30 ` James Prestwood
  9 siblings, 0 replies; 12+ messages in thread
From: James Prestwood @ 2020-06-02 17:30 UTC (permalink / raw)
  To: iwd

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

---
 autotests/testHotspot/anqp_delay_test.py | 83 ++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 autotests/testHotspot/anqp_delay_test.py

diff --git a/autotests/testHotspot/anqp_delay_test.py b/autotests/testHotspot/anqp_delay_test.py
new file mode 100644
index 00000000..161b1a8e
--- /dev/null
+++ b/autotests/testHotspot/anqp_delay_test.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python3
+
+import unittest
+import sys
+import os
+
+sys.path.append('../util')
+import iwd
+from iwd import IWD
+from iwd import PSKAgent
+from iwd import NetworkType
+from hostapd import HostapdCLI
+from hwsim import Hwsim
+import testutil
+from time import sleep
+
+class Test(unittest.TestCase):
+
+    def test_connection_success(self):
+        hwsim = Hwsim()
+
+        bss_radio = hwsim.get_radio('rad0')
+        rule0 = hwsim.rules.create()
+        rule0.source = bss_radio.addresses[0]
+        rule0.bidirectional = True
+        # Force the case where ANQP does not finish before Connect() comes in
+        rule0.delay = 100
+
+        wd = IWD(True, '/tmp')
+
+        hapd = HostapdCLI(config='ssidHotspot.conf')
+
+        psk_agent = PSKAgent('abc', ('domain\\user', 'testpasswd'))
+        wd.register_psk_agent(psk_agent)
+
+        devices = wd.list_devices(1)
+        device = devices[0]
+
+        # We are dependent on a periodic scan here. We want to wait for this
+        # because this is the first opportunity IWD has to do ANQP. Once ANQP
+        # has been done once the network is set up and we cannot simulate the
+        # 'Connect() before ANQP' race condition anymore.
+        condition = 'obj.scanning'
+        wd.wait_for_object_condition(device, condition)
+
+        condition = 'not obj.scanning'
+        wd.wait_for_object_condition(device, condition)
+
+        ordered_network = device.get_ordered_network('Hotspot')
+
+        self.assertEqual(ordered_network.type, NetworkType.eap)
+
+        condition = 'not obj.connected'
+        wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+        ordered_network.network_object.connect()
+
+        condition = 'obj.connected'
+        wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+        testutil.test_iface_operstate()
+        testutil.test_ifaces_connected(device.name, hapd.ifname)
+
+        device.disconnect()
+
+        condition = 'not obj.connected'
+        wd.wait_for_object_condition(ordered_network.network_object, condition)
+
+        wd.unregister_psk_agent(psk_agent)
+
+    @classmethod
+    def setUpClass(cls):
+        IWD.copy_to_hotspot('example.conf')
+        conf = '[General]\nDisableANQP=0\n'
+        os.system('echo "%s" > /tmp/main.conf' % conf)
+
+    @classmethod
+    def tearDownClass(cls):
+        IWD.clear_storage()
+        os.remove('/tmp/main.conf')
+
+if __name__ == '__main__':
+    unittest.main(exit=True)
-- 
2.21.1

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

* Re: [PATCH 07/10] hwsim: add new 'Delay' property to Rules
  2020-06-02 17:30 ` [PATCH 07/10] hwsim: add new 'Delay' property to Rules James Prestwood
@ 2020-06-08 20:15   ` Denis Kenzior
  0 siblings, 0 replies; 12+ messages in thread
From: Denis Kenzior @ 2020-06-08 20:15 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 6/2/20 12:30 PM, James Prestwood wrote:
> This allows a matching frame to be delayed by some number of
> milliseconds.
> ---
>   tools/hwsim.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 48 insertions(+), 5 deletions(-)
> 

Patch 7 & 8 applied, thanks.

Regards,
-Denis

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

end of thread, other threads:[~2020-06-08 20:15 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-02 17:30 [PATCH 00/10] ANQP Refactor to use frame-xchg James Prestwood
2020-06-02 17:30 ` [PATCH 01/10] frame-xchg: fix bug when starting new xchg from callback James Prestwood
2020-06-02 17:30 ` [PATCH 02/10] frame-xchg: Fix frame_watch_remove_by_handler for group 0 James Prestwood
2020-06-02 17:30 ` [PATCH 03/10] frame-xchg: Use frame_watch_group_match in frame_watch_group_get James Prestwood
2020-06-02 17:30 ` [PATCH 04/10] anqp: refactor to use frame-xchg James Prestwood
2020-06-02 17:30 ` [PATCH 05/10] station: add ANQP state watch API James Prestwood
2020-06-02 17:30 ` [PATCH 06/10] network: delay connect if ANQP has not completed James Prestwood
2020-06-02 17:30 ` [PATCH 07/10] hwsim: add new 'Delay' property to Rules James Prestwood
2020-06-08 20:15   ` Denis Kenzior
2020-06-02 17:30 ` [PATCH 08/10] doc: document new 'Delay' property for hwsim Rules James Prestwood
2020-06-02 17:30 ` [PATCH 09/10] auto-t: add 'Delay' property to hwsim python module James Prestwood
2020-06-02 17:30 ` [PATCH 10/10] auto-t: add test for delayed ANQP response 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.