]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Add FTM range request
authorDavid Spinadel <david.spinadel@intel.com>
Wed, 6 Apr 2016 16:42:12 +0000 (19:42 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 17 Apr 2016 09:29:12 +0000 (12:29 +0300)
Add FTM range request via RRM. The AP sends Radio measurement request
with FTM range request as a request for the receiving STA to send FTM
requests to the given list of APs. The neighbor report part of the
request is taken from the neighbor database.

The control interface command is:

REQ_RANGE <dst addr> <rand_int> <min_ap> <responder> [<responder>..]

dst addr: MAC address of an associated STA
rand_int: Randomization Interval (0..65535) in TUs
min_ap: Minimum AP Count (1..15); minimum number of requested FTM ranges
between the associated STA and the listed APs
responder: List of BSSIDs for neighboring APs for which a measurement
is requested

Signed-off-by: David Spinadel <david.spinadel@intel.com>
hostapd/ctrl_iface.c
hostapd/hostapd_cli.c
src/ap/hostapd.h
src/ap/neighbor_db.c
src/ap/neighbor_db.h
src/ap/rrm.c
src/ap/rrm.h
src/common/ieee802_11_defs.h

index 4c2b559404359cddc79bd8778456cfb548aac25a..7040069ad9255888eeacb01df91c14c90777421b 100644 (file)
@@ -2087,6 +2087,65 @@ 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_INFO,
+                          "CTRL: REQ_RANGE - Bad destination address");
+               return -1;
+       }
+
+       token = str_token(cmd, " ", &context);
+       if (!token)
+               return -1;
+
+       random_interval = atoi(token);
+       if (random_interval < 0 || random_interval > 0xffff)
+               return -1;
+
+       token = str_token(cmd, " ", &context);
+       if (!token)
+               return -1;
+
+       min_ap = atoi(token);
+       if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
+               return -1;
+
+       n_responders = 0;
+       while ((token = str_token(cmd, " ", &context))) {
+               if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
+                       wpa_printf(MSG_INFO,
+                                  "CTRL: REQ_RANGE: Too many responders");
+                       return -1;
+               }
+
+               if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
+                       wpa_printf(MSG_INFO,
+                                  "CTRL: REQ_RANGE: Bad responder address");
+                       return -1;
+               }
+
+               n_responders++;
+       }
+
+       if (!n_responders) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: REQ_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;
@@ -2457,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;
index 563d8d59ec015e154e808297f7e09b497ee21413..ff133f6aeef2d331a1f14bdcc804d2a22e4e649a 100644 (file)
@@ -1206,6 +1206,18 @@ static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       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;
+       }
+
+       return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv);
+}
+
+
 struct hostapd_cli_cmd {
        const char *cmd;
        int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1270,6 +1282,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_cmd_req_lci },
+       { "req_range", hostapd_cli_cmd_req_range },
        { NULL, NULL }
 };
 
index 76b0ca6d00ca8c8bde9c9aaf3b8a7cb00ab98e9d..4dba8cb3606e02524df7a0667e182a7b8c770f4b 100644 (file)
@@ -300,7 +300,9 @@ struct hostapd_data {
        struct dl_list nr_db;
 
        u8 lci_req_token;
+       u8 range_req_token;
        unsigned int lci_req_active:1;
+       unsigned int range_req_active:1;
 };
 
 
index 1156aa2adb7d3f5fafa2ee8898d74f7e15747acc..a2efff6182868dda0a55c2238592dfc780add645 100644 (file)
@@ -14,7 +14,7 @@
 #include "neighbor_db.h"
 
 
-static struct hostapd_neighbor_entry *
+struct hostapd_neighbor_entry *
 hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
                     const struct wpa_ssid_value *ssid)
 {
@@ -23,8 +23,10 @@ hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
        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;
index 40c42e725cdad69a85c9f2684d7712870670a699..c22e043c120ef0bb1b3816ad124760d85f7758e4 100644 (file)
@@ -10,6 +10,9 @@
 #ifndef NEIGHBOR_DB_H
 #define NEIGHBOR_DB_H
 
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+                    const struct wpa_ssid_value *ssid);
 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
                         const struct wpa_ssid_value *ssid,
                         const struct wpabuf *nr, const struct wpabuf *lci,
index 8548ea406b5aacf2d3230a7c3425661a9896a8b1..3569f955bcd27351ccd8cb488d71b406c2c83180 100644 (file)
@@ -44,6 +44,31 @@ static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
 }
 
 
+static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+       struct hostapd_data *hapd = eloop_data;
+
+       wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
+                  hapd->range_req_token);
+       hapd->range_req_active = 0;
+}
+
+
+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 %u",
+                          token);
+               return;
+       }
+
+       hapd->range_req_active = 0;
+       eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+       wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
+}
+
+
 static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
                                             const u8 *buf, size_t len)
 {
@@ -67,6 +92,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",
@@ -386,9 +414,131 @@ int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
 }
 
 
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+                          u16 random_interval, u8 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 %u min AP %u n_responders %u", MAC2STR(addr),
+                  random_interval, min_ap, n_responders);
+
+       if (min_ap == 0 || min_ap > n_responders) {
+               wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
+               return -1;
+       }
+
+       sta = ap_get_sta(hapd, addr);
+       if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+               wpa_printf(MSG_INFO,
+                          "Request range: Destination address is not connected");
+               return -1;
+       }
+
+       if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
+               wpa_printf(MSG_ERROR,
+                          "Request range: Destination station does not support FTM 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,
+                                      hostapd_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++;
+
+       /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
+       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); /* Dialog Token */
+       wpabuf_put_le16(buf, 0); /* Number of Repetitions */
+
+       /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
+       wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+       len = wpabuf_put(buf, 1); /* Length will be set later */
+
+       wpabuf_put_u8(buf, 1); /* Measurement Token */
+       /*
+        * Parallel and Enable bits are 0; Duration, Request, and Report are
+        * reserved.
+        */
+       wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+       wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
+
+       /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
+       wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
+       wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
+
+       /* FTM Range Subelements */
+
+       /*
+        * Taking the neighbor report part of the range request from neighbor
+        * database 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_INFO, "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,
+                              hostapd_range_rep_timeout_handler, hapd, NULL);
+
+       return 0;
+}
+
+
 void hostapd_clean_rrm(struct hostapd_data *hapd)
 {
        hostpad_free_neighbor_db(hapd);
        eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
        hapd->lci_req_active = 0;
+       eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+       hapd->range_req_active = 0;
 }
index f3e15bd7385399f5db13b7f34f32b9029fd72cd7..f07fd41ac0195d612d1224cc109a885a66d8d0a4 100644 (file)
 #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,
+                          u16 random_interval, u8 min_ap,
+                          const u8 *responders, unsigned int n_responders);
 void hostapd_clean_rrm(struct hostapd_data *hapd);
 
 #endif /* RRM_H */
index cbf8336fd86c9ec5de5875cb92dd1553b5c5b6bb..5be747b461ed5ae49fcc0bc894b34b4e9e7ea2d8 100644 (file)
 #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_FTM_RANGE_REPORT BIT(2)
+
+/*
+ * IEEE P802.11-REVmc/D5.0, 9.4.2.21.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