WNM: Add neighbor report processing for BSS Transition Management

Process the neighbor report received in BSS Management Request frames.

Signed-hostap: Vinayak Kamath <vkamat@codeaurora.org>
This commit is contained in:
Vinayak Kamath 2013-05-16 17:48:59 +03:00 committed by Jouni Malinen
parent f3e907a745
commit e27d20bb68
5 changed files with 402 additions and 16 deletions

View file

@ -1049,6 +1049,15 @@ enum wnm_action {
#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
#define WNM_NEIGHBOR_TSF 1
#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3
#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4
#define WNM_NEIGHBOR_BEARING 5
#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66
#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)

View file

@ -14,8 +14,12 @@
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "scan.h"
#include "ctrl_iface.h"
#include "bss.h"
#include "wnm_sta.h"
#define MAX_TFS_IE_LEN 1024
#define WNM_MAX_NEIGHBOR_REPORT 10
/* get the TFS IE from driver */
@ -294,6 +298,199 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
}
void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
{
int i;
for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
}
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
}
static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
u8 id, u8 elen, const u8 *pos)
{
switch (id) {
case WNM_NEIGHBOR_TSF:
if (elen < 2 + 2) {
wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
break;
}
rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
if (rep->tsf_info == NULL)
break;
rep->tsf_info->present = 1;
os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
break;
case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
if (elen < 2) {
wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
"country string");
break;
}
rep->con_coun_str =
os_zalloc(sizeof(struct condensed_country_string));
if (rep->con_coun_str == NULL)
break;
rep->con_coun_str->present = 1;
os_memcpy(rep->con_coun_str->country_string, pos, 2);
break;
case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
if (elen < 1) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
"candidate");
break;
}
rep->bss_tran_can =
os_zalloc(sizeof(struct bss_transition_candidate));
if (rep->bss_tran_can == NULL)
break;
rep->bss_tran_can->present = 1;
rep->bss_tran_can->preference = pos[0];
break;
case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
if (elen < 12) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
"duration");
break;
}
rep->bss_term_dur =
os_zalloc(sizeof(struct bss_termination_duration));
if (rep->bss_term_dur == NULL)
break;
rep->bss_term_dur->present = 1;
os_memcpy(rep->bss_term_dur->duration, pos, 12);
break;
case WNM_NEIGHBOR_BEARING:
if (elen < 8) {
wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
"bearing");
break;
}
rep->bearing = os_zalloc(sizeof(struct bearing));
if (rep->bearing == NULL)
break;
rep->bearing->present = 1;
os_memcpy(rep->bearing->bearing, pos, 8);
break;
case WNM_NEIGHBOR_MEASUREMENT_PILOT:
if (elen < 2) {
wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
"pilot");
break;
}
rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
if (rep->meas_pilot == NULL)
break;
rep->meas_pilot->present = 1;
rep->meas_pilot->measurement_pilot = pos[0];
rep->meas_pilot->num_vendor_specific = pos[1];
os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
break;
case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
if (elen < 4) {
wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
"capabilities");
break;
}
rep->rrm_cap =
os_zalloc(sizeof(struct rrm_enabled_capabilities));
if (rep->rrm_cap == NULL)
break;
rep->rrm_cap->present = 1;
os_memcpy(rep->rrm_cap->capabilities, pos, 4);
break;
case WNM_NEIGHBOR_MULTIPLE_BSSID:
if (elen < 2) {
wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
break;
}
rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
if (rep->mul_bssid == NULL)
break;
rep->mul_bssid->present = 1;
rep->mul_bssid->max_bssid_indicator = pos[0];
rep->mul_bssid->num_vendor_specific = pos[1];
os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
break;
}
}
static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
const u8 *pos, u8 len,
struct neighbor_report *rep)
{
u8 left = len;
if (left < 13) {
wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
return;
}
os_memcpy(rep->bssid, pos, ETH_ALEN);
os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
rep->regulatory_class = *(pos + 10);
rep->channel_number = *(pos + 11);
rep->phy_type = *(pos + 12);
pos += 13;
left -= 13;
while (left >= 2) {
u8 id, elen;
id = *pos++;
elen = *pos++;
wnm_parse_neighbor_report_elem(rep, id, elen, pos);
left -= 2 + elen;
pos += elen;
}
}
static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res,
struct neighbor_report *neigh_rep,
u8 num_neigh_rep, u8 *bssid_to_connect)
{
u8 i, j;
if (scan_res == NULL || num_neigh_rep == 0)
return 0;
for (i = 0; i < num_neigh_rep; i++) {
for (j = 0; j < scan_res->num; j++) {
/* Check for a better RSSI AP */
if (os_memcmp(scan_res->res[j]->bssid,
neigh_rep[i].bssid, ETH_ALEN) == 0 &&
scan_res->res[j]->level >
wpa_s->current_bss->level) {
/* Got a BSSID with better RSSI value */
os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
ETH_ALEN);
return 1;
}
}
}
return 0;
}
static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s,
u8 dialog_token, u8 status,
u8 delay, const u8 *target_bssid)
@ -332,29 +529,90 @@ static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s,
}
void wnm_scan_response(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
u8 bssid[ETH_ALEN];
if (scan_res == NULL) {
wpa_printf(MSG_ERROR, "Scan result is NULL");
goto send_bss_resp_fail;
}
/* Compare the Neighbor Report and scan results */
if (compare_scan_neighbor_results(wpa_s, scan_res,
wpa_s->wnm_neighbor_report_elements,
wpa_s->wnm_num_neighbor_report,
bssid) == 1) {
/* Associate to the network */
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!bss) {
wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
"BSS table");
goto send_bss_resp_fail;
}
/* Send the BSS Management Response - Accept */
if (wpa_s->wnm_reply) {
wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token,
0, /* Accept */
0, NULL);
}
wpa_s->reassociate = 1;
wpa_supplicant_connect(wpa_s, bss, ssid);
wnm_deallocate_memory(wpa_s);
return;
}
/* Send reject response for all the failures */
send_bss_resp_fail:
wnm_deallocate_memory(wpa_s);
if (wpa_s->wnm_reply) {
wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token,
1 /* Reject - unspecified */,
0, NULL);
}
return;
}
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
const u8 *pos, const u8 *end,
int reply)
{
u8 dialog_token;
u8 mode;
u16 disassoc_timer;
if (pos + 5 > end)
return;
dialog_token = pos[0];
mode = pos[1];
disassoc_timer = WPA_GET_LE16(pos + 2);
wpa_s->wnm_dialog_token = pos[0];
wpa_s->wnm_mode = pos[1];
wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
wpa_s->wnm_validity_interval = pos[4];
wpa_s->wnm_reply = reply;
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
"dialog_token=%u request_mode=0x%x "
"disassoc_timer=%u validity_interval=%u",
dialog_token, mode, disassoc_timer, pos[4]);
wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
pos += 5;
if (mode & 0x08)
if (wpa_s->wnm_mode & 0x08) {
if (pos + 12 > end) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
return;
}
os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
pos += 12; /* BSS Termination Duration */
if (mode & 0x10) {
}
if (wpa_s->wnm_mode & 0x10) {
char url[256];
if (pos + 1 > end || pos + 1 + pos[0] > end) {
wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
@ -363,14 +621,15 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
}
os_memcpy(url, pos + 1, pos[0]);
url[pos[0]] = '\0';
pos += 1 + pos[0];
wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - "
"session_info_url=%s", url);
}
if (mode & 0x04) {
if (wpa_s->wnm_mode & 0x04) {
wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
"Disassociation Timer %u", disassoc_timer);
if (disassoc_timer && !wpa_s->scanning) {
"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
/* TODO: mark current BSS less preferred for
* selection */
wpa_printf(MSG_DEBUG, "Trying to find another BSS");
@ -378,9 +637,44 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
}
}
if (reply) {
/* TODO: add support for reporting Accept */
wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token,
if (wpa_s->wnm_mode & 0x01) {
wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
wpa_s->wnm_num_neighbor_report = 0;
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = os_zalloc(
WNM_MAX_NEIGHBOR_REPORT *
sizeof(struct neighbor_report));
if (wpa_s->wnm_neighbor_report_elements == NULL)
return;
while (pos + 2 <= end &&
wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
{
u8 tag = *pos++;
u8 len = *pos++;
wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
tag);
if (pos + len > end) {
wpa_printf(MSG_DEBUG, "WNM: Truncated request");
return;
}
wnm_parse_neighbor_report(
wpa_s, pos, len,
&wpa_s->wnm_neighbor_report_elements[
wpa_s->wnm_num_neighbor_report]);
pos += len;
wpa_s->wnm_num_neighbor_report++;
}
wpa_s->scan_res_handler = wnm_scan_response;
wpa_supplicant_req_scan(wpa_s, 0, 0);
} else if (reply) {
wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management "
"Request Mode is zero");
wnm_send_bss_transition_mgmt_resp(wpa_s,
wpa_s->wnm_dialog_token,
1 /* Reject - unspecified */,
0, NULL);
}
@ -418,6 +712,7 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
break;
default:
wpa_printf(MSG_ERROR, "WNM: Unknown request");
break;
}
}

View file

@ -12,10 +12,77 @@
struct rx_action;
struct wpa_supplicant;
struct tsf_info {
u8 present;
u8 tsf_offset[2];
u8 beacon_interval[2];
};
struct condensed_country_string {
u8 present;
u8 country_string[2];
};
struct bss_transition_candidate {
u8 present;
u8 preference;
};
struct bss_termination_duration {
u8 present;
u8 duration[12];
};
struct bearing {
u8 present;
u8 bearing[8];
};
struct measurement_pilot {
u8 present;
u8 measurement_pilot;
u8 num_vendor_specific;
u8 vendor_specific[255];
};
struct rrm_enabled_capabilities {
u8 present;
u8 capabilities[4];
};
struct multiple_bssid {
u8 present;
u8 max_bssid_indicator;
u8 num_vendor_specific;
u8 vendor_specific[255];
};
struct neighbor_report {
u8 bssid[ETH_ALEN];
u8 bssid_information[4];
u8 regulatory_class;
u8 channel_number;
u8 phy_type;
struct tsf_info *tsf_info;
struct condensed_country_string *con_coun_str;
struct bss_transition_candidate *bss_tran_can;
struct bss_termination_duration *bss_term_dur;
struct bearing *bearing;
struct measurement_pilot *meas_pilot;
struct rrm_enabled_capabilities *rrm_cap;
struct multiple_bssid *mul_bssid;
};
int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
u8 action, u16 intval, struct wpabuf *tfs_req);
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
struct rx_action *action);
void wnm_scan_response(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
#endif /* WNM_STA_H */

View file

@ -49,6 +49,7 @@
#include "scan.h"
#include "offchannel.h"
#include "hs20_supplicant.h"
#include "wnm_sta.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
@ -470,6 +471,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpa_s->disallow_aps_ssid = NULL;
wnm_bss_keep_alive_deinit(wpa_s);
#ifdef CONFIG_WNM
wnm_deallocate_memory(wpa_s);
#endif /* CONFIG_WNM */
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;

View file

@ -690,6 +690,17 @@ struct wpa_supplicant {
u8 last_gas_dialog_token;
unsigned int no_keep_alive:1;
#ifdef CONFIG_WNM
u8 wnm_dialog_token;
u8 wnm_reply;
u8 wnm_num_neighbor_report;
u8 wnm_mode;
u16 wnm_dissoc_timer;
u8 wnm_validity_interval;
u8 wnm_bss_termination_duration[12];
struct neighbor_report *wnm_neighbor_report_elements;
#endif /* CONFIG_WNM */
};