From 57acf1e8da1e3e7c0a84fbbde43c5c62873cb372 Mon Sep 17 00:00:00 2001
From: David Weidenkopf <dweidenkopf@cococorp.com>
Date: Thu, 7 Jul 2016 17:33:19 -0700
Subject: [PATCH] WNM: BSS transition (802.11v) support. New CLI command to use
the BSS transition function.
Signed-off-by: Henry M. Bennett <hbennett@cococorp.com>
Signed-off-by: Alexis Green <agreen@cococorp.com>
Signed-off-by: David Weidenkopf <dweidenkopf@cococorp.com>
---
hostapd/ctrl_iface.c | 63 ++++++++++++++++++++
hostapd/defconfig | 2 +-
hostapd/hostapd_cli.c | 19 ++++++
src/ap/ieee802_11.c | 28 ++++++++-
src/ap/sta_info.h | 2 +-
src/ap/wnm_ap.c | 138 ++++++++++++++++++++++++++++++++++++++++---
src/ap/wnm_ap.h | 5 ++
src/common/ieee802_11_defs.h | 50 ++++++++++++++++
8 files changed, 295 insertions(+), 12 deletions(-)
@@ -1070,6 +1070,66 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
return ret;
}
+static int hostapd_ctrl_iface_bss_transition(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ u8 ap_addr[ETH_ALEN];
+ const char *timerstr, *apstr, *channelstr;
+ int disassoc_timer;
+ u8 dest_ap_channel;
+ struct sta_info *sta;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, " not found for BSS transition message");
+
+ //wpa_printf(MSG_DEBUG, "Station " MACSTR
+ // " not found for BSS transition message",
+ // MAC2STR(addr));
+ return -1;
+ }
+
+ timerstr = cmd + 17;
+ if (*timerstr != ' ') {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, "time not found");
+ return -1;
+ }
+ timerstr++; // skip the space
+ disassoc_timer = atoi(timerstr);
+ if (disassoc_timer < 0 || disassoc_timer > 65535) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, "time out of range");
+ return -1;
+ }
+
+ apstr = os_strchr(timerstr, ' ');
+ if (apstr == NULL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, "dest AP not found");
+ return -1;
+ }
+ apstr++; // skip the space
+ if (hwaddr_aton(apstr, ap_addr)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, "dest AP couldn't be decoded");
+ return -1;
+ }
+
+ channelstr = os_strchr(apstr, ' ');
+ channelstr++; // skip the space
+ sscanf(channelstr, "%hhu", &dest_ap_channel); // C99 way of reading in an unsigned char
+ //dest_ap_channel = atoi(channelstr);
+
+ return wnm_send_bss_tm_req2(hapd, sta, disassoc_timer, ap_addr, dest_ap_channel);
+}
+
+
#endif /* CONFIG_WNM */
@@ -2415,6 +2475,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_TRANSITION ", 15) == 0) {
+ if (hostapd_ctrl_iface_bss_transition(hapd, buf + 15))
+ reply_len = -1;
#endif /* CONFIG_WNM */
} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
@@ -152,7 +152,7 @@ CONFIG_IPV6=y
# Wireless Network Management (IEEE Std 802.11v-2011)
# Note: This is experimental and not complete implementation.
-#CONFIG_WNM=y
+CONFIG_WNM=y
# IEEE 802.11ac (Very High Throughput) support
#CONFIG_IEEE80211AC=y
@@ -686,6 +686,24 @@ static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
return wpa_ctrl_command(ctrl, buf);
}
+static int hostapd_cli_cmd_bss_transition(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[300];
+ int res;
+
+ if (argc < 4) {
+ printf("Invalid 'bss_transition' command - four arguments (STA "
+ "addr, disassoc beacon timer, AP addr, AP channel) are needed\n");
+ return -1;
+ }
+
+ res = os_snprintf(buf, sizeof(buf), "BSS_TRANSITION %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ if (res < 0 || res >= (int) sizeof(buf))
+ return -1;
+ return wpa_ctrl_command(ctrl, buf);
+}
static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
@@ -1251,6 +1269,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "wps_config", hostapd_cli_cmd_wps_config },
{ "wps_get_status", hostapd_cli_cmd_wps_get_status },
#endif /* CONFIG_WPS */
+ { "bss_transition", hostapd_cli_cmd_bss_transition },
{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
@@ -1494,6 +1494,19 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
sta->qos_map_enabled = 1;
}
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_WNM
+ // BIT#19 BSS Transition
+ // The STA sets the BSS Transition field to 1 when
+ // dot11MgmtOptionBSSTransitionActivated is true, and sets it to 0 otherwise. See
+ // 10.23.6.
+ if (ext_capab_ie_len >= 3) {
+ if (ext_capab_ie[2] & 0x8) {
+ sta->dot11MgmtOptionBSSTransitionActivated = 1;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: dot11MgmtOptionBSSTransitionActivated is true\n");
+ }
+ }
+#endif
if (ext_capab_ie_len > 0)
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
@@ -3016,8 +3029,19 @@ int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen)
{
- /* TODO */
- return 0;
+ int len = 0, ret = 0;
+
+ if(sta->dot11MgmtOptionBSSTransitionActivated)
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "dot11MgmtOptionBSSTransitionActivated=%u\n",
+ sta->dot11MgmtOptionBSSTransitionActivated);
+
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
}
@@ -97,7 +97,7 @@ struct sta_info {
u16 igtk_key_id;
u8 sae_auth_retry;
#endif /* CONFIG_MESH */
-
+ unsigned int dot11MgmtOptionBSSTransitionActivated:1;
unsigned int nonerp_set:1;
unsigned int no_short_slot_time_set:1;
unsigned int no_short_preamble_set:1;
@@ -1,6 +1,7 @@
/*
* hostapd - WNM
* Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2015, CoCo Communications, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -17,9 +18,15 @@
#include "ap/ap_config.h"
#include "ap/ap_drv_ops.h"
#include "ap/wpa_auth.h"
+#include "ap/wpa_auth_i.h"
+#include "radius/radius.h"
+#include "ap/accounting.h"
+#include "ap/ap_mlme.h"
+#include "ap/ieee802_1x.h"
#include "mbo_ap.h"
#include "wnm_ap.h"
+#define WNM_DEAUTH_DELAY_USEC 10
#define MAX_TFS_IE_LEN 1024
@@ -319,7 +326,8 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
dialog_token = *pos++;
reason = *pos++;
- wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: BSS Transition Management Query from "
MACSTR " dialog_token=%u reason=%u",
MAC2STR(addr), dialog_token, reason);
@@ -336,6 +344,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
{
u8 dialog_token, status_code, bss_termination_delay;
const u8 *pos, *end;
+ struct sta_info *sta;
if (len < 3) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
@@ -349,7 +358,8 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
status_code = *pos++;
bss_termination_delay = *pos++;
- wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: BSS Transition Management Response from "
MACSTR " dialog_token=%u status_code=%u "
"bss_termination_delay=%u", MAC2STR(addr), dialog_token,
status_code, bss_termination_delay);
@@ -359,14 +369,38 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
return;
}
- wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
- MAC2STR(pos));
- wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
- " status_code=%u bss_termination_delay=%u target_bssid="
- MACSTR,
- MAC2STR(addr), status_code, bss_termination_delay,
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: BSS Transition Accepted - target BSSID: " MACSTR,
MAC2STR(pos));
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, "sta "MACSTR" not found for BSS transition response",
+ MAC2STR(addr));
+ return;
+ }
+
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~WLAN_STA_ASSOC;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: disassociated due to accepted BSS transition request");
+
+ sta->timeout_next = STA_DEAUTH;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(0, WNM_DEAUTH_DELAY_USEC, ap_handle_timer, hapd, sta);
+ mlme_disassociate_indication(hapd, sta, WLAN_REASON_DISASSOC_STA_HAS_LEFT);
+
pos += ETH_ALEN;
+ } else if (status_code == WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: BSS Transition Rejected - No Suitable Candidates");
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
" status_code=%u bss_termination_delay=%u",
@@ -594,6 +628,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
if (url) {
/* Session Information URL */
url_len = os_strlen(url);
+
if (url_len > 255) {
os_free(buf);
return -1;
@@ -629,3 +664,90 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
return 0;
}
+
+/*
+802.11-2012 10.23.6.3 BSS transition management request
+The AP may send an unsolicited BSS Transition
+Management Request frame to a non-AP STA at any time if the non-AP STA indicates that it supports the
+BSS Transition Management capability in the Extended Capabilities element.
+A non-AP STA that supports BSS transition management shall
+respond to an individually addressed BSS Transition Management Request frame with a BSS Transition
+Management Response frame.
+This version will create the neighbor report using the passed in bssid and channel
+*/
+int wnm_send_bss_tm_req2(struct hostapd_data *hapd,
+ struct sta_info *sta, int disassoc_timer,
+ const u8 *bssid, u8 ap_channel)
+{
+ static const u8 valid_int = 255;
+ u8 report_ie_len;
+ u8 op_class, channel;
+ struct wnm_neighbor_report_element* report_ie;
+ u8 req_mode = 0;
+ u8* pos = NULL;
+
+ /*
+ 802.11-2012
+ The BSS Transition Candidate List Entries field of a BSS Transition Management Response frame contains
+ zero or more Neighbor Report elements describing the non-AP STA’s preferences for target BSS
+ candidates. The Preference field value of a Neighbor Report element used in a BSS Transition Management
+ Response frame shall be between 1 and 255. The value of 0 is reserved. The values between 1 and 255
+ provide the indication of order, with 255 indicating the most preferred BSS within the given candidate list,
+ decreasing numbers representing decreasing preference relative only to entries with lower values of the
+ Preference field, and equal numbers representing equal preference. The non-AP STA should not list any
+ BSS that is not considered as a target BSS candidate.
+
+ Contains the description of candidate BSS transition APs and their capabilities as described in 8.4.2.39.
+ */
+ /* add 3 octets for candidate preference */
+ report_ie = (struct wnm_neighbor_report_element*)
+ os_zalloc(sizeof(struct wnm_neighbor_report_element) + 3);
+ if (report_ie == NULL) return -1;
+
+ req_mode = WNM_BSS_TM_REQ_ABRIDGED | WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+ if(disassoc_timer > 0) {
+ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ }
+
+ report_ie_len = sizeof(struct wnm_neighbor_report_element);
+ report_ie->eid = WLAN_EID_NEIGHBOR_REPORT;
+ report_ie->len = report_ie_len - 2 + 3;
+ os_memcpy(report_ie->bssid, bssid, ETH_ALEN);
+
+ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+ hapd->iconf->secondary_channel,
+ hapd->iconf->vht_oper_chwidth,
+ &op_class, &channel) != NUM_HOSTAPD_MODES)
+ report_ie->operating_class = op_class;
+
+ report_ie->channel_number = ap_channel;
+
+ /*
+ The AP Reachability field indicates whether the AP identified by this BSSID is reachable by the STA that
+ requested the neighbor report. For example, the AP identified by this BSSID is reachable for the exchange of
+ preauthentication frames as described in 11.5.9.2
+
+ The Security bit, if 1, indicates that the AP identified by this BSSID supports the same security provisioning
+ as used by the STA in its current association.
+ */
+ report_ie->bssid_info[0] = WNM_REACHABILITY_REACHABLE | WNM_SECURITY;
+
+ pos = (u8*) report_ie;
+ pos += report_ie_len;
+
+ /* BSS Transition Candidate Preference subelement field */
+ *pos++ = 3; /* Subelement ID */
+ *pos++ = 1; /* length */
+ *pos++ = 255; /* Preference */
+ report_ie_len += 3;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "WNM: Send BSS Transition Management Request "
+ "client "MACSTR" (disassoc_timer=%d) to AP "MACSTR,
+ MAC2STR(sta->addr), disassoc_timer, MAC2STR(bssid));
+
+ return wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+ valid_int, NULL, NULL, (u8*) report_ie,
+ report_ie_len, NULL, 0);
+}
+
@@ -1,6 +1,7 @@
/*
* IEEE 802.11v WNM related functions and structures
* Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2015, CoCo Communications, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -23,5 +24,9 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *bss_term_dur, const char *url,
const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len);
+int wnm_send_bss_tm_req2(struct hostapd_data *hapd,
+ struct sta_info *sta, int disassoc_timer,
+ const u8 *ap_addr, u8 ap_channel);
+
#endif /* WNM_AP_H */
@@ -1433,6 +1433,56 @@ enum wnm_action {
#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+/* IEEE Std 802.11-2012 - Figure 8-141 Measurement Report field */
+#define WNM_REACHABILITY_NOT 0x1
+#define WNM_REACHABILITY_UNKNOWN 0x2
+#define WNM_REACHABILITY_REACHABLE 0x3
+
+#define WNM_SECURITY 0x4
+
+#define WNM_MOBILITY_DOMAIN 0x4
+
+/* IEEE 802.11-2012 - 8.4.2.39 Neighbor Report element */
+struct wnm_neighbor_report_element {
+ u8 eid; /* WLAN_EID_NEIGHBOR_REPORT */
+ u8 len;
+ u8 bssid[6];
+ /*
+ u8 info_ap_reachability:2;
+ u8 info_security:1;
+ u8 info_key_scope:1;
+ u8 info_spectrum_management:1;
+ u8 info_QoS:1;
+ u8 info_APSD:1;
+ u8 info_rm:1;
+ u8 info_delayed_block_ack:1;
+ u8 info_immediate_block_ack:1;
+ u8 info_mobility_domain:1;
+ u8 info_high_throughput:1;
+ u8 info_reserved1:4;
+ */
+ u8 bssid_info[4];
+ u8 operating_class;
+ u8 channel_number;
+ u8 PHY_type;
+ u8 subelements[0]; /* Optional Subelements */
+} STRUCT_PACKED;
+
+/* IEEE 802.11-2012 Table 8-115 Optional subelement IDs for neighbor report */
+struct wnm_neighbor_report_candidate_preference_subelement {
+ u8 sub_eid; // 3
+ u8 len; // 1
+ u8 preference; // 0 is ignore, 255 is highest
+} STRUCT_PACKED;
+
+/* IEEE 802.11-2012 - 8.4.2.38 AP Channel Report element */
+struct wnm_ap_channel_report_element {
+ u8 eid; /* WLAN_EID_CHANNEL_REPORT */
+ u8 len;
+ u8 operating_class;
+ u8 channel_list[0];
+} STRUCT_PACKED;
+
/* IEEE Std 802.11-2012 - Table 8-253 */
enum bss_trans_mgmt_status_code {
--
1.9.1