All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] auto-t: support hostapd event processing
@ 2019-10-29 17:50 James Prestwood
  2019-10-29 17:50 ` [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities James Prestwood
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: James Prestwood @ 2019-10-29 17:50 UTC (permalink / raw)
  To: iwd

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

Hostapd has a feature where you can connect to its control socket and
receive events it generates. Currently we only send commands via this
socket.

First we open the socket (/var/run/hostapd/<iface>) and send the
ATTACH command. This tells hostapd we are ready and after this any
events will be sent over this socket.

A new API, wait_for_event, was added which takes an event string and
waits for some timeout. The glib event loop has been integrated into
this, though its not technically async since we are selecting over a
socket which blocks. To mitigate this a small timeout was chosen for
each select call and then wrapped in a while loop which waits for the
full timeout.
---
 autotests/util/hostapd.py | 68 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/autotests/util/hostapd.py b/autotests/util/hostapd.py
index 209d8c24..49047f38 100644
--- a/autotests/util/hostapd.py
+++ b/autotests/util/hostapd.py
@@ -3,6 +3,9 @@ import os, os.path
 from wiphy import wiphy_map
 import re
 import socket
+import select
+import time
+from gi.repository import GLib
 
 chan_freq_map = [
     None,
@@ -22,12 +25,17 @@ chan_freq_map = [
     2484
 ]
 
+ctrl_count = 0
+mainloop = GLib.MainLoop()
+
 hostapd_map = {ifname: intf for wname, wiphy in wiphy_map.items()
         for ifname, intf in wiphy.interface_map.items()
         if wiphy.use == 'hostapd'}
 
 class HostapdCLI:
     def __init__(self, interface=None, config=None):
+        global ctrl_count
+
         if not interface and not config:
             raise Exception('interface or config must be provided')
 
@@ -48,10 +56,64 @@ class HostapdCLI:
 
         self._hostapd_restarted = False
 
+        self.local_ctrl = '/tmp/hostapd_' + str(os.getpid()) + '_' + \
+                            str(ctrl_count)
+        self.ctrl_sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+        self.ctrl_sock.bind(self.local_ctrl)
+        self.ctrl_sock.connect(self.socket_path + '/' + self.ifname)
+
+        if 'OK' not in self._ctrl_request('ATTACH'):
+            raise Exception('ATTACH failed')
+
+        ctrl_count = ctrl_count + 1
+
+    def wait_for_event(self, event, timeout=10):
+        global mainloop
+        self._wait_timed_out = False
+
+        def wait_timeout_cb():
+            self._wait_timed_out = True
+            return False
+
+        timeout = GLib.timeout_add_seconds(timeout, wait_timeout_cb)
+        context = mainloop.get_context()
+
+        while True:
+            context.iteration(may_block=False)
+
+            while self._data_available(0.25):
+                data = self.ctrl_sock.recv(4096).decode('utf-8')
+                if event in data:
+                    return data
+
+            if self._wait_timed_out:
+                raise TimeoutError('waiting for hostapd event timed out')
+
+        return None
+
+    def _data_available(self, timeout=2):
+        [r, w, e] = select.select([self.ctrl_sock], [], [], timeout)
+        if r:
+            return True
+        return False
+
+    def _ctrl_request(self, command, timeout=10):
+        if type(command) is str:
+            command = str.encode(command)
+
+        self.ctrl_sock.send(bytes(command))
+
+        if self._data_available(timeout):
+            return self.ctrl_sock.recv(4096).decode('utf-8')
+
+        raise Exception('timeout waiting for control response')
+
     def __del__(self):
         if self._hostapd_restarted:
             os.system('killall hostapd')
 
+        self.ctrl_sock.close()
+
     def wps_push_button(self):
         os.system(self.cmdline + ' wps_pbc')
 
@@ -140,3 +202,9 @@ class HostapdCLI:
 
         # set flag so hostapd can be killed after the test
         self._hostapd_restarted = True
+
+    def req_beacon(self, addr, request):
+        '''
+            Send a RRM Beacon request
+        '''
+        os.system(self.cmdline + ' req_beacon ' + addr + ' ' + request)
-- 
2.17.1

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

* [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities
  2019-10-29 17:50 [PATCH 1/5] auto-t: support hostapd event processing James Prestwood
@ 2019-10-29 17:50 ` James Prestwood
  2019-11-04 20:43   ` Denis Kenzior
  2019-10-29 17:50 ` [PATCH 3/5] station: add APIs to get connected BSS and BSS list James Prestwood
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: James Prestwood @ 2019-10-29 17:50 UTC (permalink / raw)
  To: iwd

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

This tells AP's that we support Passive, Active, and Table beacon
measurements.
---
 src/wiphy.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/wiphy.c b/src/wiphy.c
index 36e6c73d..37d06bc7 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -961,6 +961,8 @@ static void wiphy_setup_rm_enabled_capabilities(struct wiphy *wiphy)
 
 	wiphy->rm_enabled_capabilities[0] = IE_TYPE_RM_ENABLED_CAPABILITIES;
 	wiphy->rm_enabled_capabilities[1] = 5;
+	/* Bits: Passive (4), Active (5), and Beacon Table (6) capabilities */
+	wiphy->rm_enabled_capabilities[2] = 0x70;
 
 	/*
 	 * TODO: Support at least Link Measurement if TX_POWER_INSERTION is
-- 
2.17.1

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

* [PATCH 3/5] station: add APIs to get connected BSS and BSS list
  2019-10-29 17:50 [PATCH 1/5] auto-t: support hostapd event processing James Prestwood
  2019-10-29 17:50 ` [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities James Prestwood
@ 2019-10-29 17:50 ` James Prestwood
  2019-11-04 20:44   ` Denis Kenzior
  2019-10-29 17:50 ` [PATCH 4/5] rrm: add radio resource management module James Prestwood
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: James Prestwood @ 2019-10-29 17:50 UTC (permalink / raw)
  To: iwd

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

For Radio Resource Management (RRM) we will need access to the currently
connected BSS as well as the last scan results in order to do certain
kinds of requested measurements.
---
 src/station.c | 10 ++++++++++
 src/station.h |  2 ++
 2 files changed, 12 insertions(+)

diff --git a/src/station.c b/src/station.c
index 315b3dcd..3246e90c 100644
--- a/src/station.c
+++ b/src/station.c
@@ -3015,6 +3015,16 @@ void station_network_foreach(struct station *station,
 	l_hashmap_foreach(station->networks, network_foreach, &data);
 }
 
+struct l_queue *station_get_bss_list(struct station *station)
+{
+	return station->bss_list;
+}
+
+struct scan_bss *station_get_connected_bss(struct station *station)
+{
+	return station->connected_bss;
+}
+
 static struct station *station_create(struct netdev *netdev)
 {
 	struct station *station;
diff --git a/src/station.h b/src/station.h
index 469b5400..4155081b 100644
--- a/src/station.h
+++ b/src/station.h
@@ -85,3 +85,5 @@ void station_foreach(station_foreach_func_t func, void *user_data);
 void station_network_foreach(struct station *station,
 				station_network_foreach_func_t func,
 				void *user_data);
+struct l_queue *station_get_bss_list(struct station *station);
+struct scan_bss *station_get_connected_bss(struct station *station);
-- 
2.17.1

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

* [PATCH 4/5] rrm: add radio resource management module
  2019-10-29 17:50 [PATCH 1/5] auto-t: support hostapd event processing James Prestwood
  2019-10-29 17:50 ` [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities James Prestwood
  2019-10-29 17:50 ` [PATCH 3/5] station: add APIs to get connected BSS and BSS list James Prestwood
@ 2019-10-29 17:50 ` James Prestwood
  2019-11-04 21:09   ` Denis Kenzior
  2019-10-29 17:50 ` [PATCH 5/5] auto-t: add RRM autotest James Prestwood
  2019-11-04 20:42 ` [PATCH 1/5] auto-t: support hostapd event processing Denis Kenzior
  4 siblings, 1 reply; 11+ messages in thread
From: James Prestwood @ 2019-10-29 17:50 UTC (permalink / raw)
  To: iwd

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

This module takes care of radio measurements which an AP can request.
There are many types of requests, and for now only beacon requests
are supported.

IWD will filter certain types of beacon requests that are NOT
supported:

 - AP channel reports. Only single channel requests will be supported
 - Autonomous measurements. Only direct requests will be supported.
   IWD will not accept requets to trigger reports under certain
   conditions (SNR/RSSI thresholds, etc.)
 - Timed measurements. Only immediate measurements will be performed.
   The accuracy for timed measurements cannot be reliably guaranteed
   due to kernel scheduling/queues.
 - Full reporting detail. The AP can request the STA return the full
   set of IEs in a beacon. IWD does not currently save all IEs, plus
   there is quite a bit of complexity involved as certain IEs get
   truncated, and there are other length limitations.

There are other limitations not specific to beacon requests:

 - IWD will support single measurement requests per report. Multiple
   measurement request IEs can be included, but the reports will be
   sent out separately.

 - IWD will limit the number of requests it responds to in a given
   amount of time. As it stands now this is hard coded to 2 requests
   per second maximum. This will prevent DoS attacks.

 - IWD will not accept any measurement requests from APs it is not
   connected to, and will not accept any requests until connected.
---
 Makefile.am |   1 +
 src/rrm.c   | 764 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 765 insertions(+)
 create mode 100644 src/rrm.c

diff --git a/Makefile.am b/Makefile.am
index 823b7d02..f3c34680 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -226,6 +226,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
 					src/hotspot.c \
 					src/p2putil.h src/p2putil.c \
 					src/module.c \
+					src/rrm.c \
 					$(eap_sources) \
 					$(builtin_sources)
 
diff --git a/src/rrm.c b/src/rrm.c
new file mode 100644
index 00000000..e9a94a09
--- /dev/null
+++ b/src/rrm.c
@@ -0,0 +1,764 @@
+/*
+ *
+ *  Wireless daemon for Linux
+ *
+ *  Copyright (C) 2019  Intel Corporation. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <math.h>
+#include <linux/if_ether.h>
+
+#include <ell/ell.h>
+
+#include "src/mpdu.h"
+#include "src/netdev.h"
+#include "src/iwd.h"
+#include "src/ie.h"
+#include "src/util.h"
+#include "src/station.h"
+#include "src/scan.h"
+#include "src/nl80211util.h"
+
+#include "linux/nl80211.h"
+
+/* Limit requests per second */
+#define MAX_REQUESTS_PER_SEC		2
+/* Microseconds between requests */
+#define MIN_MICROS_BETWEEN_REQUESTS	(1000000 / MAX_REQUESTS_PER_SEC)
+
+/* 802.11-2016 Table 9-90 */
+#define REPORT_DETAIL_NO_FIELDS_OR_ELEMS		0
+#define REPORT_DETAIL_ALL_FIELDS_AND_ANY_REQUEST_ELEMS	1
+#define REPORT_DETAIL_ALL_FIELDS_AND_ELEMS		2
+
+/* 802.11-2016 Table 9-192 */
+#define REPORT_REJECT_LATE	(1 << 0)
+#define REPORT_REJECT_INCAPABLE	(1 << 1)
+#define REPORT_REJECT_REFUSED	(1 << 2)
+
+/* 802.11-2016 Table 9-87 */
+enum rrm_beacon_req_mode {
+	RRM_BEACON_REQ_MODE_PASSIVE =	0,
+	RRM_BEACON_REQ_MODE_ACTIVE =	1,
+	RRM_BEACON_REQ_MODE_TABLE =	2,
+};
+
+/* 802.11-2016 Table 9-88 */
+enum rrm_beacon_req_subelem_id {
+	RRM_BEACON_REQ_SUBELEM_ID_SSID			= 0,
+	RRM_BEACON_REQ_SUBELEM_ID_BEACON_REPORTING	= 1,
+	RRM_BEACON_REQ_SUBELEM_ID_REPORTING_DETAIL	= 2,
+	/* 3 - 9 reserved */
+	RRM_BEACON_REQ_SUBELEM_ID_REQUEST		= 10,
+	RRM_BEACON_REQ_SUBELEM_ID_EXT_REQUEST		= 11,
+	/* 12 - 50 reserved */
+	RRM_BEACON_REQ_SUBELEM_ID_AP_CHAN_REPORT	= 51,
+	/* 52 - 162 reserved */
+	RRM_BEACON_REQ_SUBELEM_ID_WIDE_BAND_SWITCH	= 163,
+	/* 164 - 220 reserved */
+	RRM_BEACON_REQ_SUBELEM_ID_VENDOR		= 221,
+	/* 222 - 255 reserved */
+};
+
+/* 802.11-2016 Annex C - dot11PHYType */
+enum rrm_phy_type {
+	RRM_PHY_TYPE_DSSS	= 2,
+	RRM_PHY_TYPE_OFDM	= 4,
+	RRM_PHY_TYPE_HRDSSS	= 5,
+	RRM_PHY_TYPE_ERP	= 6,
+	RRM_PHY_TYPE_HT		= 7,
+	RRM_PHY_TYPE_DMG	= 8,
+	RRM_PHY_TYPE_VHT	= 9,
+	RRM_PHY_TYPE_TVHT	= 10,
+};
+
+/*
+ * Basically the same as 802.11-2016 9.4.2.21.7
+ *
+ * Note: Not packed as this is only for saving values for response
+ */
+struct rrm_beacon_req_info {
+	uint8_t oper_class;
+	uint8_t channel;	/* The single channel provided in request */
+	uint8_t bssid[6];
+	char *ssid;
+};
+
+struct rrm_request_info {
+	uint32_t ifindex;
+	uint8_t from[6];
+	uint8_t dialog_token; /* dialog token in Radio Measurement Request */
+	uint8_t mtoken; /* token in measurement request element */
+	uint8_t mode;
+	uint8_t type;
+
+	/* TODO: once more measurements are supported this can be a union */
+	struct rrm_beacon_req_info *beacon;
+};
+
+static uint64_t last_request_us;
+static const uint8_t wildcard_bss[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static struct l_genl_family *nl80211;
+static uint32_t netdev_watch;
+static uint32_t unicast_watch;
+
+static void rrm_info_destroy(void *data)
+{
+	struct rrm_request_info *info = data;
+
+	if (info->beacon) {
+		l_free(info->beacon->ssid);
+		l_free(info->beacon);
+	}
+
+	l_free(info);
+}
+
+static uint8_t rrm_phy_type(struct scan_bss *bss)
+{
+	if (bss->vht_capable)
+		return RRM_PHY_TYPE_VHT;
+
+	if (bss->ht_capable)
+		return RRM_PHY_TYPE_HT;
+
+	/*
+	 * Default to 802.11g phy type. You can get quite fancy here determining
+	 * the phy type by looking at the frequency and operator class among
+	 * other things. Since 802.11a/b are so old, defaulting to 802.11g just
+	 * removes a lot of complexity. Above, HT/VHT are easy as all you need
+	 * to look for is the presence of the IE.
+	 */
+	return RRM_PHY_TYPE_ERP;
+}
+
+static void rrm_send_response_cb(struct l_genl_msg *msg, void *user_data)
+{
+	int err = l_genl_msg_get_error(msg);
+
+	if (err < 0)
+		l_error("Error sending response: %d", err);
+}
+
+static bool rrm_send_response(uint32_t ifindex, const uint8_t *dest,
+				const uint8_t *frame, size_t len)
+{
+	struct station *station = station_find(ifindex);
+	struct netdev *netdev = netdev_find(ifindex);
+	struct scan_bss *connected_bss = station_get_connected_bss(station);
+	const uint8_t *own_addr = netdev_get_address(netdev);
+	uint32_t own_freq = connected_bss->frequency;
+	struct l_genl_msg *msg;
+	struct iovec iov;
+
+	iov.iov_base = (void *)frame;
+	iov.iov_len = len;
+
+	msg = nl80211_build_cmd_frame(ifindex, own_addr, dest,
+					own_freq, &iov, 1);
+
+	if (!l_genl_family_send(nl80211, msg, rrm_send_response_cb,
+					NULL, NULL)) {
+		l_genl_msg_unref(msg);
+		l_error("Failed to send report for "MAC, MAC_STR(dest));
+		return false;
+	}
+
+	return true;
+}
+
+static bool rrm_reject_measurement_request(struct rrm_request_info *info,
+						uint8_t mode)
+{
+	uint8_t frame[8];
+
+	frame[0] = 0x05; /* Category: Radio Measurement */
+	frame[1] = 0x01; /* Action: Radio Measurement Report */
+	frame[2] = info->dialog_token;
+	frame[3] = IE_TYPE_MEASUREMENT_REQUEST;
+	frame[4] = 3;
+	frame[5] = info->mtoken;
+	frame[6] = mode;
+	frame[7] = info->type;
+
+	if (!rrm_send_response(info->ifindex, info->from, frame, sizeof(frame)))
+		return false;
+
+	rrm_info_destroy(info);
+
+	return true;
+}
+
+static void rrm_build_measurement_report(struct rrm_request_info *info,
+				const void *report, size_t report_len,
+				uint8_t *to)
+{
+	*to++ = IE_TYPE_MEASUREMENT_REPORT;
+	*to++ = 3 + report_len;
+	*to++ = info->mtoken;
+	*to++ = 0;
+	*to++ = info->type;
+
+	if (report)
+		memcpy(to, report, report_len);
+}
+
+static void rrm_register_frame_cb(struct l_genl_msg *msg, void *user_data)
+{
+	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));
+}
+
+static void rrm_register_frame(uint32_t ifindex)
+{
+	struct l_genl_msg *msg;
+	uint16_t frame_type = 0x00d0;
+	uint8_t prefix[] = { 0x05, 0x00 }; /* Radio Measurment Request */
+
+	msg = l_genl_msg_new_sized(NL80211_CMD_REGISTER_FRAME, 34);
+
+	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);
+
+	l_genl_family_send(nl80211, msg, rrm_register_frame_cb,
+			L_UINT_TO_PTR(frame_type), NULL);
+}
+
+static void rrm_netdev_watch(struct netdev *netdev,
+				enum netdev_watch_event event, void *user_data)
+{
+	switch (event) {
+	case NETDEV_WATCH_EVENT_NEW:
+		rrm_register_frame(netdev_get_ifindex(netdev));
+		return;
+	default:
+		break;
+	}
+}
+
+/*
+ * 802.11-2016 11.11.9.1 Beacon report
+ *
+ * "If the stored beacon information is based on a measurement made by
+ *  the reporting STA, and if the actual measurement start time,
+ *  measurement duration, and Parent TSF are available for this
+ *  measurement, then the beacon report shall include the actual
+ *  measurement start time, measurement duration, and Parent TSF;
+ *  otherwise the actual measurement start time, measurement duration,
+ *  and Parent TSF shall be set to 0. The RCPI and RSNI for that stored
+ *  beacon measurement may be included in the beacon report; otherwise
+ *  the beacon report shall indicate that RCPI and RSNI measurements
+ *  are not available"
+ *
+ * Since accurate timing is unreliable we are setting start/duration/TSF time to
+ * zero for all cases (table, passive, active).
+ */
+static size_t build_report_for_bss(struct rrm_request_info *info,
+					struct scan_bss *bss,
+					uint8_t *to)
+{
+	uint8_t *start = to;
+	double dbms = bss->signal_strength / 100;
+
+	*to++ = info->beacon->oper_class;
+	*to++ = scan_freq_to_channel(bss->frequency, NULL);
+	/* skip start time/duration */
+	memset(to, 0, 10);
+	to += 10;
+	*to++ = rrm_phy_type(bss);
+
+	/* 802.11 Table 9-154 - RCPI values */
+	if (dbms < -109.5)
+		*to++ = 0;
+	else if (dbms >= -109.5 && dbms < 0)
+		*to++ = (uint8_t)floor(2 * (dbms + 110));
+	else
+		*to++ = 220;
+
+	/* RSNI not available (could get this from GET_SURVEY) */
+	*to++ = 255;
+	memcpy(to, bss->addr, 6);
+	to += 6;
+	/* Antenna identifier unknown */
+	*to++ = 0;
+	/* Parent TSF - zero */
+	memset(to, 0, 4);
+	to += 4;
+
+	/*
+	 * TODO: Support optional subelements
+	 *
+	 * (see "TODO: Support Reported Frame Body..." below)
+	 */
+
+	return to - start;
+}
+
+static bool bss_in_request_range(struct rrm_request_info *info,
+					struct scan_bss *bss)
+{
+	uint8_t channel = scan_freq_to_channel(bss->frequency, NULL);
+
+	/* Must be a table measurement */
+	if (info->beacon->channel == 0 || info->beacon->channel == 255)
+		return true;
+
+	if (info->beacon->channel == channel)
+		return true;
+
+	return false;
+}
+
+static bool rrm_report_results(struct rrm_request_info *info,
+				struct l_queue *bss_list)
+{
+	bool wildcard = memcmp(info->beacon->bssid, wildcard_bss, 6) == 0;
+	const struct l_queue_entry *entry;
+	uint8_t frame[512];
+	uint8_t *ptr = frame;
+
+	*ptr++ = 0x05; /* Category: Radio Measurement */
+	*ptr++ = 0x01; /* Action: Radio Measurement Report */
+	*ptr++ = info->dialog_token;
+
+	for (entry = l_queue_get_entries(bss_list); entry;
+							entry = entry->next) {
+		struct scan_bss *bss = entry->data;
+		uint8_t report[257];
+		size_t report_len;
+
+		/* If request included a specific BSSID match only this BSS */
+		if (!wildcard && memcmp(bss->addr, info->beacon->bssid, 6) != 0)
+			continue;
+
+		/* If request was for a certain SSID, match only this SSID */
+		if (info->beacon->ssid && strncmp(info->beacon->ssid,
+							(const char *)bss->ssid,
+							sizeof(bss->ssid)) != 0)
+			continue;
+
+		/*
+		 * The kernel may have returned a cached scan, so we have to
+		 * sort out any non-matching frequencies before building the
+		 * report
+		 */
+		if (!bss_in_request_range(info, bss))
+			continue;
+
+		report_len = build_report_for_bss(info, bss, report);
+
+		rrm_build_measurement_report(info, report, report_len, ptr);
+
+		ptr += report_len + 5;
+	}
+
+	return rrm_send_response(info->ifindex, info->from, frame, ptr - frame);
+}
+
+static bool rrm_handle_beacon_table(struct rrm_request_info *info)
+{
+	struct station *station = station_find(info->ifindex);
+	struct l_queue *bss_list;
+
+	bss_list = station_get_bss_list(station);
+	if (!bss_list)
+		return rrm_reject_measurement_request(info,
+						REPORT_REJECT_INCAPABLE);
+
+	if (!rrm_report_results(info, bss_list))
+		l_error("Error reporting beacon table results");
+
+	/*
+	 * Table measurements do not require any async operations so info is
+	 * freed here always.
+	 */
+	rrm_info_destroy(info);
+
+	return true;
+}
+
+static bool rrm_scan_results(int err, struct l_queue *bss_list, void *userdata)
+{
+	struct rrm_request_info *info = userdata;
+
+	l_debug("RRM scan results for %u APs", l_queue_length(bss_list));
+
+	rrm_report_results(info, bss_list);
+	/* We aren't saving this BSS list */
+	return false;
+}
+
+static void rrm_scan_triggered(int err, void *userdata)
+{
+	struct rrm_request_info *info = userdata;
+
+	if (err < 0) {
+		l_error("Could not start RRM scan");
+		rrm_reject_measurement_request(info, REPORT_REJECT_INCAPABLE);
+	}
+}
+
+static bool rrm_handle_beacon_scan(struct rrm_request_info *info,
+					bool passive)
+{
+	struct netdev *netdev = netdev_find(info->ifindex);
+	struct scan_freq_set *freqs = scan_freq_set_new();
+	struct scan_parameters params = { .freqs = freqs, .flush = true };
+	enum scan_band band = scan_oper_class_to_band(NULL,
+						info->beacon->oper_class);
+	uint32_t freq;
+	uint32_t scan_id;
+
+	freq = scan_channel_to_freq(info->beacon->channel, band);
+	scan_freq_set_add(freqs, freq);
+
+	if (passive)
+		scan_id = scan_passive(netdev_get_wdev_id(netdev), freqs,
+						rrm_scan_triggered,
+						rrm_scan_results, info,
+						rrm_info_destroy);
+	else
+		scan_id = scan_active_full(netdev_get_wdev_id(netdev), &params,
+						rrm_scan_triggered,
+						rrm_scan_results, info,
+						rrm_info_destroy);
+
+	scan_freq_set_free(freqs);
+
+	return scan_id != 0;
+}
+
+static bool rrm_verify_beacon_request(const uint8_t *request, size_t len)
+{
+	if (len < 13)
+		return false;
+
+	if (request[6] != RRM_BEACON_REQ_MODE_TABLE) {
+		/*
+		 * Rejecting any iterative measurements, only accepting explicit
+		 * channels and operating classes except for table measurements.
+		 */
+		if (request[0] == 0 || request[0] == 255 ||
+					request[1] == 0 || request[1] == 255)
+			return false;
+
+		/*
+		 * Not handling interval/duration requests. We can omit this
+		 * check for table requests since we just return whatever we
+		 * have cached.
+		 */
+		if (!util_mem_is_zero(request + 2, 4))
+			return false;
+	}
+
+	/* Check this is a valid operating class */
+	if (!scan_oper_class_to_band(NULL, request[0]))
+		return false;
+
+	return true;
+}
+
+static bool rrm_handle_beacon_request(struct rrm_request_info *info,
+					const uint8_t *request, size_t len)
+{
+	struct ie_tlv_iter iter;
+	/*
+	 * 802.11-2016 - Table 9-90
+	 *
+	 * "All fixed-length fields and elements (default, used when Reporting
+	 *  Detail subelement is not included in a Beacon request)"
+	 */
+	uint8_t detail = REPORT_DETAIL_NO_FIELDS_OR_ELEMS;
+
+	if (!rrm_verify_beacon_request(request, len))
+		return rrm_reject_measurement_request(info,
+							REPORT_REJECT_REFUSED);
+
+	info->beacon = l_new(struct rrm_beacon_req_info, 1);
+
+	info->beacon->oper_class = request[0];
+	info->beacon->channel = request[1];
+	memcpy(info->beacon->bssid, request + 7, 6);
+
+	ie_tlv_iter_init(&iter, request + 13, len - 13);
+
+	while (ie_tlv_iter_next(&iter)) {
+		uint8_t length = ie_tlv_iter_get_length(&iter);
+		const unsigned char *data = ie_tlv_iter_get_data(&iter);
+
+		switch (ie_tlv_iter_get_tag(&iter)) {
+		case RRM_BEACON_REQ_SUBELEM_ID_SSID:
+			/*
+			 * Zero length is wildcard SSID, which has the same
+			 * effect as no SSID.
+			 */
+			if (length > 0 && length < 32)
+				info->beacon->ssid = l_strndup(
+							(const char *)data,
+							length);
+
+			break;
+		case RRM_BEACON_REQ_SUBELEM_ID_REPORTING_DETAIL:
+			if (length != 1) {
+				l_error("Invalid length in reporting detail");
+				return false;
+			}
+
+			detail = l_get_u8(data);
+			break;
+		case RRM_BEACON_REQ_SUBELEM_ID_BEACON_REPORTING:
+			/*
+			 * 802.11-2016 9.4.2.21.7
+			 *
+			 * "The Beacon reporting subelement is optionally
+			 *  present in a Beacon request for repeated
+			 *  measurements; otherwise it is not present"
+			 *
+			 * Since repeated measurements are not supported we can
+			 * reject this request now.
+			 */
+		case RRM_BEACON_REQ_SUBELEM_ID_AP_CHAN_REPORT:
+			/*
+			 * Only supporting single channel requests
+			 */
+			return rrm_reject_measurement_request(info,
+						REPORT_REJECT_INCAPABLE);
+		}
+	}
+
+	/*
+	 * TODO: Support Reported Frame Body of 1 and 2. This requires that all
+	 * fixed length fields are available from the scan request. Currently
+	 * scan.c parses out only the details we care about. There is also
+	 * limitations on length, and some IEs are treated specially and
+	 * truncated. This adds quite a bit of complexity. For now skip these
+	 * types of frame body reports.
+	 */
+	if (detail != REPORT_DETAIL_NO_FIELDS_OR_ELEMS) {
+		l_debug("Unsupported report detail");
+		return rrm_reject_measurement_request(info,
+						REPORT_REJECT_INCAPABLE);
+	}
+
+	/* Mode */
+	switch (request[6]) {
+	case RRM_BEACON_REQ_MODE_PASSIVE:
+		return rrm_handle_beacon_scan(info, true);
+	case RRM_BEACON_REQ_MODE_ACTIVE:
+		return rrm_handle_beacon_scan(info, false);
+	case RRM_BEACON_REQ_MODE_TABLE:
+		return rrm_handle_beacon_table(info);
+	default:
+		l_error("Unknown beacon mode %u", request[6]);
+		return rrm_reject_measurement_request(info,
+							REPORT_REJECT_REFUSED);
+	}
+}
+
+static bool rrm_handle_measurement_request(struct rrm_request_info *info,
+				const uint8_t *data, size_t len)
+{
+	if (len < 3)
+		return false;
+
+	info->mtoken = data[0];
+	info->mode = data[1];
+	info->type = data[2];
+
+	/* 'Enable' bit is set */
+	if (util_is_bit_set(info->mode, 1)) {
+		/*
+		 * 802.11-2016 11.11.8
+		 *
+		 * "A STA may also refuse to enable triggered autonomous
+		 * reporting. In this case a Measurement Report element shall be
+		 * returned to the requesting STA with the refused bit set to 1"
+		 *
+		 * At least for the time being, we will not support autonomous
+		 * reporting, so decline any request to do so.
+		 */
+		return rrm_reject_measurement_request(info,
+							REPORT_REJECT_REFUSED);
+	}
+
+	/* TODO: handle other measurement types */
+	switch (info->type) {
+	case 5: /* Beacon Request */
+		return rrm_handle_beacon_request(info, data + 3, len - 3);
+	default:
+		l_error("Measurement type %u not supported", info->type);
+		return rrm_reject_measurement_request(info,
+						REPORT_REJECT_INCAPABLE);
+	}
+}
+
+static void rrm_unicast_notify(struct l_genl_msg *msg, void *user_data)
+{
+	struct station *station;
+	struct scan_bss *bss;
+	const struct mmpdu_header *mpdu = NULL;
+	const uint8_t *request;
+	struct l_genl_attr attr;
+	uint16_t type, len;
+	uint16_t frame_len = 0;
+	const void *data;
+	uint8_t cmd;
+	uint32_t ifindex = 0;
+	struct ie_tlv_iter iter;
+
+	cmd = l_genl_msg_get_command(msg);
+	if (cmd != NL80211_CMD_FRAME)
+		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;
+
+	station = station_find(ifindex);
+	if (!station)
+		return;
+
+	if (station_get_state(station) != STATION_STATE_CONNECTED)
+		return;
+
+	bss = station_get_connected_bss(station);
+	if (!bss)
+		return;
+
+	/* Not from connected AP */
+	if (memcmp(bss->addr, mpdu->address_2, 6) != 0)
+		return;
+
+	request = mmpdu_body(mpdu);
+
+	frame_len -= mmpdu_header_len(mpdu);
+
+	if (frame_len < 5)
+		return;
+
+	if (request[0] != 0x05)
+		return;
+
+	if (request[1] != 0x00)
+		return;
+
+	/*
+	 * We have reached our max requests per second, no point in continuing
+	 */
+	if (l_time_now() - last_request_us < MIN_MICROS_BETWEEN_REQUESTS) {
+		l_debug("Max requests per second reached, ignoring request");
+		return;
+	}
+
+	/* Update time regardless of success */
+	last_request_us = l_time_now();
+
+	ie_tlv_iter_init(&iter, request + 5, frame_len - 5);
+
+	while (ie_tlv_iter_next(&iter)) {
+		struct rrm_request_info *info;
+
+		if (ie_tlv_iter_get_tag(&iter) != IE_TYPE_MEASUREMENT_REQUEST)
+			continue;
+
+		info = l_new(struct rrm_request_info, 1);
+
+		info->ifindex = ifindex;
+		memcpy(info->from, mpdu->address_2, 6);
+		info->dialog_token = request[2];
+
+		if (!rrm_handle_measurement_request(info,
+					ie_tlv_iter_get_data(&iter),
+					ie_tlv_iter_get_length(&iter))) {
+			/*
+			 * A failure here means there was some problem
+			 * responding, or the request data was invalid. In
+			 * either case its best to just bail on this set of
+			 * requests.
+			 */
+			rrm_info_destroy(info);
+			return;
+		}
+	}
+}
+
+static int rrm_init(void)
+{
+	struct l_genl *genl = iwd_get_genl();
+
+	nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
+
+	netdev_watch =  netdev_watch_add(rrm_netdev_watch, NULL, NULL);
+
+	unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
+						rrm_unicast_notify,
+						NULL, NULL);
+
+	last_request_us = l_time_now();
+
+	return 0;
+}
+
+static void rrm_exit(void)
+{
+	struct l_genl *genl = iwd_get_genl();
+
+	l_genl_family_free(nl80211);
+	nl80211 = NULL;
+
+	netdev_watch_remove(netdev_watch);
+
+	l_genl_remove_unicast_watch(genl, unicast_watch);
+}
+
+IWD_MODULE(rrm, rrm_init, rrm_exit);
+IWD_MODULE_DEPENDS(rrm, netdev);
-- 
2.17.1

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

* [PATCH 5/5] auto-t: add RRM autotest
  2019-10-29 17:50 [PATCH 1/5] auto-t: support hostapd event processing James Prestwood
                   ` (2 preceding siblings ...)
  2019-10-29 17:50 ` [PATCH 4/5] rrm: add radio resource management module James Prestwood
@ 2019-10-29 17:50 ` James Prestwood
  2019-11-04 20:42 ` [PATCH 1/5] auto-t: support hostapd event processing Denis Kenzior
  4 siblings, 0 replies; 11+ messages in thread
From: James Prestwood @ 2019-10-29 17:50 UTC (permalink / raw)
  To: iwd

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

This test merely verifies hostapd receieved our measurement reports
and verified they were valid. Hostapd does not verify the actual
beacon report body. Really, the only way to test this is on an
actual network which makes these requests.
---
 autotests/testRRM/connection_test.py | 114 +++++++++++++++++++++++++++
 autotests/testRRM/hw.conf            |   8 ++
 autotests/testRRM/ssidOther.conf     |  10 +++
 autotests/testRRM/ssidRRM.conf       |  13 +++
 4 files changed, 145 insertions(+)
 create mode 100644 autotests/testRRM/connection_test.py
 create mode 100644 autotests/testRRM/hw.conf
 create mode 100644 autotests/testRRM/ssidOther.conf
 create mode 100644 autotests/testRRM/ssidRRM.conf

diff --git a/autotests/testRRM/connection_test.py b/autotests/testRRM/connection_test.py
new file mode 100644
index 00000000..93fd6cb8
--- /dev/null
+++ b/autotests/testRRM/connection_test.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python3
+
+import unittest
+import sys
+
+sys.path.append('../util')
+import iwd
+from iwd import IWD
+from iwd import PSKAgent
+from iwd import NetworkType
+from hostapd import HostapdCLI
+import testutil
+
+from time import sleep
+
+# Table beacon with wildcard BSSID
+basic_beacon = '51000000000002ffffffffffff020100'
+# Table beacon with wildcard BSSID and SSID filter
+beacon_with_ssid = '51000000000002ffffffffffff02010000077373696452524d'
+# Passive beacon with wildcard BSSID
+beacon_passive = '510b0000000000ffffffffffff020100'
+# Active beacon with wildcard BSSID
+beacon_active = '510b0000000001ffffffffffff020100'
+
+class Test(unittest.TestCase):
+
+    def test_connection_success(self):
+        hapd = HostapdCLI(config='ssidRRM.conf')
+        wd = IWD()
+
+        psk_agent = PSKAgent("secret123")
+        wd.register_psk_agent(psk_agent)
+
+        devices = wd.list_devices(1)
+        device = devices[0]
+
+        condition = 'not obj.scanning'
+        wd.wait_for_object_condition(device, condition)
+
+        device.scan()
+
+        condition = 'not obj.scanning'
+        wd.wait_for_object_condition(device, condition)
+
+        ordered_network = device.get_ordered_network('ssidRRM')
+
+        self.assertEqual(ordered_network.type, NetworkType.psk)
+
+        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()
+
+        hapd.wait_for_event('AP-STA-CONNECTED')
+
+        # This should return both APs
+        hapd.req_beacon(device.address, basic_beacon)
+
+        for e in ['BEACON-RESP-RX', 'BEACON-RESP-RX']:
+            event = hapd.wait_for_event(e)
+            if event:
+                print(event)
+
+        sleep(0.5)
+
+        # This should return just ssidRRM
+        hapd.req_beacon(device.address, beacon_with_ssid)
+        event = hapd.wait_for_event('BEACON-RESP-RX')
+        if event:
+            print(event)
+
+        sleep(0.5)
+
+        # This should passive scan on channel 11, returning otherSSID
+        hapd.req_beacon(device.address, beacon_passive)
+        # TODO: See if we are scanning here (scan not initiated from station)
+
+        event = hapd.wait_for_event('BEACON-RESP-RX')
+        if event:
+            print(event)
+
+        sleep(0.5)
+
+        # This should active scan on channel 11, returning otherSSID
+        hapd.req_beacon(device.address, beacon_active)
+        # TODO: See if we are scanning here (scan not initiated from station)
+
+        event = hapd.wait_for_event('BEACON-RESP-RX')
+        if event:
+            print(event)
+
+        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):
+        pass
+
+    @classmethod
+    def tearDownClass(cls):
+        IWD.clear_storage()
+
+if __name__ == '__main__':
+    unittest.main(exit=True)
diff --git a/autotests/testRRM/hw.conf b/autotests/testRRM/hw.conf
new file mode 100644
index 00000000..91c757ce
--- /dev/null
+++ b/autotests/testRRM/hw.conf
@@ -0,0 +1,8 @@
+[SETUP]
+num_radios=3
+max_test_exec_interval_sec=40
+reg_domain=US
+
+[HOSTAPD]
+rad0=ssidRRM.conf
+rad1=ssidOther.conf
diff --git a/autotests/testRRM/ssidOther.conf b/autotests/testRRM/ssidOther.conf
new file mode 100644
index 00000000..e0f0fd40
--- /dev/null
+++ b/autotests/testRRM/ssidOther.conf
@@ -0,0 +1,10 @@
+ctrl_interface=/var/run/hostapd
+
+hw_mode=g
+channel=11
+ssid=ssidOther
+
+wpa=2
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
+rrm_beacon_report=1
diff --git a/autotests/testRRM/ssidRRM.conf b/autotests/testRRM/ssidRRM.conf
new file mode 100644
index 00000000..233392f9
--- /dev/null
+++ b/autotests/testRRM/ssidRRM.conf
@@ -0,0 +1,13 @@
+ctrl_interface=/var/run/hostapd
+
+hw_mode=a
+ssid=ssidRRM
+
+wpa=2
+wpa_pairwise=CCMP
+wpa_passphrase=secret123
+rrm_beacon_report=1
+ht_capab=[HT40+]
+
+ieee80211n=1
+channel=36
-- 
2.17.1

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

* Re: [PATCH 1/5] auto-t: support hostapd event processing
  2019-10-29 17:50 [PATCH 1/5] auto-t: support hostapd event processing James Prestwood
                   ` (3 preceding siblings ...)
  2019-10-29 17:50 ` [PATCH 5/5] auto-t: add RRM autotest James Prestwood
@ 2019-11-04 20:42 ` Denis Kenzior
  4 siblings, 0 replies; 11+ messages in thread
From: Denis Kenzior @ 2019-11-04 20:42 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 10/29/19 12:50 PM, James Prestwood wrote:
> Hostapd has a feature where you can connect to its control socket and
> receive events it generates. Currently we only send commands via this
> socket.
> 
> First we open the socket (/var/run/hostapd/<iface>) and send the
> ATTACH command. This tells hostapd we are ready and after this any
> events will be sent over this socket.
> 
> A new API, wait_for_event, was added which takes an event string and
> waits for some timeout. The glib event loop has been integrated into
> this, though its not technically async since we are selecting over a
> socket which blocks. To mitigate this a small timeout was chosen for
> each select call and then wrapped in a while loop which waits for the
> full timeout.
> ---
>   autotests/util/hostapd.py | 68 +++++++++++++++++++++++++++++++++++++++
>   1 file changed, 68 insertions(+)
> 

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities
  2019-10-29 17:50 ` [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities James Prestwood
@ 2019-11-04 20:43   ` Denis Kenzior
  2019-11-04 22:12     ` James Prestwood
  0 siblings, 1 reply; 11+ messages in thread
From: Denis Kenzior @ 2019-11-04 20:43 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 10/29/19 12:50 PM, James Prestwood wrote:
> This tells AP's that we support Passive, Active, and Table beacon
> measurements.
> ---
>   src/wiphy.c | 2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/src/wiphy.c b/src/wiphy.c
> index 36e6c73d..37d06bc7 100644
> --- a/src/wiphy.c
> +++ b/src/wiphy.c
> @@ -961,6 +961,8 @@ static void wiphy_setup_rm_enabled_capabilities(struct wiphy *wiphy)
>   
>   	wiphy->rm_enabled_capabilities[0] = IE_TYPE_RM_ENABLED_CAPABILITIES;
>   	wiphy->rm_enabled_capabilities[1] = 5;
> +	/* Bits: Passive (4), Active (5), and Beacon Table (6) capabilities */
> +	wiphy->rm_enabled_capabilities[2] = 0x70;

Do you really mean to add all of these or just the Beacon table one?

>   
>   	/*
>   	 * TODO: Support at least Link Measurement if TX_POWER_INSERTION is
> 

Regards,
-Denis

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

* Re: [PATCH 3/5] station: add APIs to get connected BSS and BSS list
  2019-10-29 17:50 ` [PATCH 3/5] station: add APIs to get connected BSS and BSS list James Prestwood
@ 2019-11-04 20:44   ` Denis Kenzior
  0 siblings, 0 replies; 11+ messages in thread
From: Denis Kenzior @ 2019-11-04 20:44 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 10/29/19 12:50 PM, James Prestwood wrote:
> For Radio Resource Management (RRM) we will need access to the currently
> connected BSS as well as the last scan results in order to do certain
> kinds of requested measurements.
> ---
>   src/station.c | 10 ++++++++++
>   src/station.h |  2 ++
>   2 files changed, 12 insertions(+)
> 

Applied, thanks.

Regards,
-Denis

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

* Re: [PATCH 4/5] rrm: add radio resource management module
  2019-10-29 17:50 ` [PATCH 4/5] rrm: add radio resource management module James Prestwood
@ 2019-11-04 21:09   ` Denis Kenzior
  0 siblings, 0 replies; 11+ messages in thread
From: Denis Kenzior @ 2019-11-04 21:09 UTC (permalink / raw)
  To: iwd

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

Hi James,

On 10/29/19 12:50 PM, James Prestwood wrote:
> This module takes care of radio measurements which an AP can request.
> There are many types of requests, and for now only beacon requests
> are supported.
> 
> IWD will filter certain types of beacon requests that are NOT
> supported:
> 
>   - AP channel reports. Only single channel requests will be supported
>   - Autonomous measurements. Only direct requests will be supported.
>     IWD will not accept requets to trigger reports under certain
>     conditions (SNR/RSSI thresholds, etc.)
>   - Timed measurements. Only immediate measurements will be performed.
>     The accuracy for timed measurements cannot be reliably guaranteed
>     due to kernel scheduling/queues.
>   - Full reporting detail. The AP can request the STA return the full
>     set of IEs in a beacon. IWD does not currently save all IEs, plus
>     there is quite a bit of complexity involved as certain IEs get
>     truncated, and there are other length limitations.
> 
> There are other limitations not specific to beacon requests:
> 
>   - IWD will support single measurement requests per report. Multiple
>     measurement request IEs can be included, but the reports will be
>     sent out separately.
> 
>   - IWD will limit the number of requests it responds to in a given
>     amount of time. As it stands now this is hard coded to 2 requests
>     per second maximum. This will prevent DoS attacks.
> 
>   - IWD will not accept any measurement requests from APs it is not
>     connected to, and will not accept any requests until connected.
> ---
>   Makefile.am |   1 +
>   src/rrm.c   | 764 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 765 insertions(+)
>   create mode 100644 src/rrm.c
> 

<snip>

> +/*
> + * Basically the same as 802.11-2016 9.4.2.21.7
> + *
> + * Note: Not packed as this is only for saving values for response
> + */
> +struct rrm_beacon_req_info {
> +	uint8_t oper_class;
> +	uint8_t channel;	/* The single channel provided in request */
> +	uint8_t bssid[6];
> +	char *ssid;

ssid is a fixed length field, so you can store it that way, e.g. char [33];

> +};
> +
> +struct rrm_request_info {
> +	uint32_t ifindex;
> +	uint8_t from[6];
> +	uint8_t dialog_token; /* dialog token in Radio Measurement Request */
> +	uint8_t mtoken; /* token in measurement request element */
> +	uint8_t mode;
> +	uint8_t type;
> +
> +	/* TODO: once more measurements are supported this can be a union */
> +	struct rrm_beacon_req_info *beacon;
> +};

Why not use container_of and store rrm_request_info right in 
rrm_beacon_req_info.

> +
> +static uint64_t last_request_us;
> +static const uint8_t wildcard_bss[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

This seems like a utility method candidate...

<snip>

> +
> +static void rrm_build_measurement_report(struct rrm_request_info *info,
> +				const void *report, size_t report_len,
> +				uint8_t *to)
> +{
> +	*to++ = IE_TYPE_MEASUREMENT_REPORT;
> +	*to++ = 3 + report_len;
> +	*to++ = info->mtoken;
> +	*to++ = 0;
> +	*to++ = info->type;
> +
> +	if (report)
> +		memcpy(to, report, report_len);
> +}
> +
> +static void rrm_register_frame_cb(struct l_genl_msg *msg, void *user_data)
> +{
> +	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));
> +}
> +
> +static void rrm_register_frame(uint32_t ifindex)
> +{
> +	struct l_genl_msg *msg;
> +	uint16_t frame_type = 0x00d0;
> +	uint8_t prefix[] = { 0x05, 0x00 }; /* Radio Measurment Request */
> +
> +	msg = l_genl_msg_new_sized(NL80211_CMD_REGISTER_FRAME, 34);
> +
> +	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);
> +
> +	l_genl_family_send(nl80211, msg, rrm_register_frame_cb,
> +			L_UINT_TO_PTR(frame_type), NULL);
> +}
> +
> +static void rrm_netdev_watch(struct netdev *netdev,
> +				enum netdev_watch_event event, void *user_data)
> +{
> +	switch (event) {
> +	case NETDEV_WATCH_EVENT_NEW:
> +		rrm_register_frame(netdev_get_ifindex(netdev));

Might want to check that we're in station mode here.  You sort of do in 
the actual frame unicast watch callback, but not registering to it 
unless needed in the first place is even better.

Also, any reason for not using netdev_frame_watch_add?

> +		return;
> +	default:
> +		break;
> +	}
> +}
> +

<snip>

> +static bool rrm_handle_beacon_scan(struct rrm_request_info *info,
> +					bool passive)
> +{
> +	struct netdev *netdev = netdev_find(info->ifindex);
> +	struct scan_freq_set *freqs = scan_freq_set_new();
> +	struct scan_parameters params = { .freqs = freqs, .flush = true };
> +	enum scan_band band = scan_oper_class_to_band(NULL,
> +						info->beacon->oper_class);
> +	uint32_t freq;
> +	uint32_t scan_id;
> +
> +	freq = scan_channel_to_freq(info->beacon->channel, band);
> +	scan_freq_set_add(freqs, freq);
> +
> +	if (passive)
> +		scan_id = scan_passive(netdev_get_wdev_id(netdev), freqs,
> +						rrm_scan_triggered,
> +						rrm_scan_results, info,
> +						rrm_info_destroy);
> +	else
> +		scan_id = scan_active_full(netdev_get_wdev_id(netdev), &params,
> +						rrm_scan_triggered,
> +						rrm_scan_results, info,
> +						rrm_info_destroy);
> +
> +	scan_freq_set_free(freqs);

Should we be canceling the scan if station disconnects?

> +
> +	return scan_id != 0;
> +}
> +

<snip>

Regards,
-Denis

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

* Re: [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities
  2019-11-04 20:43   ` Denis Kenzior
@ 2019-11-04 22:12     ` James Prestwood
  2019-11-04 22:35       ` Denis Kenzior
  0 siblings, 1 reply; 11+ messages in thread
From: James Prestwood @ 2019-11-04 22:12 UTC (permalink / raw)
  To: iwd

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

On Mon, 2019-11-04 at 14:43 -0600, Denis Kenzior wrote:
> Hi James,
> 
> On 10/29/19 12:50 PM, James Prestwood wrote:
> > This tells AP's that we support Passive, Active, and Table beacon
> > measurements.
> > ---
> >   src/wiphy.c | 2 ++
> >   1 file changed, 2 insertions(+)
> > 
> > diff --git a/src/wiphy.c b/src/wiphy.c
> > index 36e6c73d..37d06bc7 100644
> > --- a/src/wiphy.c
> > +++ b/src/wiphy.c
> > @@ -961,6 +961,8 @@ static void
> > wiphy_setup_rm_enabled_capabilities(struct wiphy *wiphy)
> >   
> >   	wiphy->rm_enabled_capabilities[0] =
> > IE_TYPE_RM_ENABLED_CAPABILITIES;
> >   	wiphy->rm_enabled_capabilities[1] = 5;
> > +	/* Bits: Passive (4), Active (5), and Beacon Table (6)
> > capabilities */
> > +	wiphy->rm_enabled_capabilities[2] = 0x70;
> 
> Do you really mean to add all of these or just the Beacon table one?

Yeah this was intended. This lets us support passive and active scan
requests. Did you only want to allow table measurements?

Thanks,
James

> 
> >   
> >   	/*
> >   	 * TODO: Support at least Link Measurement if
> > TX_POWER_INSERTION is
> > 
> 
> Regards,
> -Denis

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

* Re: [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities
  2019-11-04 22:12     ` James Prestwood
@ 2019-11-04 22:35       ` Denis Kenzior
  0 siblings, 0 replies; 11+ messages in thread
From: Denis Kenzior @ 2019-11-04 22:35 UTC (permalink / raw)
  To: iwd

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

Hi James,

>>> +	/* Bits: Passive (4), Active (5), and Beacon Table (6)
>>> capabilities */
>>> +	wiphy->rm_enabled_capabilities[2] = 0x70;
>>
>> Do you really mean to add all of these or just the Beacon table one?
> 
> Yeah this was intended. This lets us support passive and active scan
> requests. Did you only want to allow table measurements?
> 

Ah no, this is fine.  I now realize I mixed up Beacon Table and Beacon 
Request.  So ignore this, the commit is fine.  I'll take it in the next 
version.

Regards,
-Denis

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

end of thread, other threads:[~2019-11-04 22:35 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-29 17:50 [PATCH 1/5] auto-t: support hostapd event processing James Prestwood
2019-10-29 17:50 ` [PATCH 2/5] wiphy: add beacon bits to RM Enabled Capabilities James Prestwood
2019-11-04 20:43   ` Denis Kenzior
2019-11-04 22:12     ` James Prestwood
2019-11-04 22:35       ` Denis Kenzior
2019-10-29 17:50 ` [PATCH 3/5] station: add APIs to get connected BSS and BSS list James Prestwood
2019-11-04 20:44   ` Denis Kenzior
2019-10-29 17:50 ` [PATCH 4/5] rrm: add radio resource management module James Prestwood
2019-11-04 21:09   ` Denis Kenzior
2019-10-29 17:50 ` [PATCH 5/5] auto-t: add RRM autotest James Prestwood
2019-11-04 20:42 ` [PATCH 1/5] auto-t: support hostapd event processing Denis Kenzior

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.