Add BSS_TM_REQ command to send BSS Transition Management Request
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>
This commit is contained in:
parent
64d04af5cc
commit
a30dff07fb
4 changed files with 323 additions and 25 deletions
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
|
|
124
src/ap/wnm_ap.c
124
src/ap/wnm_ap.c
|
@ -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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
/* send disassociation frame after time-out */
|
||||
set_disassoc_timer(hapd, sta, disassoc_timer);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue