2016-04-06 18:42:07 +02:00
|
|
|
/*
|
|
|
|
* hostapd / Neighboring APs DB
|
|
|
|
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
|
|
|
|
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
|
|
|
|
*
|
|
|
|
* 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"
|
2021-07-28 01:42:16 +02:00
|
|
|
#include "utils/crc32.h"
|
2016-04-06 18:42:07 +02:00
|
|
|
#include "hostapd.h"
|
2018-08-20 14:20:44 +02:00
|
|
|
#include "ieee802_11.h"
|
2016-04-06 18:42:07 +02:00
|
|
|
#include "neighbor_db.h"
|
|
|
|
|
|
|
|
|
2016-04-06 18:42:12 +02:00
|
|
|
struct hostapd_neighbor_entry *
|
2016-04-06 18:42:07 +02:00
|
|
|
hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
|
|
|
|
const struct wpa_ssid_value *ssid)
|
|
|
|
{
|
|
|
|
struct hostapd_neighbor_entry *nr;
|
|
|
|
|
|
|
|
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
|
|
|
|
list) {
|
|
|
|
if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
|
2016-04-06 18:42:12 +02:00
|
|
|
(!ssid ||
|
|
|
|
(ssid->ssid_len == nr->ssid.ssid_len &&
|
|
|
|
os_memcmp(ssid->ssid, nr->ssid.ssid,
|
|
|
|
ssid->ssid_len) == 0)))
|
2016-04-06 18:42:07 +02:00
|
|
|
return nr;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-19 18:09:50 +01:00
|
|
|
int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
struct hostapd_neighbor_entry *nr;
|
|
|
|
char *pos, *end;
|
|
|
|
|
|
|
|
pos = buf;
|
|
|
|
end = buf + buflen;
|
|
|
|
|
|
|
|
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
|
|
|
|
list) {
|
|
|
|
int ret;
|
|
|
|
char nrie[2 * 255 + 1];
|
|
|
|
char lci[2 * 255 + 1];
|
|
|
|
char civic[2 * 255 + 1];
|
|
|
|
char ssid[SSID_MAX_LEN * 2 + 1];
|
|
|
|
|
|
|
|
ssid[0] = '\0';
|
|
|
|
wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
|
|
|
|
nr->ssid.ssid_len);
|
|
|
|
|
|
|
|
nrie[0] = '\0';
|
|
|
|
if (nr->nr)
|
|
|
|
wpa_snprintf_hex(nrie, sizeof(nrie),
|
|
|
|
wpabuf_head(nr->nr),
|
|
|
|
wpabuf_len(nr->nr));
|
|
|
|
|
|
|
|
lci[0] = '\0';
|
|
|
|
if (nr->lci)
|
|
|
|
wpa_snprintf_hex(lci, sizeof(lci),
|
|
|
|
wpabuf_head(nr->lci),
|
|
|
|
wpabuf_len(nr->lci));
|
|
|
|
|
|
|
|
civic[0] = '\0';
|
|
|
|
if (nr->civic)
|
|
|
|
wpa_snprintf_hex(civic, sizeof(civic),
|
|
|
|
wpabuf_head(nr->civic),
|
|
|
|
wpabuf_len(nr->civic));
|
|
|
|
|
|
|
|
ret = os_snprintf(pos, end - pos, MACSTR
|
|
|
|
" ssid=%s%s%s%s%s%s%s%s\n",
|
|
|
|
MAC2STR(nr->bssid), ssid,
|
|
|
|
nr->nr ? " nr=" : "", nrie,
|
|
|
|
nr->lci ? " lci=" : "", lci,
|
|
|
|
nr->civic ? " civic=" : "", civic,
|
|
|
|
nr->stationary ? " stat" : "");
|
|
|
|
if (os_snprintf_error(end - pos, ret))
|
|
|
|
break;
|
|
|
|
pos += ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos - buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-06 18:42:07 +02:00
|
|
|
static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
|
|
|
|
{
|
|
|
|
wpabuf_free(nr->nr);
|
|
|
|
nr->nr = NULL;
|
|
|
|
wpabuf_free(nr->lci);
|
|
|
|
nr->lci = NULL;
|
|
|
|
wpabuf_free(nr->civic);
|
|
|
|
nr->civic = NULL;
|
|
|
|
os_memset(nr->bssid, 0, sizeof(nr->bssid));
|
|
|
|
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
|
2016-10-27 14:18:25 +02:00
|
|
|
nr->stationary = 0;
|
2016-04-06 18:42:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct hostapd_neighbor_entry *
|
|
|
|
hostapd_neighbor_add(struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
struct hostapd_neighbor_entry *nr;
|
|
|
|
|
|
|
|
nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
|
|
|
|
if (!nr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dl_list_add(&hapd->nr_db, &nr->list);
|
|
|
|
|
|
|
|
return nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
|
|
|
|
const struct wpa_ssid_value *ssid,
|
|
|
|
const struct wpabuf *nr, const struct wpabuf *lci,
|
2021-07-28 01:42:17 +02:00
|
|
|
const struct wpabuf *civic, int stationary,
|
|
|
|
u8 bss_parameters)
|
2016-04-06 18:42:07 +02:00
|
|
|
{
|
|
|
|
struct hostapd_neighbor_entry *entry;
|
|
|
|
|
|
|
|
entry = hostapd_neighbor_get(hapd, bssid, ssid);
|
|
|
|
if (!entry)
|
|
|
|
entry = hostapd_neighbor_add(hapd);
|
|
|
|
if (!entry)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
hostapd_neighbor_clear_entry(entry);
|
|
|
|
|
|
|
|
os_memcpy(entry->bssid, bssid, ETH_ALEN);
|
|
|
|
os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
|
2022-09-05 22:18:23 +02:00
|
|
|
entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
|
2016-04-06 18:42:07 +02:00
|
|
|
|
|
|
|
entry->nr = wpabuf_dup(nr);
|
|
|
|
if (!entry->nr)
|
|
|
|
goto fail;
|
|
|
|
|
2016-10-27 14:18:23 +02:00
|
|
|
if (lci && wpabuf_len(lci)) {
|
2016-04-06 18:42:07 +02:00
|
|
|
entry->lci = wpabuf_dup(lci);
|
|
|
|
if (!entry->lci || os_get_time(&entry->lci_date))
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-10-27 14:18:23 +02:00
|
|
|
if (civic && wpabuf_len(civic)) {
|
2016-04-06 18:42:07 +02:00
|
|
|
entry->civic = wpabuf_dup(civic);
|
|
|
|
if (!entry->civic)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-10-27 14:18:25 +02:00
|
|
|
entry->stationary = stationary;
|
2021-07-28 01:42:17 +02:00
|
|
|
entry->bss_parameters = bss_parameters;
|
2016-10-27 14:18:25 +02:00
|
|
|
|
2016-04-06 18:42:07 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
hostapd_neighbor_remove(hapd, bssid, ssid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
|
|
|
|
const struct wpa_ssid_value *ssid)
|
|
|
|
{
|
|
|
|
struct hostapd_neighbor_entry *nr;
|
|
|
|
|
|
|
|
nr = hostapd_neighbor_get(hapd, bssid, ssid);
|
|
|
|
if (!nr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
hostapd_neighbor_clear_entry(nr);
|
|
|
|
dl_list_del(&nr->list);
|
|
|
|
os_free(nr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-02 15:40:34 +01:00
|
|
|
void hostapd_free_neighbor_db(struct hostapd_data *hapd)
|
2016-04-06 18:42:07 +02:00
|
|
|
{
|
|
|
|
struct hostapd_neighbor_entry *nr, *prev;
|
|
|
|
|
|
|
|
dl_list_for_each_safe(nr, prev, &hapd->nr_db,
|
|
|
|
struct hostapd_neighbor_entry, list) {
|
|
|
|
hostapd_neighbor_clear_entry(nr);
|
|
|
|
dl_list_del(&nr->list);
|
|
|
|
os_free(nr);
|
|
|
|
}
|
|
|
|
}
|
2018-08-20 14:20:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
#ifdef NEED_AP_MLME
|
|
|
|
static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
|
2019-05-20 09:55:09 +02:00
|
|
|
int ht, int vht, int he)
|
2018-08-20 14:20:44 +02:00
|
|
|
{
|
2022-05-13 18:17:40 +02:00
|
|
|
enum oper_chan_width oper_chwidth;
|
|
|
|
|
|
|
|
oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
|
2019-05-20 09:55:07 +02:00
|
|
|
|
2019-05-20 09:55:09 +02:00
|
|
|
if (!ht && !vht && !he)
|
2018-08-20 14:20:44 +02:00
|
|
|
return NR_CHAN_WIDTH_20;
|
|
|
|
if (!hapd->iconf->secondary_channel)
|
|
|
|
return NR_CHAN_WIDTH_20;
|
2022-05-13 18:17:40 +02:00
|
|
|
if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
|
2018-08-20 14:20:44 +02:00
|
|
|
return NR_CHAN_WIDTH_40;
|
2022-05-13 18:17:40 +02:00
|
|
|
if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
|
2018-08-20 14:20:44 +02:00
|
|
|
return NR_CHAN_WIDTH_80;
|
2022-05-13 18:17:40 +02:00
|
|
|
if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
|
2018-08-20 14:20:44 +02:00
|
|
|
return NR_CHAN_WIDTH_160;
|
2022-05-13 18:17:40 +02:00
|
|
|
if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
|
2018-08-20 14:20:44 +02:00
|
|
|
return NR_CHAN_WIDTH_80P80;
|
|
|
|
return NR_CHAN_WIDTH_20;
|
|
|
|
}
|
|
|
|
#endif /* NEED_AP_MLME */
|
|
|
|
|
|
|
|
|
|
|
|
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
#ifdef NEED_AP_MLME
|
|
|
|
u16 capab = hostapd_own_capab_info(hapd);
|
|
|
|
int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
|
|
|
|
int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
|
2020-06-30 16:23:46 +02:00
|
|
|
int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
|
2022-04-19 20:04:15 +02:00
|
|
|
bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
|
2018-08-20 14:20:44 +02:00
|
|
|
struct wpa_ssid_value ssid;
|
|
|
|
u8 channel, op_class;
|
|
|
|
u8 center_freq1_idx = 0, center_freq2_idx = 0;
|
|
|
|
enum nr_chan_width width;
|
|
|
|
u32 bssid_info;
|
|
|
|
struct wpabuf *nr;
|
|
|
|
|
|
|
|
if (!(hapd->conf->radio_measurements[0] &
|
|
|
|
WLAN_RRM_CAPS_NEIGHBOR_REPORT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
bssid_info = 3; /* AP is reachable */
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
|
|
|
|
|
|
|
|
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
|
|
|
|
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
|
|
|
|
|
|
|
|
if (hapd->conf->wmm_enabled) {
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_QOS;
|
|
|
|
|
|
|
|
if (hapd->conf->wmm_uapsd &&
|
|
|
|
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_APSD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ht) {
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_HT |
|
|
|
|
NEI_REP_BSSID_INFO_DELAYED_BA;
|
|
|
|
|
|
|
|
/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
|
|
|
|
if (vht)
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_VHT;
|
|
|
|
}
|
|
|
|
|
2022-04-19 20:04:15 +02:00
|
|
|
if (he)
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_HE;
|
|
|
|
if (eht)
|
|
|
|
bssid_info |= NEI_REP_BSSID_INFO_EHT;
|
2018-08-20 14:20:44 +02:00
|
|
|
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
|
|
|
|
|
|
|
|
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
|
|
|
|
hapd->iconf->secondary_channel,
|
2019-05-20 09:55:07 +02:00
|
|
|
hostapd_get_oper_chwidth(hapd->iconf),
|
2018-08-20 14:20:44 +02:00
|
|
|
&op_class, &channel) ==
|
|
|
|
NUM_HOSTAPD_MODES)
|
|
|
|
return;
|
2019-05-20 09:55:09 +02:00
|
|
|
width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
|
2018-08-20 14:20:44 +02:00
|
|
|
if (vht) {
|
2019-05-20 09:55:07 +02:00
|
|
|
center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
|
|
|
|
hapd->iconf);
|
2018-08-20 14:20:44 +02:00
|
|
|
if (width == NR_CHAN_WIDTH_80P80)
|
|
|
|
center_freq2_idx =
|
2019-05-20 09:55:07 +02:00
|
|
|
hostapd_get_oper_centr_freq_seg1_idx(
|
|
|
|
hapd->iconf);
|
2018-08-20 14:20:44 +02:00
|
|
|
} else if (ht) {
|
|
|
|
ieee80211_freq_to_chan(hapd->iface->freq +
|
|
|
|
10 * hapd->iconf->secondary_channel,
|
|
|
|
¢er_freq1_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssid.ssid_len = hapd->conf->ssid.ssid_len;
|
|
|
|
os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Neighbor Report element size = BSSID + BSSID info + op_class + chan +
|
|
|
|
* phy type + wide bandwidth channel subelement.
|
|
|
|
*/
|
|
|
|
nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
|
|
|
|
if (!nr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
|
|
|
|
wpabuf_put_le32(nr, bssid_info);
|
|
|
|
wpabuf_put_u8(nr, op_class);
|
|
|
|
wpabuf_put_u8(nr, channel);
|
|
|
|
wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wide Bandwidth Channel subelement may be needed to allow the
|
|
|
|
* receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
|
|
|
|
* Figure 9-301.
|
|
|
|
*/
|
|
|
|
wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
|
|
|
|
wpabuf_put_u8(nr, 3);
|
|
|
|
wpabuf_put_u8(nr, width);
|
|
|
|
wpabuf_put_u8(nr, center_freq1_idx);
|
|
|
|
wpabuf_put_u8(nr, center_freq2_idx);
|
|
|
|
|
|
|
|
hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
|
2021-07-28 01:42:17 +02:00
|
|
|
hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
|
2018-08-20 14:20:44 +02:00
|
|
|
|
|
|
|
wpabuf_free(nr);
|
|
|
|
#endif /* NEED_AP_MLME */
|
|
|
|
}
|