@@ -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;
@@ -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 }
};
@@ -301,6 +301,9 @@ struct hostapd_data {
u8 lci_req_token;
int lci_req_active;
+
+ u8 range_req_token;
+ int range_req_active;
};
@@ -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;
}
@@ -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);
@@ -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_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;
}
@@ -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 */
@@ -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