]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Add BSS_TM_REQ command to send BSS Transition Management Request
authorJouni Malinen <jouni@qca.qualcomm.com>
Sat, 22 Nov 2014 09:31:03 +0000 (11:31 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 22 Nov 2014 14:34:34 +0000 (16:34 +0200)
hostapd control interface can now be used to request transmission of a
BSS Transition Management Request frame to a specified station.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
hostapd/ctrl_iface.c
hostapd/hostapd_cli.c
src/ap/wnm_ap.c
src/ap/wnm_ap.h

index 162a642e1ad37c502f1cd4265177b4492763b356..fa0e81fa04ba7cb3453af2a8a7d87ac04194d41a 100644 (file)
@@ -863,6 +863,193 @@ static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
        return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
 }
 
+
+static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+                                        const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       const char *pos, *end;
+       int disassoc_timer = 0;
+       struct sta_info *sta;
+       u8 req_mode = 0, valid_int = 0x01;
+       u8 bss_term_dur[12];
+       char *url = NULL;
+       int ret;
+       u8 nei_rep[1000];
+       u8 *nei_pos = nei_rep;
+
+       if (hwaddr_aton(cmd, addr)) {
+               wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+               return -1;
+       }
+
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "Station " MACSTR
+                          " not found for BSS TM Request message",
+                          MAC2STR(addr));
+               return -1;
+       }
+
+       pos = os_strstr(cmd, " disassoc_timer=");
+       if (pos) {
+               pos += 16;
+               disassoc_timer = atoi(pos);
+               if (disassoc_timer < 0 || disassoc_timer > 65535) {
+                       wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+                       return -1;
+               }
+       }
+
+       pos = os_strstr(cmd, " valid_int=");
+       if (pos) {
+               pos += 11;
+               valid_int = atoi(pos);
+       }
+
+       pos = os_strstr(cmd, " bss_term=");
+       if (pos) {
+               pos += 10;
+               req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+               /* TODO: TSF configurable/learnable */
+               bss_term_dur[0] = 4; /* Subelement ID */
+               bss_term_dur[1] = 10; /* Length */
+               os_memset(bss_term_dur, 2, 8);
+               end = os_strchr(pos, ',');
+               if (end == NULL) {
+                       wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+                       return -1;
+               }
+               end++;
+               WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+       }
+
+
+       /*
+        * BSS Transition Candidate List Entries - Neighbor Report elements
+        * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+        * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+        */
+       pos = cmd;
+       while (pos) {
+               u8 *nei_start;
+               long int val;
+               char *endptr, *tmp;
+
+               pos = os_strstr(pos, " neighbor=");
+               if (!pos)
+                       break;
+               if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Not enough room for additional neighbor");
+                       return -1;
+               }
+               pos += 10;
+
+               nei_start = nei_pos;
+               *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+               nei_pos++; /* length to be filled in */
+
+               if (hwaddr_aton(pos, nei_pos)) {
+                       wpa_printf(MSG_DEBUG, "Invalid BSSID");
+                       return -1;
+               }
+               nei_pos += ETH_ALEN;
+               pos += 17;
+               if (*pos != ',') {
+                       wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+                       return -1;
+               }
+               pos++;
+
+               val = strtol(pos, &endptr, 0);
+               WPA_PUT_LE32(nei_pos, val);
+               nei_pos += 4;
+               if (*endptr != ',') {
+                       wpa_printf(MSG_DEBUG, "Missing Operating Class");
+                       return -1;
+               }
+               pos = endptr + 1;
+
+               *nei_pos++ = atoi(pos); /* Operating Class */
+               pos = os_strchr(pos, ',');
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG, "Missing Channel Number");
+                       return -1;
+               }
+               pos++;
+
+               *nei_pos++ = atoi(pos); /* Channel Number */
+               pos = os_strchr(pos, ',');
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG, "Missing PHY Type");
+                       return -1;
+               }
+               pos++;
+
+               *nei_pos++ = atoi(pos); /* PHY Type */
+               end = os_strchr(pos, ' ');
+               tmp = os_strchr(pos, ',');
+               if (tmp && (!end || tmp < end)) {
+                       /* Optional Subelements (hexdump) */
+                       size_t len;
+
+                       pos = tmp + 1;
+                       end = os_strchr(pos, ' ');
+                       if (end)
+                               len = end - pos;
+                       else
+                               len = os_strlen(pos);
+                       if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "Not enough room for neighbor subelements");
+                               return -1;
+                       }
+                       if (len & 0x01 ||
+                           hexstr2bin(pos, nei_pos, len / 2) < 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "Invalid neighbor subelement info");
+                               return -1;
+                       }
+                       nei_pos += len / 2;
+                       pos = end;
+               }
+
+               nei_start[1] = nei_pos - nei_start - 2;
+       }
+
+       pos = os_strstr(cmd, " url=");
+       if (pos) {
+               size_t len;
+               pos += 5;
+               end = os_strchr(pos, ' ');
+               if (end)
+                       len = end - pos;
+               else
+                       len = os_strlen(pos);
+               url = os_malloc(len + 1);
+               if (url == NULL)
+                       return -1;
+               os_memcpy(url, pos, len);
+               url[len] = '\0';
+               req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+       }
+
+       if (os_strstr(cmd, " pref=1"))
+               req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+       if (os_strstr(cmd, " abridged=1"))
+               req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+       if (os_strstr(cmd, " disassoc_imminent=1"))
+               req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+       ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+                                 valid_int, bss_term_dur, url,
+                                 nei_pos > nei_rep ? nei_rep : NULL,
+                                 nei_pos - nei_rep);
+       os_free(url);
+       return ret;
+}
+
 #endif /* CONFIG_WNM */
 
 
@@ -1725,6 +1912,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
                if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+               if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
+                       reply_len = -1;
 #endif /* CONFIG_WNM */
        } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
                reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
index 283e20fb08cc91aa1d05e1929a20f03105a97d7d..61217629301951e4dff28eb9883c7ac0c3397f7c 100644 (file)
@@ -622,6 +622,33 @@ static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char buf[2000], *tmp;
+       int res, i, total;
+
+       if (argc < 1) {
+               printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
+               return -1;
+       }
+
+       res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
+       if (res < 0 || res >= (int) sizeof(buf))
+               return -1;
+
+       total = res;
+       for (i = 1; i < argc; i++) {
+               tmp = &buf[total];
+               res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
+               if (res < 0 || (size_t) res >= sizeof(buf) - total - 1)
+                       return -1;
+               total += res;
+       }
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
 static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
                                      char *argv[])
 {
@@ -1010,6 +1037,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
 #endif /* CONFIG_WPS */
        { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
        { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+       { "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
        { "get_config", hostapd_cli_cmd_get_config },
        { "help", hostapd_cli_cmd_help },
        { "interface", hostapd_cli_cmd_interface },
index cf25dbb688c754ba76064e11635473f40cf352ad..a24900a1ffb9f47e1d02af0c45e63c487b50939f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - WNM
- * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -436,6 +436,34 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
 }
 
 
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+                              int disassoc_timer)
+{
+       int timeout, beacon_int;
+
+       /*
+        * Prevent STA from reconnecting using cached PMKSA to force
+        * full authentication with the authentication server (which may
+        * decide to reject the connection),
+        */
+       wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+       beacon_int = hapd->iconf->beacon_int;
+       if (beacon_int < 1)
+               beacon_int = 100; /* best guess */
+       /* Calculate timeout in ms based on beacon_int in TU */
+       timeout = disassoc_timer * beacon_int * 128 / 125;
+       wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+                  " set to %d ms", MAC2STR(sta->addr), timeout);
+
+       sta->timeout_next = STA_DISASSOC_FROM_CLI;
+       eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+       eloop_register_timeout(timeout / 1000,
+                              timeout % 1000 * 1000,
+                              ap_handle_timer, hapd, sta);
+}
+
+
 int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
                                   struct sta_info *sta, const char *url,
                                   int disassoc_timer)
@@ -477,30 +505,78 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
                return -1;
        }
 
-       /* send disassociation frame after time-out */
        if (disassoc_timer) {
-               int timeout, beacon_int;
+               /* send disassociation frame after time-out */
+               set_disassoc_timer(hapd, sta, disassoc_timer);
+       }
 
-               /*
-                * Prevent STA from reconnecting using cached PMKSA to force
-                * full authentication with the authentication server (which may
-                * decide to reject the connection),
-                */
-               wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
-
-               beacon_int = hapd->iconf->beacon_int;
-               if (beacon_int < 1)
-                       beacon_int = 100; /* best guess */
-               /* Calculate timeout in ms based on beacon_int in TU */
-               timeout = disassoc_timer * beacon_int * 128 / 125;
-               wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
-                          " set to %d ms", MAC2STR(sta->addr), timeout);
-
-               sta->timeout_next = STA_DISASSOC_FROM_CLI;
-               eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-               eloop_register_timeout(timeout / 1000,
-                                      timeout % 1000 * 1000,
-                                      ap_handle_timer, hapd, sta);
+       return 0;
+}
+
+
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+                       u8 req_mode, int disassoc_timer, u8 valid_int,
+                       const u8 *bss_term_dur, const char *url,
+                       const u8 *nei_rep, size_t nei_rep_len)
+{
+       u8 *buf, *pos;
+       struct ieee80211_mgmt *mgmt;
+       size_t url_len;
+
+       wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+                  MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
+                  MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+       buf = os_zalloc(1000 + nei_rep_len);
+       if (buf == NULL)
+               return -1;
+       mgmt = (struct ieee80211_mgmt *) buf;
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+       mgmt->u.action.category = WLAN_ACTION_WNM;
+       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+       mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+       mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
+       mgmt->u.action.u.bss_tm_req.disassoc_timer =
+               host_to_le16(disassoc_timer);
+       mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+       pos = mgmt->u.action.u.bss_tm_req.variable;
+
+       if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+           bss_term_dur) {
+               os_memcpy(pos, bss_term_dur, 12);
+               pos += 12;
+       }
+
+       if (url) {
+               /* Session Information URL */
+               url_len = os_strlen(url);
+               if (url_len > 255)
+                       return -1;
+               *pos++ = url_len;
+               os_memcpy(pos, url, url_len);
+               pos += url_len;
+       }
+
+       if (nei_rep) {
+               os_memcpy(pos, nei_rep, nei_rep_len);
+               pos += nei_rep_len;
+       }
+
+       if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to send BSS Transition Management Request frame");
+               os_free(buf);
+               return -1;
+       }
+       os_free(buf);
+
+       if (disassoc_timer) {
+               /* send disassociation frame after time-out */
+               set_disassoc_timer(hapd, sta, disassoc_timer);
        }
 
        return 0;
index eeaf5eca3a45c115efec9658c288f83b5f704f90..7789307209c9962ce8642bf1e62f991e73b1c5e1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11v WNM related functions and structures
- * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,5 +18,9 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
 int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
                                   struct sta_info *sta, const char *url,
                                   int disassoc_timer);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+                       u8 req_mode, int disassoc_timer, u8 valid_int,
+                       const u8 *bss_term_dur, const char *url,
+                       const u8 *nei_rep, size_t nei_rep_len);
 
 #endif /* WNM_AP_H */