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 ¢er_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