| /* |
| * 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); |
| } |