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