From 57acf1e8da1e3e7c0a84fbbde43c5c62873cb372 Mon Sep 17 00:00:00 2001 From: David Weidenkopf 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 Signed-off-by: Alexis Green Signed-off-by: David Weidenkopf --- 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