blob: 4c9279eb97cee5143c558c2789c0638aef534a75 [file] [log] [blame]
/*
* hostapd / RM (Radio Management)
* Copyright (c) 2015, Google, Inc
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
/* Common clients only look at the first 6 APs in the list anyway. */
#define NR_MAX_APS 6
/*
* bssinfo is defined in 802.11-2012 Figure 8-216,
* Table 8-114, and Figure 8-217.
*/
static u32 cap_to_bssinfo(u32 cap)
{
u32 ap_reach = 2 << 0; // Reachability unknown
u32 security = 1 << 2;
u32 key_scope = 0 << 3;
u32 capabilities = 0;
if (cap & (1 << 8)) capabilities |= (1 << 4); // Spectrum Management
capabilities |= (0 << 5); // QoS
if (cap & (1 << 11)) capabilities |= (1 << 6); // APSD
if (cap & (1 << 12)) capabilities |= (1 << 7); // Radio Measurement
if (cap & (1 << 14)) capabilities |= (1 << 8); // Delayed Block Ack
if (cap & (1 << 15)) capabilities |= (1 << 9); // Immed Block Ack
return ap_reach | security | key_scope | capabilities;
}
static void rm_send_neighbor_report(struct hostapd_data *hapd, const u8 *addr,
u8 dialog_token)
{
#define ELEMSIZ sizeof(struct rrm_neighbor_report_element)
u8 buf[sizeof(struct ieee80211_mgmt) + NR_MAX_APS * ELEMSIZ];
struct ieee80211_mgmt *m = (struct ieee80211_mgmt *)buf;
u8 *v = m->u.action.u.rm_action.variable;
struct rrm_neighbor_report_element *elements =
(struct rrm_neighbor_report_element *) v;
int num_aps = 0;
size_t len;
/* TODO: if the neighbor request included a specific SSID,
* we need to compare with hapd->conf->ssid. If the SSID
* does not match, we should send back an action frame with
* some kind of refused or mismatch error code. */
os_memset(&buf, 0, sizeof(buf));
m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(m->da, addr, ETH_ALEN);
os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
m->u.action.category = WLAN_ACTION_RADIO_MEASUREMENT;
m->u.action.u.rm_action.action = WLAN_RRM_NEIGHBOR_REPORT_RESPONSE;
m->u.action.u.rm_action.dialog_token = dialog_token;
if (hapd->conf->neighbor_ap_list_file) {
FILE *f = fopen(hapd->conf->neighbor_ap_list_file, "r");
char line[128];
while (f && fgets(line, sizeof(line), f)) {
char *start = line;
char *tok, *save = NULL;
const char *bssid = NULL;
u32 cap = 0, bssinfo = 0;
u8 channel = 0, phy_type = 0;
struct rrm_neighbor_report_element *elem;
if (num_aps >= NR_MAX_APS) {
break;
}
while ((tok = strtok_r(start, "|", &save)) != NULL) {
start = NULL;
if (strncmp(tok, "bssid:", 6) == 0) {
bssid = tok + 6;
}
if (strncmp(tok, "cap:", 4) == 0) {
cap = strtol(tok + 4, NULL, 0);
}
if (strncmp(tok, "freq:", 5) == 0) {
u32 freq = strtol(tok + 5, NULL, 0);
ieee80211_freq_to_chan(freq, &channel);
}
if (strncmp(tok, "phy:", 4) == 0) {
phy_type = (u8)strtol(tok + 4, NULL, 0);
}
}
if (bssid == NULL || *bssid == '\0') {
continue;
}
elem = &elements[num_aps];
if (hwaddr_aton(bssid, elem->bssid) != 0) {
continue;
}
elem->eid = WLAN_EID_NEIGHBOR_REPORT;
elem->len = sizeof(*elem) - 2;
bssinfo = cap_to_bssinfo(cap);
WPA_PUT_LE32((u8 *)&elem->bssinfo, bssinfo);
elem->op_class = 0; // TODO: regulatory class.
elem->channel = channel;
elem->phy_type = phy_type;
num_aps++;
}
if (f) {
fclose(f);
} else {
hostapd_logger(hapd, m->da, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE, "unable to open %s",
hapd->conf->neighbor_ap_list_file);
}
}
len = (u8 *)&elements[num_aps] - buf;
hostapd_logger(hapd, m->da, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE, "sending Neighbor List with %d APs",
num_aps);
if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
wpa_printf(MSG_WARNING, "rm_send_neighbor_report: send failed");
}
/*
* Process a WLAN_ACTION_RADIO_MEASUREMENT frame.
*/
void hostapd_rm_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
int action_code;
struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
u8 dialog_token;
/* Check that the request comes from a valid station. */
if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
"rm action received is not from an associated "
"station");
/* TODO: respond with action frame refused status code */
return;
}
action_code = mgmt->u.action.u.rm_action.action;
dialog_token = mgmt->u.action.u.wmm_action.dialog_token;
switch (action_code) {
case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
rm_send_neighbor_report(hapd, mgmt->sa, dialog_token);
return;
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"hostapd_rm_action - unknown action code %d",
action_code);
}