diff mbox

[RFC,2/3] BSS transition with candidate list

Message ID CACdfkS+6u2=pBuvjrApJhKCOOGeWmob3iNHDz8JynLqpKDw3Vg@mail.gmail.com
State RFC
Headers show

Commit Message

David Weidenkopf July 20, 2016, 12:34 a.m. UTC
This patch extends BSS transition (802.11v) support. It includes a new
simplified BSS Transition function in the CLI command. This function allows
the user to specify the client station and a target BSS transition
candidate which is included as a neighbor report. The BSS Transition
request packet is then automatically filled in with the appropriate data
for bssid of the target AP, the channel of the target AP, and the
preference value of the AP.
diff mbox

Patch

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(-)

diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 62bef18..c26ca1b 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -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,
diff --git a/hostapd/defconfig b/hostapd/defconfig
index f7b60e0..58eaa31 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -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
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index ff133f6..222b120 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -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 },
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 11f12f9..ad035e4 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -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;
 }
 
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 5d4d0c8..9b1ad21 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -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;
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 41d50ce..3401714 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -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);
+}
+
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index a44eadb..bba9dd7 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -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 */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 02d2ad7..b4343c5 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.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