[PATCH v3 12/19] hostapd: Add range request

Ilan Peer ilan.peer at intel.com
Wed Apr 6 09:42:12 PDT 2016


From: David Spinadel <david.spinadel at intel.com>

Add range request via RRM. The AP sends Radio measurement request
with FTM range request, the receiving STA sends FTM requests to the
given list of APs. The neighbor report part of the request is taken
from neighbor DB.

The command API is:

req_range <DEST_ADDR> <rand_interval> <min_ap> <responder> [Additional responders]

Signed-off-by: David Spinadel <david.spinadel at intel.com>
---
 hostapd/ctrl_iface.c         |  58 ++++++++++++++++
 hostapd/hostapd_cli.c        |  30 ++++++++
 src/ap/hostapd.h             |   3 +
 src/ap/neighbor_db.c         |  11 +--
 src/ap/neighbor_db.h         |   4 ++
 src/ap/rrm.c                 | 162 +++++++++++++++++++++++++++++++++++++++++++
 src/ap/rrm.h                 |   9 +++
 src/common/ieee802_11_defs.h |   7 ++
 8 files changed, 279 insertions(+), 5 deletions(-)

diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 9a188b7..905a1c6 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2088,6 +2088,61 @@ static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
 }
 
 
+int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	char *token, *context = NULL;
+	int random_interval, min_ap;
+	u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
+	unsigned int n_responders;
+
+	token = str_token(cmd, " ", &context);
+	if (!token || hwaddr_aton(token, addr)) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: Request range - bad destination address");
+		return -1;
+	}
+
+	token = str_token(cmd, " ", &context);
+	if (!token)
+		return -1;
+
+	random_interval = atoi(token);
+
+	token = str_token(cmd, " ", &context);
+	if (!token)
+		return -1;
+
+	min_ap = atoi(token);
+
+	n_responders = 0;
+	while ((token = str_token(cmd, " ", &context))) {
+		if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL: Request range - too many responders");
+			return -1;
+		}
+
+		if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL: Request range - bad responder address");
+			return -1;
+		}
+
+		n_responders++;
+	}
+
+	if (!n_responders) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: Request range - no FTM responder address");
+		return -1;
+	}
+
+	return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
+				       responders, n_responders);
+}
+
+
 static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
 {
 	struct wpa_ssid_value ssid;
@@ -2461,6 +2516,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
 	} else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
 		if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
+		if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
+			reply_len = -1;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index b2e0de1..d46f20c 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1158,6 +1158,35 @@ static int hostapd_cli_req_lci(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int hostapd_cli_req_range(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[512];
+	char *pos  = cmd, *end = cmd + sizeof(cmd);
+	int i, res;
+
+	if (argc < 4) {
+		printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count and 1 to 16 AP addresses\n");
+		return -1;
+	}
+
+	res = os_snprintf(pos, end - pos, "%s", "REQ_RANGE ");
+	if (os_snprintf_error(end - pos, res))
+		return -1;
+	pos += res;
+
+	for (i = 0; i < argc; i++) {
+		res = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, res)) {
+			printf("Too long REQ_RANGE command");
+			return -1;
+		}
+		pos += res;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
@@ -1269,6 +1298,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	{ "set_neighbor", hostapd_cli_cmd_set_neighbor },
 	{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor },
 	{ "req_lci", hostapd_cli_req_lci },
+	{ "req_range", hostapd_cli_req_range },
 	{ NULL, NULL }
 };
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index c3d7807..6961d37 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -301,6 +301,9 @@ struct hostapd_data {
 
 	u8 lci_req_token;
 	int lci_req_active;
+
+	u8 range_req_token;
+	int range_req_active;
 };
 
 
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index e3084e3..985b4a3 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -13,16 +13,17 @@
 #include "neighbor_db.h"
 
 
-static struct hostapd_neighbor_entry
-*hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
-		      struct wpa_ssid_value *ssid)
+struct hostapd_neighbor_entry *hostapd_neighbor_get(struct hostapd_data *hapd,
+						    const u8 *bssid,
+						    struct wpa_ssid_value *ssid)
 {
 	struct hostapd_neighbor_entry *nr;
 
 	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list)
 		if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
-		    ssid->ssid_len == nr->ssid.ssid_len &&
-		    os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) == 0)
+		    (!ssid || (ssid->ssid_len == nr->ssid.ssid_len &&
+				os_memcmp(ssid->ssid, nr->ssid.ssid,
+					  ssid->ssid_len) == 0)))
 			return nr;
 	return NULL;
 }
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index 35084a7..6c4139f 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -10,6 +10,10 @@
 #ifndef NEIGHBOR_DB_H
 #define NEIGHBOR_DB_H
 
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd,
+		     const u8 *bssid,
+		     struct wpa_ssid_value *ssid);
 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
 			 struct wpa_ssid_value *ssid, struct wpabuf *nr,
 			 struct wpabuf *lci, struct wpabuf *civic);
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index 3aaa2cf..fb4ce0e 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -7,6 +7,7 @@
  * See README for more details.
  */
 
+#include <limits.h>
 #include "utils/includes.h"
 #include "utils/common.h"
 #include "hostapd.h"
@@ -28,6 +29,15 @@ static void hoastapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
 }
 
 
+static void hoastapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: range request timed out");
+	hapd->range_req_active = 0;
+}
+
+
 static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
 				      const u8 *pos, size_t len)
 {
@@ -43,6 +53,22 @@ static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
 }
 
 
+static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
+					const u8 *pos, size_t len)
+{
+	if (!hapd->range_req_active || hapd->range_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected range report, token %d",
+			   token);
+	} else {
+		hapd->range_req_active = 0;
+		eloop_cancel_timeout(hoastapd_range_rep_timeout_handler, hapd,
+				     NULL);
+		wpa_printf(MSG_DEBUG, "Range report token %d len %zu", token,
+			   len);
+	}
+}
+
+
 static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
 					     const u8 *buf, size_t len)
 {
@@ -65,6 +91,9 @@ static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
 		case MEASURE_TYPE_LCI:
 			hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
 			break;
+		case MEASURE_TYPE_FTM_RANGE:
+			hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
+			break;
 		default:
 			wpa_printf(MSG_DEBUG,
 				   "Measurement report type %u is not supported",
@@ -384,10 +413,143 @@ int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
 }
 
 
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   int random_interval, int min_ap,
+			   const u8 *responders, unsigned int n_responders)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta;
+	u8 *len;
+	unsigned int i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Request range: dest addr" MACSTR
+		   "rand interval %d min ap %d n_responders %d", MAC2STR(addr),
+		   random_interval, min_ap, n_responders);
+
+	if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP ||
+	    (unsigned int)min_ap > n_responders) {
+		wpa_printf(MSG_ERROR, "Request range: wrong min AP count");
+		return -1;
+	}
+
+	if (random_interval >= USHRT_MAX) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: too big randomization interval");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: destination address isn't in station list");
+		return -1;
+	}
+
+	if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: destination address isn't connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_RANGE_REPORT)) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: receiving station doesn't support range report in RRM");
+		return -1;
+	}
+
+	if (hapd->range_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request range: range request is already in process; overriding.");
+		hapd->range_req_active = 0;
+		eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+				       hoastapd_range_rep_timeout_handler, hapd,
+				       NULL);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	buf = wpabuf_alloc(7 + 255);
+	if (!buf)
+		return -1;
+
+	hapd->range_req_token++;
+	if (!hapd->range_req_token) /* For wraparounds */
+		hapd->range_req_token++;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->range_req_token);
+	/* Num of repetitions */
+	wpabuf_put_le16(buf, 0);
+
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	/* Len will be set later */
+	len = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+	wpabuf_put_u8(buf, 0);
+
+	/* Measurement token */
+	wpabuf_put_u8(buf, 1);
+	/* Parallel and enable bits are 0, duration, request and report are
+	 * reserved
+	 */
+	wpabuf_put_u8(buf, 0);
+	wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE);
+
+	wpabuf_put_le16(buf, random_interval);
+	wpabuf_put_u8(buf, min_ap);
+
+	/* Taking the neighbor report part of the range request from neighbor
+	 * DB (instead of requesting the separate bits of data from the user).
+	 */
+	for (i = 0; i < n_responders; i++) {
+		struct hostapd_neighbor_entry *nr;
+
+		nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
+					  NULL);
+		if (!nr) {
+			wpa_printf(MSG_ERROR, "Missing neighbor report for"
+				   MACSTR, MAC2STR(responders + ETH_ALEN * i));
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
+			wpa_printf(MSG_ERROR, "Too long range request");
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, wpabuf_len(nr->nr));
+		wpabuf_put_buf(buf, nr->nr);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	*len = wpabuf_len(buf) - 7;
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->range_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hoastapd_range_rep_timeout_handler,
+			       hapd, NULL);
+
+	return 0;
+}
+
+
 void hostapd_clean_rrm(struct hostapd_data *hapd)
 {
 	hostpad_free_neighbor_db(hapd);
 	eloop_cancel_timeout(hoastapd_lci_rep_timeout_handler, hapd,
 			     NULL);
 	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hoastapd_range_rep_timeout_handler, hapd,
+			     NULL);
+	hapd->range_req_active = 0;
 }
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
index 08ae438..ac63777 100644
--- a/src/ap/rrm.h
+++ b/src/ap/rrm.h
@@ -10,10 +10,19 @@
 #ifndef RRM_H
 #define RRM_H
 
+/* Max measure request length is 255, -6 of the body we have 249 for the
+ * neighbor report elements. Each neighbor report element is at least 2 + 13
+ * bytes, so we can't have more than 16 responders in the request.
+ */
+#define RRM_RANGE_REQ_MAX_RESPONDERS 16
+
 void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
 				      const u8 *buf, size_t len);
 
 int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   int random_interval, int min_ap,
+			   const u8 *responders, unsigned int n_responders);
 void hostapd_clean_rrm(struct hostapd_data *hapd);
 
 #endif /* RRM_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 5f548dd..87addaf 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -368,6 +368,13 @@
 #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
 /* byte 2 (out of 5) */
 #define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4)
+/* byte 5 (out of 5) */
+#define WLAN_RRM_CAPS_RANGE_REPORT BIT(2)
+
+/* 802.11 MC D4.3 spec - 8.4.2.20.19 (Fine Timing Measurement Range request) -
+ * Minimum AP count
+ */
+#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15
 
 /* Timeout Interval Type */
 #define WLAN_TIMEOUT_REASSOC_DEADLINE 1
-- 
1.9.1




More information about the Hostap mailing list