Home | History | Annotate | Line # | Download | only in ap
      1 /*
      2  * hostapd / Neighboring APs DB
      3  * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
      4  * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
      5  *
      6  * This software may be distributed under the terms of the BSD license.
      7  * See README for more details.
      8  */
      9 
     10 #include "utils/includes.h"
     11 
     12 #include "utils/common.h"
     13 #include "utils/crc32.h"
     14 #include "hostapd.h"
     15 #include "ieee802_11.h"
     16 #include "neighbor_db.h"
     17 
     18 
     19 struct hostapd_neighbor_entry *
     20 hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
     21 		     const struct wpa_ssid_value *ssid)
     22 {
     23 	struct hostapd_neighbor_entry *nr;
     24 
     25 	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
     26 			 list) {
     27 		if (ether_addr_equal(bssid, nr->bssid) &&
     28 		    (!ssid ||
     29 		     (ssid->ssid_len == nr->ssid.ssid_len &&
     30 		      os_memcmp(ssid->ssid, nr->ssid.ssid,
     31 				ssid->ssid_len) == 0)))
     32 			return nr;
     33 	}
     34 	return NULL;
     35 }
     36 
     37 
     38 int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
     39 {
     40 	struct hostapd_neighbor_entry *nr;
     41 	char *pos, *end;
     42 
     43 	pos = buf;
     44 	end = buf + buflen;
     45 
     46 	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
     47 			 list) {
     48 		int ret;
     49 		char nrie[2 * 255 + 1];
     50 		char lci[2 * 255 + 1];
     51 		char civic[2 * 255 + 1];
     52 		char ssid[SSID_MAX_LEN * 2 + 1];
     53 
     54 		ssid[0] = '\0';
     55 		wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
     56 				 nr->ssid.ssid_len);
     57 
     58 		nrie[0] = '\0';
     59 		if (nr->nr)
     60 			wpa_snprintf_hex(nrie, sizeof(nrie),
     61 					 wpabuf_head(nr->nr),
     62 					 wpabuf_len(nr->nr));
     63 
     64 		lci[0] = '\0';
     65 		if (nr->lci)
     66 			wpa_snprintf_hex(lci, sizeof(lci),
     67 					 wpabuf_head(nr->lci),
     68 					 wpabuf_len(nr->lci));
     69 
     70 		civic[0] = '\0';
     71 		if (nr->civic)
     72 			wpa_snprintf_hex(civic, sizeof(civic),
     73 					 wpabuf_head(nr->civic),
     74 					 wpabuf_len(nr->civic));
     75 
     76 		ret = os_snprintf(pos, end - pos, MACSTR
     77 				  " ssid=%s%s%s%s%s%s%s%s\n",
     78 				  MAC2STR(nr->bssid), ssid,
     79 				  nr->nr ? " nr=" : "", nrie,
     80 				  nr->lci ? " lci=" : "", lci,
     81 				  nr->civic ? " civic=" : "", civic,
     82 				  nr->stationary ? " stat" : "");
     83 		if (os_snprintf_error(end - pos, ret))
     84 			break;
     85 		pos += ret;
     86 	}
     87 
     88 	return pos - buf;
     89 }
     90 
     91 
     92 static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
     93 {
     94 	wpabuf_free(nr->nr);
     95 	nr->nr = NULL;
     96 	wpabuf_free(nr->lci);
     97 	nr->lci = NULL;
     98 	wpabuf_free(nr->civic);
     99 	nr->civic = NULL;
    100 	os_memset(nr->bssid, 0, sizeof(nr->bssid));
    101 	os_memset(&nr->ssid, 0, sizeof(nr->ssid));
    102 	os_memset(&nr->lci_date, 0, sizeof(nr->lci_date));
    103 	nr->stationary = 0;
    104 	nr->short_ssid = 0;
    105 	nr->bss_parameters = 0;
    106 }
    107 
    108 
    109 static struct hostapd_neighbor_entry *
    110 hostapd_neighbor_add(struct hostapd_data *hapd)
    111 {
    112 	struct hostapd_neighbor_entry *nr;
    113 
    114 	nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
    115 	if (!nr)
    116 		return NULL;
    117 
    118 	dl_list_add(&hapd->nr_db, &nr->list);
    119 
    120 	return nr;
    121 }
    122 
    123 
    124 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
    125 			 const struct wpa_ssid_value *ssid,
    126 			 const struct wpabuf *nr, const struct wpabuf *lci,
    127 			 const struct wpabuf *civic, int stationary,
    128 			 u8 bss_parameters)
    129 {
    130 	struct hostapd_neighbor_entry *entry;
    131 
    132 	entry = hostapd_neighbor_get(hapd, bssid, ssid);
    133 	if (!entry)
    134 		entry = hostapd_neighbor_add(hapd);
    135 	if (!entry)
    136 		return -1;
    137 
    138 	hostapd_neighbor_clear_entry(entry);
    139 
    140 	os_memcpy(entry->bssid, bssid, ETH_ALEN);
    141 	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
    142 	entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
    143 
    144 	entry->nr = wpabuf_dup(nr);
    145 	if (!entry->nr)
    146 		goto fail;
    147 
    148 	if (lci && wpabuf_len(lci)) {
    149 		entry->lci = wpabuf_dup(lci);
    150 		if (!entry->lci || os_get_time(&entry->lci_date))
    151 			goto fail;
    152 	}
    153 
    154 	if (civic && wpabuf_len(civic)) {
    155 		entry->civic = wpabuf_dup(civic);
    156 		if (!entry->civic)
    157 			goto fail;
    158 	}
    159 
    160 	entry->stationary = stationary;
    161 	entry->bss_parameters = bss_parameters;
    162 
    163 	return 0;
    164 
    165 fail:
    166 	hostapd_neighbor_remove(hapd, bssid, ssid);
    167 	return -1;
    168 }
    169 
    170 
    171 static void hostapd_neighbor_free(struct hostapd_neighbor_entry *nr)
    172 {
    173 	hostapd_neighbor_clear_entry(nr);
    174 	dl_list_del(&nr->list);
    175 	os_free(nr);
    176 }
    177 
    178 
    179 int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
    180 			    const struct wpa_ssid_value *ssid)
    181 {
    182 	struct hostapd_neighbor_entry *nr;
    183 
    184 	nr = hostapd_neighbor_get(hapd, bssid, ssid);
    185 	if (!nr)
    186 		return -1;
    187 
    188 	hostapd_neighbor_free(nr);
    189 
    190 	return 0;
    191 }
    192 
    193 
    194 void hostapd_free_neighbor_db(struct hostapd_data *hapd)
    195 {
    196 	struct hostapd_neighbor_entry *nr, *prev;
    197 
    198 	dl_list_for_each_safe(nr, prev, &hapd->nr_db,
    199 			      struct hostapd_neighbor_entry, list) {
    200 		hostapd_neighbor_free(nr);
    201 	}
    202 }
    203 
    204 
    205 #ifdef NEED_AP_MLME
    206 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
    207 						    int ht, int vht, int he)
    208 {
    209 	enum oper_chan_width oper_chwidth;
    210 
    211 	oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
    212 
    213 	if (!ht && !vht && !he)
    214 		return NR_CHAN_WIDTH_20;
    215 	if (!hapd->iconf->secondary_channel)
    216 		return NR_CHAN_WIDTH_20;
    217 	if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
    218 		return NR_CHAN_WIDTH_40;
    219 	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
    220 		return NR_CHAN_WIDTH_80;
    221 	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
    222 		return NR_CHAN_WIDTH_160;
    223 	if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
    224 		return NR_CHAN_WIDTH_80P80;
    225 	return NR_CHAN_WIDTH_20;
    226 }
    227 #endif /* NEED_AP_MLME */
    228 
    229 
    230 void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
    231 {
    232 #ifdef NEED_AP_MLME
    233 	u16 capab = hostapd_own_capab_info(hapd);
    234 	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
    235 	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
    236 	int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
    237 	bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
    238 	struct wpa_ssid_value ssid;
    239 	u8 channel, op_class;
    240 	u8 center_freq1_idx = 0, center_freq2_idx = 0;
    241 	enum nr_chan_width width;
    242 	u32 bssid_info;
    243 	struct wpabuf *nr;
    244 
    245 	if (!(hapd->conf->radio_measurements[0] &
    246 	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
    247 		return;
    248 
    249 	bssid_info = 3; /* AP is reachable */
    250 	bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
    251 	bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
    252 
    253 	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
    254 		bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
    255 
    256 	bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
    257 
    258 	if (hapd->conf->wmm_enabled) {
    259 		bssid_info |= NEI_REP_BSSID_INFO_QOS;
    260 
    261 		if (hapd->conf->wmm_uapsd &&
    262 		    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
    263 			bssid_info |= NEI_REP_BSSID_INFO_APSD;
    264 	}
    265 
    266 	if (ht) {
    267 		bssid_info |= NEI_REP_BSSID_INFO_HT |
    268 			NEI_REP_BSSID_INFO_DELAYED_BA;
    269 
    270 		/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
    271 		if (vht)
    272 			bssid_info |= NEI_REP_BSSID_INFO_VHT;
    273 	}
    274 
    275 	if (he)
    276 		bssid_info |= NEI_REP_BSSID_INFO_HE;
    277 	if (eht)
    278 		bssid_info |= NEI_REP_BSSID_INFO_EHT;
    279 	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
    280 
    281 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
    282 					  hapd->iconf->secondary_channel,
    283 					  hostapd_get_oper_chwidth(hapd->iconf),
    284 					  &op_class, &channel) ==
    285 	    NUM_HOSTAPD_MODES)
    286 		return;
    287 	width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
    288 	if (vht) {
    289 		center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
    290 			hapd->iconf);
    291 		if (width == NR_CHAN_WIDTH_80P80)
    292 			center_freq2_idx =
    293 				hostapd_get_oper_centr_freq_seg1_idx(
    294 					hapd->iconf);
    295 	} else if (ht) {
    296 		ieee80211_freq_to_chan(hapd->iface->freq +
    297 				       10 * hapd->iconf->secondary_channel,
    298 				       &center_freq1_idx);
    299 	}
    300 
    301 	ssid.ssid_len = hapd->conf->ssid.ssid_len;
    302 	os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
    303 
    304 	/*
    305 	 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
    306 	 * phy type + wide bandwidth channel subelement.
    307 	 */
    308 	nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
    309 	if (!nr)
    310 		return;
    311 
    312 	wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
    313 	wpabuf_put_le32(nr, bssid_info);
    314 	wpabuf_put_u8(nr, op_class);
    315 	wpabuf_put_u8(nr, channel);
    316 	wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
    317 
    318 	/*
    319 	 * Wide Bandwidth Channel subelement may be needed to allow the
    320 	 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
    321 	 * Figure 9-301.
    322 	 */
    323 	wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
    324 	wpabuf_put_u8(nr, 3);
    325 	wpabuf_put_u8(nr, width);
    326 	wpabuf_put_u8(nr, center_freq1_idx);
    327 	wpabuf_put_u8(nr, center_freq2_idx);
    328 
    329 	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
    330 			     hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
    331 
    332 	wpabuf_free(nr);
    333 #endif /* NEED_AP_MLME */
    334 }
    335 
    336 
    337 static struct hostapd_neighbor_entry *
    338 hostapd_neighbor_get_diff_short_ssid(struct hostapd_data *hapd, const u8 *bssid)
    339 {
    340 	struct hostapd_neighbor_entry *nr;
    341 
    342 	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
    343 			 list) {
    344 		if (ether_addr_equal(bssid, nr->bssid) &&
    345 		    nr->short_ssid != hapd->conf->ssid.short_ssid)
    346 			return nr;
    347 	}
    348 	return NULL;
    349 }
    350 
    351 
    352 int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
    353 {
    354 	struct hostapd_neighbor_entry *nr;
    355 
    356 	nr = hostapd_neighbor_get_diff_short_ssid(hapd, hapd->own_addr);
    357 	if (!nr)
    358 		return -1;
    359 
    360 	/* Clear old entry due to SSID change */
    361 	hostapd_neighbor_free(nr);
    362 
    363 	hostapd_neighbor_set_own_report(hapd);
    364 
    365 	return 0;
    366 }
    367